diff --git a/3sum/EcoFriendlyAppleSu.kt b/3sum/EcoFriendlyAppleSu.kt new file mode 100644 index 000000000..3846aa507 --- /dev/null +++ b/3sum/EcoFriendlyAppleSu.kt @@ -0,0 +1,104 @@ +package leetcode_study + +/** + * 주어진 배열의 세 원소의 합이 0인 경우를 구하는 문제. (세 개의 값의 결과는 중복되지 않음) + * + * 주어진 조건 + * 3 <= nums.length <= 3000 + * -105 <= nums[i] <= 105 + */ + +/** + * case01. 조합을 사용한 풀이. + * 시간 초과 발생 이유 + * -> 모든 가능한 세 개의 조합을 생성하기 때문에 발생. + * 시간 복잡도: + * -> 세 개의 조합 생성 과정: O(n * (n-1) * (n-2)) / 3. 최악의 경우 n = 3000, 4.5 억개 조합 생성 + * -> 세 개의 조합 결과 sorting 과정: O(klogk). k = 3 + * -> 결과값을 필터링해 합이 0인 배열을 필터하는 과정: O(n) + * 나머지 연산이 세 개의 조합 생성 과정에 영향을 받아 계산 횟수 증가. + * + * 공간 복잡도: + * -> 각 조합을 모두 저장: O(n^3) + */ +fun threeSumUseCombination(nums: IntArray): List> { + // 결과를 담을 Set 자료구조 + val processResult = mutableSetOf>() + + // 주어진 배열의 크기를 담는 변수 + val maxNumber = nums.size + + // 조합 배열의 크기 + val givenSize = 3 + + // 나타낼 인덱스를 구하는 배열 초기화 + val indices = IntArray(givenSize) + for (i in 0 until givenSize) { + indices[i] = i + } + + while (indices[givenSize - 1] < maxNumber) { + processResult.add(indices.map { nums[it] }.sorted()) + var i = givenSize - 1 + + while (i >= 0 && indices[i] == i + maxNumber - givenSize) { + i-- + } + + if (i >= 0) { + indices[i]++ + for (j in i + 1 until givenSize) { + indices[j] = indices[j-1] + 1 + } + } else break + } + + return processResult.filter { it.sum() == 0 } +} + +/** + * case02. 투 포인터를 사용한 풀이 + * 조합을 사용한 풀이와 달리 시간 초과가 발생하지 않음. O(n^3)의 시간 복잡도를 O(n^2)으로 줄임 + * + * 시간 복잡도: + * -> 주어진 숫자 배열 오름차순으로 정렬: O(nlogn) + * -> 세 개의 숫자를 더하는 로직 + * -> 외부 반복문을 통해 주어진 배열 전체 조회: O(n) + * -> 내부 반복문을 통해 start to last index 순회: O(n) + * -> O(n^2) + * ∴ O(nlogn) + O(n^2) => O(n^2) + * + * 공간 복잡도: + * -> 주어진 숫자 배열의 정렬을 담는 공간 필요: O(n) + */ +fun threeSum(nums: IntArray): List> { + val processResult = mutableListOf>() + val sortedNums = nums.sorted() + + for (i in sortedNums.indices) { + if (i > 0 && sortedNums[i] == sortedNums[i-1]) continue + + var startIndex = i + 1 + var lastIndex = sortedNums.size - 1 + + while (startIndex < lastIndex) { + val sum = sortedNums[i] + sortedNums[startIndex] + sortedNums[lastIndex] + when { + sum == 0 -> { + processResult.add(listOf(sortedNums[i], sortedNums[startIndex], sortedNums[lastIndex])) + while (startIndex < lastIndex && sortedNums[startIndex] == sortedNums[startIndex + 1]) startIndex++ + while (startIndex < lastIndex && sortedNums[lastIndex] == sortedNums[lastIndex - 1]) lastIndex-- + startIndex++ + lastIndex-- + } + sum < 0 -> { + startIndex++ + } + else -> { + lastIndex-- + } + } + } + } + return processResult +} diff --git a/climbing-stairs/EcoFriendlyAppleSu.kt b/climbing-stairs/EcoFriendlyAppleSu.kt new file mode 100644 index 000000000..011656a11 --- /dev/null +++ b/climbing-stairs/EcoFriendlyAppleSu.kt @@ -0,0 +1,24 @@ +package leetcode_study + +/** + * 계단에 올라갈 수 있는 경우의 수 구하는 방법 + * 시간 복잡도: O(n) + * -> 주어진 횟수 만큼 반복 진행 + * 공간 복잡도: O(k) + * -> 주어진 계단 수 만큼 횟수를 저장할 공간 필요 + */ +fun climbStairs(n: Int): Int { + val step = IntArray(n+1) + + if (n == 1) { + return 1 + } + step[1] = 1 + step[2] = 2 + + for (i in 3 until step.size) { + step[i] = step[i-1] + step[i-2] + } + + return step[n] +} diff --git a/construct-binary-tree-from-preorder-and-inorder-traversal/EcoFriendlyAppleSu.kt b/construct-binary-tree-from-preorder-and-inorder-traversal/EcoFriendlyAppleSu.kt new file mode 100644 index 000000000..8451d2e35 --- /dev/null +++ b/construct-binary-tree-from-preorder-and-inorder-traversal/EcoFriendlyAppleSu.kt @@ -0,0 +1,45 @@ +package leetcode_study + +/** + * 전위 순회, 중위 순회를 통한 원본 Binary Tree를 구하는 문제 + * + * 전위 순회 탐색 순서 : 루트 -> 왼쪽 서브 트리 -> 오른쪽 서브 트리 + * 중위 순회 탐색 순서 : 왼쪽 서브 트리 -> 루트 -> 오른쪽 서브 트리 + * 탐색은 동일한 Binary Tree를 두고 진행되기 때문에 배열의 길이는 같습니다. + * + * 시간 복잡도: O(n^2) + * -> Ary RootNode Index 를 찾는 과정: O(n) + * -> 재귀 안에서 RootNode Index 를 찾는 과정: O(n) + * --> 두 계의 과정은 별도로 존재하는 것이 아닌 내부적으로 일어나기 때문에 O(n) * O(n). 즉, O(n^2)의 시간 복잡도가 소요됨 + * + * 공간 복잡도: O(n) or O(logn) + * -> 재귀를 통할 때마다 배열이 새로 생성. 각 노드에 대해 배열이 나뉘므로 O(n)의 공간 복잡도를 가짐 + * -> 만약 Binary Tree가 한 쪽으로 치우지지 않은 Balanced Binary Tree라면 O(logn)의 공간 복잡도를 가짐. + */ +fun buildTree(preorder: IntArray, inorder: IntArray): TreeNode? { + if (preorder.isEmpty() || inorder.isEmpty()) return null + + // 왼쪽 오른쪽 분류의 기준이 되는 rootValue를 구하는 과정 + val rootValue = preorder[0] + val rootNode = TreeNode(rootValue) + + // root를 기준으로 왼쪽, 오른쪽 subTree를 구성 + val pivotValue = inorder.indexOf(rootValue) + val leftInOrder = inorder.slice(0 until pivotValue) + val rightInOrder = inorder.slice(pivotValue + 1 until inorder.size) + + // 왼쪽, 오른쪽 subTree를 기준으로 leftPreOrder, rightPreOrder 구성 + val leftPreOrder = preorder.slice(1 until leftInOrder.size + 1) + val rightPreOrder = preorder.slice(leftInOrder.size + 1 until preorder.size) + + // 재귀를 통한 subTree 생성 + rootNode.left = buildTree(leftPreOrder.toIntArray(), leftInOrder.toIntArray()) + rootNode.right = buildTree(rightPreOrder.toIntArray(), rightInOrder.toIntArray()) + + return rootNode +} + +class TreeNode(val value: Int, ) { + var left: TreeNode? = null + var right: TreeNode? = null +} diff --git a/valid-anagram/EcoFriendlyAppleSu.kt b/valid-anagram/EcoFriendlyAppleSu.kt new file mode 100644 index 000000000..d01b1632f --- /dev/null +++ b/valid-anagram/EcoFriendlyAppleSu.kt @@ -0,0 +1,35 @@ +package leetcode_study + +/** + * 문장의 문자 순서를 바꾸어 새로운 단어나 문장을 만들 수 있는지 판별하는 문제 + * 시간 복잡도: O(n) + * -> 주어진 문자열을 순회하며 Map 자료구조에 값을 채워넣는 과정: O(n) + * -> 알파벳 문자열 세팅 과정: O(1) + * -> Map<알파벳, 빈도> 초기화 과정: O(1) + * -> 알파벳 비교 과정: O(1) + * O(1) + O(1) + O(1) + O(n) => O(n) + * + * 공간 복잡도: O(1) + * -> 알파벳 빈도수를 저장 Map: O(1) + */ +fun isAnagram(s: String, t: String): Boolean { + val alphaArray = CharArray(26) { 'a' + it} + + if (s.length != t.length) return false + + val sMap = alphaArray.associateWith { 0 }.toMutableMap() + val tMap = alphaArray.associateWith { 0 }.toMutableMap() + + for (i in s.indices) { + sMap[s[i]] = sMap.getValue(s[i]).plus(1) + tMap[t[i]] = tMap.getValue(t[i]).plus(1) + } + + for (alphabet in alphaArray) { + if (sMap[alphabet] != tMap[alphabet]) { + return false + } + } + + return true +}