Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
139 changes: 139 additions & 0 deletions combination-sum/haxr369.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
* 1번째 풀이는 2번째 풀이를 개선한 것.
* 2번째 풀이는 dfs를 이욯해서 중복순열의 경우의 수를 찾기 때문에 시간복잡도가 매우 높다.
* 또한 중복순열에서 중복을 제거하면서도 낭비되는 연산이 매우 많다.
*
*/
class Solution {

/**
* 중복순열을 구하는 부분은 2번 풀이와 동일하지만,
* 후보배열을 정렬하고, 후보 선택 시 이전 값과 같거나 큰 후보만을 선택하기에
* 동일 요소가 존재하는 배열을 중복해서 만들지 않음.
* 또한 Set 같은 저장위치를 제거해서 메모리 최적화.
*
* Runtime: 5 ms (Beats 8.49%)
* Memory: 45.77 MB (Beats 16.78%)
* Space Complexity: O(N)
* - 사용된 후보를 저장하는 배열 O(N)
* > O(N)
* Time Complexity: O(2^N/N!)
* - 후보 하나를 선택해서 target과 비교 => O(2^N)
* - target이 0이 되거나 누적 값이 더 커질 때까지 스택을 쌓기
* - 최대 누적할 수 있는 횟수는 40 / 2 = 20회 => O(20) = O(M)
* - 다만, 중복배열 생성을 방지하는 트릭을 추가함 O(1/N!)
* > O(2^N/N!)
*
* @param candidates
* @param target
* @return
*/
public List<List<Integer>> combinationSum(int[] candidates, int target) {
List<List<Integer>> ans = new ArrayList<>();
List<Integer> acc = new ArrayList<>();
Arrays.sort(candidates);

makecombination(ans, acc, target, candidates, 0);
return ans;
}

/**
* 이전에 더했던 후보와 같거나 큰 후보만 더한다.
* set: return용 조합 저장
* acc: 사용 후보 누적
* usedCount: 후보별 사용된 횟수 저장
* target: 만들 수
*/
private void makecombination(List<List<Integer>> set, List<Integer> acc, int target, int[] candidates,
int idx) {
// 완성된 경우 리턴하기
if (target == 0) {
List<Integer> tmp = new ArrayList<>(acc); // 깊은 복사
set.add(tmp);
return;
} else if (target < 0 || idx >= candidates.length) { // 타겟이 0 보다 작은 경우 스킵
return;
}

// 현재 인덱스 후보 사용
acc.add(candidates[idx]);
makecombination(set, acc, target - candidates[idx], candidates, idx);
acc.remove(acc.size() - 1); // 마지막 요소 제거

// 다음 인덱스 후보 사용
makecombination(set, acc, target, candidates, idx + 1);
return;
}

/**
* candidates의 후보들은 중복 사용이 가능하다.
* 모든 후보는 구별된다.=> 중복이 없다.
*
* Runtime: 48 ms (Beats 5.56%)
* Memory: 46.96 MB (Beats 6.1%)
* Space Complexity: O(N) + O(K)
* - 사용되는 후보를 Set으로 관리하기 => O(N)
* - 후보가 사용된 횟수를 관리하기 => O(K)
* > O(N) + O(K)
* Time Complexity: O(2^N)
* - 후보 하나를 선택해서 target과 비교
* - target이 0이 되거나 누적 값이 더 커질 때까지 스택을 쌓기 => O(2^N)
* > O(2^N)
*/
public List<List<Integer>> combinationSum2(int[] candidates, int target) {
Set<List<Integer>> set = new HashSet<>();
Set<Integer> acc = new HashSet<>();
int[] usedCount = new int[44];
dfs(set, acc, usedCount, target, candidates);

List<List<Integer>> ans = new ArrayList<>(set);
return ans;
}

/**
* target을 만드는 순열을 만든다.. => 중복을 포함하기 때문에 중복 연산이 많다..
* set: return용 조합 저장
* acc: 사용 후보 누적
* usedCount: 후보별 사용된 횟수 저장
* target: 만들 수
*/
private void dfs(Set<List<Integer>> set, Set<Integer> acc, int[] usedCount, int target, int[] candidates) {
// 완성된 경우 리턴하기
if (target == 0) {
// System.out.println("==========완성!! -> "+acc);
List<Integer> tmp = new ArrayList<>();
for (int n : candidates) {
if (acc.contains(n)) {
for (int i = 0; i < usedCount[n]; i++) {
tmp.add(n);
}
}
}
set.add(tmp);
return;
}

for (int n : candidates) {
// target 보다 작은 후보 사용하기

if (target >= n) {
// System.out.println("candi->"+n);
usedCount[n]++;
acc.add(n);
// System.out.println(". next target->"+(target-n));
dfs(set, acc, usedCount, target - n, candidates);
// 원복하기
usedCount[n]--;
if (usedCount[n] == 0) {
acc.remove(n);
}
}
}
}
}
44 changes: 44 additions & 0 deletions number-of-1-bits/haxr369.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
class Solution {
/**
* 주어진 숫자 n을 2진수로 만들고, 거기에 포함되는 1의 개수를 카운트하자.
*
* 원리: 2진수에서 1의 의미.
* 특정 수 M이 2^k <= M < 2^(k+1)의 관계를 가질 때,
* M이 2^k로 나눠떨어진다면 k번째 bit는 1로 표현할 수 있다.
* Runtime: 1 ms (Beats 18.72%)
* Memory: 42.17 MB (Beats 24.03%)
* Space Complexity: O(1)
* - 2의 제곱수 v와 k를 저장하는 공간 => O(1)
* > O(1)
* Time Complexity: O(N)
* - 30회의 n와 v의 비교 및 나눔셈 => O(31)
* > O(31) => O(1)
*/
public int hammingWeight(int n) {
int ans = 0;

int maxExponatial = 1 << 30;
// n >= 2^30일 때 수를 줄이기
if (maxExponatial <= n) {
n = n - maxExponatial;
ans++; // 2^31 사용했기에 1bit 추가
}

int k = 29;
int v = 1 << 29;

// 총 30번 돌기
while (k >= 0) {
// v <= n 일 때, k번째 bit는 1이다.
if (v <= n) {
n -= v;
ans++;
}
// 항상 k와 v를 줄이기.
k--;
v = v >> 1;
}
return ans;

}
}
107 changes: 107 additions & 0 deletions valid-palindrome/haxr369.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
/**
* 1번 풀이가 2번 풀이를 개선한 풀이
* 2번 풀이는 전체 조회를 2번 하지만, 1번에서는 s를 한번만 조회하고 바로 비교할 수 있게 개선.
*/
class Solution {

/**
* 문자열 s를 한번만 조회하면서 알파벳-숫자가 아닌 문자는 스킵하고
* 대문자는 소문자로 변환 후 왼쪽 문자와 오른쪽 문자를 비교
*
* Runtime: 1 ms (Beats 100.00%)
* Memory: 44.24 MB (Beats 53.06%)
* Space Complexity: O(1)
* - 문자열 s를 순회하면서 왼쪽 오른쪽 문자, 아스키 숫자를 저장
* > O(1)
* Time Complexity: O(N)
* - 문자열 s를 전체 조회 => O(N)
* > O(N) => O(N)
*/
public boolean isPalindrome(String s) {

// System.out.println(buffer);
int leftIdx = 0;
int rightIdx = s.length() - 1;
while (leftIdx < rightIdx) {
char leftC = s.charAt(leftIdx);
char rightC = s.charAt(rightIdx);
int leftNum = (int) leftC;
int rightNum = (int) rightC;

// 대문자면 소문자로 변환
if (65 <= leftNum && leftNum <= 90) {
leftC = (char) (leftNum + 32);
} else if (!(97 <= leftNum && leftNum <= 122) && !(48 <= leftNum && leftNum <= 57)) {
// 알파벳, 숫자가 아닌 문자인 경우 스킵!
leftIdx++;
continue;
}

if (65 <= rightNum && rightNum <= 90) {
rightC = (char) (rightNum + 32);
} else if (!(97 <= rightNum && rightNum <= 122) && !(48 <= rightNum && rightNum <= 57)) {
rightIdx--;
continue;
}

if (leftC != rightC) {
return false;
}
// 두 문자가 동일하면 다음 문자를 체크하기
leftIdx++;
rightIdx--;
}

return true;
}

/**
* 모든 대문자를 소문자로 만들고
* 알파벳이 아닌 문자를 다 제거한 문장이, 대칭이면 true이다. 아님 false.
*
* 1. 문장을 스캔하면서 대문자는 소문자로 buffer에 넣고, 영어가 아닌 문자는 넣지 않기
* 2. 판단할 때 아스키 코드를 이용하기
*
* Runtime: 121 ms (Beats 7.53%)
* Memory: 48.00 MB (Beats 5.23%)
* Space Complexity: O(1)
* - 문자열 s와 비스한 배렬(buffer)를 생성 => O(N)
* > O(N)
* Time Complexity: O(N)
* - 문자열 s를 조회하면서 buffer에 붙이기 => O(N)
* - buffer를 조회하면서 유효성 검사 => O(N)
* > O(2N) => O(N)
*/
public boolean isPalindrome2(String s) {
char a = 'a'; // 97
char z = 'z'; // 122
char A = 'A'; // 65
char Z = 'Z'; // 90
char zero = '0'; // 0
char nine = '9'; // 9

String buffer = "";
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
if ((a <= c && c <= z) || (zero <= c && c <= nine)) {
buffer += c;
} else if (A <= c && c <= Z) {
char lowerC = (char) ((int) c + 32);
buffer += lowerC;
}
}

// System.out.println(buffer);
int leftIdx = 0;
int rightIdx = buffer.length() - 1;
while (leftIdx < rightIdx) {
if (buffer.charAt(leftIdx) != buffer.charAt(rightIdx)) {
return false;
}
leftIdx++;
rightIdx--;
}

return true;
}
}