diff --git a/.github/workflows/integration.yaml b/.github/workflows/integration.yaml index cd481d9a9..f924e0c28 100644 --- a/.github/workflows/integration.yaml +++ b/.github/workflows/integration.yaml @@ -9,3 +9,82 @@ jobs: steps: - uses: actions/checkout@v4 - uses: fernandrone/linelint@0.0.6 + + label-lang: + runs-on: ubuntu-latest + continue-on-error: true + + permissions: + contents: write + pull-requests: write + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Node.js + uses: actions/setup-node@v4 + with: + node-version: '20' + + - name: Create package.json + run: echo '{}' > package.json + + - name: Install dependencies + run: npm install @octokit/rest node-fetch + + - name: Detect languages and add labels + env: + GITHUB_TOKEN: ${{ github.token }} + PR_NUM: ${{ github.event.number }} + run: | + node --input-type=module -e " + import { Octokit } from '@octokit/rest'; + import path from 'path'; + import fetch from 'node-fetch'; + + const octokit = new Octokit({ + auth: process.env.GITHUB_TOKEN, + request: { fetch } + }); + + const extensionsToLanguages = { + js: 'js', + ts: 'ts', + py: 'py', + java: 'java', + kt: 'kotlin', + cpp: 'c++', + go: 'go', + exs: 'elixir', + swift: 'swift' + // 필요한 다른 확장자와 언어 매핑 추가 + }; + + async function run() { + const { data: files } = await octokit.pulls.listFiles({ + owner: process.env.GITHUB_REPOSITORY.split('/')[0], + repo: process.env.GITHUB_REPOSITORY.split('/')[1], + pull_number: process.env.PR_NUM, + }); + + const languages = new Set(); + files.forEach(file => { + const ext = path.extname(file.filename).slice(1); + if (extensionsToLanguages[ext]) { + languages.add(extensionsToLanguages[ext]); + } + }); + + if (languages.size > 0) { + await octokit.issues.addLabels({ + owner: process.env.GITHUB_REPOSITORY.split('/')[0], + repo: process.env.GITHUB_REPOSITORY.split('/')[1], + issue_number: process.env.PR_NUM, + labels: Array.from(languages), + }); + } + } + + run(); + " diff --git a/construct-binary-tree-from-preorder-and-inorder-traversal/flynn.cpp b/construct-binary-tree-from-preorder-and-inorder-traversal/flynn.cpp new file mode 100644 index 000000000..9f7593623 --- /dev/null +++ b/construct-binary-tree-from-preorder-and-inorder-traversal/flynn.cpp @@ -0,0 +1,49 @@ +/** + * For the number of given nodes N, + * + * Time complexity: O(N) + * + * Space complexity: O(N) at worst + */ + +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + TreeNode* buildTree(vector& preorder, vector& inorder) { + unordered_map inorder_index_map; + stack tree_stack; + + for (int i = 0; i < inorder.size(); i++) inorder_index_map[inorder[i]] = i; + + TreeNode* root = new TreeNode(preorder[0]); + tree_stack.push(root); + + for (int i = 1; i < preorder.size(); i++) { + TreeNode* curr = new TreeNode(preorder[i]); + + if (inorder_index_map[curr->val] < inorder_index_map[tree_stack.top()->val]) { + tree_stack.top()->left = curr; + } else { + TreeNode* parent; + while (!tree_stack.empty() && inorder_index_map[curr->val] > inorder_index_map[tree_stack.top()->val]) { + parent = tree_stack.top(); + tree_stack.pop(); + } + parent->right = curr; + } + tree_stack.push(curr); + } + + return root; + } +}; diff --git a/construct-binary-tree-from-preorder-and-inorder-traversal/jdalma.kt b/construct-binary-tree-from-preorder-and-inorder-traversal/jdalma.kt new file mode 100644 index 000000000..d2327cb6d --- /dev/null +++ b/construct-binary-tree-from-preorder-and-inorder-traversal/jdalma.kt @@ -0,0 +1,56 @@ +package leetcode_study + +import io.kotest.matchers.equals.shouldBeEqual +import org.junit.jupiter.api.Test + +class `construct-binary-tree-from-preorder-and-inorder-traversal` { + + /** + * preorder : 현재(부모) 노드부터 왼쪽 자식 노드, 오른쪽 자식 노드 + * inorder : 왼쪽 자식 노드 부터 부모 노드, 오른쪽 자식 노드 + */ + fun buildTree(preorder: IntArray, inorder: IntArray): TreeNode? { + val inorderIndices = inorder.withIndex().associate { it.value to it.index } + return traversal(preorder, inorder, inorderIndices) + } + + /** + * preorder에서 조회한 부모 노드의 값은 inorder의 중간에 위치한다. + * 그 중간 위치 기준으로 왼쪽 노드, 오른쪽 노드로 분리하여 재귀적으로 탐색할 수 있다. + * 시간복잡도: O(n), 공간복잡도: O(n) + */ + private fun traversal( + preorder: IntArray, inorder: IntArray, inorderIndices: Map, + preStart: Int = 0, inStart: Int = 0, inEnd: Int = inorder.size - 1 + ): TreeNode? { + if (preStart > preorder.size - 1 || inStart > inEnd) { + return null + } + val value = preorder[preStart] + val rootIndexInInorder = inorderIndices[value]!! + + return TreeNode(value).apply { + this.left = traversal( + preorder, inorder, inorderIndices, + preStart + 1, inStart, rootIndexInInorder - 1 + ) + this.right = traversal( + preorder, inorder, inorderIndices, + preStart + rootIndexInInorder - inStart + 1, rootIndexInInorder + 1, inEnd + ) + } + } + + @Test + fun `전위 순회, 중위 순회 순서의 정수 배열을 기준으로 이진트리를 생성하여 반환한다`() { + val actual = buildTree(intArrayOf(3,9,20,15,7), intArrayOf(9,3,15,20,7))!! + val expect = TreeNode.of(3,9,20,null,null,15,7)!! + + actual shouldBeEqual expect + + val actual1 = buildTree(intArrayOf(3,9,8,10,20,15,7), intArrayOf(8,9,10,3,15,20,7))!! + val expect1 = TreeNode.of(3,9,20,8,10,15,7)!! + + actual1 shouldBeEqual expect1 + } +} diff --git a/contains-duplicate/0-chan.ts b/contains-duplicate/0-chan.ts new file mode 100644 index 000000000..e768f9c9e --- /dev/null +++ b/contains-duplicate/0-chan.ts @@ -0,0 +1,8 @@ +/** + * time complexity : O(n) + * space complexity : O(n) + */ +function containsDuplicate(nums: number[]): boolean { + const hasDuplicate = new Set(nums).size !== nums.length; + return hasDuplicate; +}; diff --git a/contains-duplicate/GUMUNYEONG.js b/contains-duplicate/GUMUNYEONG.js new file mode 100644 index 000000000..fca1f6f68 --- /dev/null +++ b/contains-duplicate/GUMUNYEONG.js @@ -0,0 +1,14 @@ +/** + * @param {number[]} nums + * @return {boolean} + */ +var containsDuplicate = function (nums) { + const setObj = new Set(nums); + + const diff = !(nums.length === setObj.size); + + return diff; +}; + +// TC: O(n) +// SC: O(n) diff --git a/contains-duplicate/codyman0.py b/contains-duplicate/codyman0.py new file mode 100644 index 000000000..1cc2b3e02 --- /dev/null +++ b/contains-duplicate/codyman0.py @@ -0,0 +1,16 @@ +""" +https://leetcode.com/problems/contains-duplicate/ +""" + +# Time complexity : O(n) + + +class Solution: + def containsDuplicate(self, nums: List[int]) -> bool: + sortedArray = sorted(nums) + for i in range(len(sortedArray)): + if i == len(sortedArray) - 1: + return False + if sortedArray[i] == sortedArray[i + 1] : + return True + return False diff --git a/contains-duplicate/f-exuan21.java b/contains-duplicate/f-exuan21.java new file mode 100644 index 000000000..acb71d219 --- /dev/null +++ b/contains-duplicate/f-exuan21.java @@ -0,0 +1,16 @@ +// time : O(n) +// space : O(n) + +class Solution { + public boolean containsDuplicate(int[] nums) { + Set set = new HashSet<>(); + for(int i : nums) { + if(set.contains(i)) { + return true; + } + set.add(i); + } + return false; + } +} + diff --git a/contains-duplicate/hajunyoo.py b/contains-duplicate/hajunyoo.py new file mode 100644 index 000000000..058172a4b --- /dev/null +++ b/contains-duplicate/hajunyoo.py @@ -0,0 +1,7 @@ +class Solution: + # Time complexity: O(n) + def containsDuplicate(self, nums: List[int]) -> bool: + string_len = len(nums) + set_len = len(set(nums)) + + return string_len != set_len diff --git a/contains-duplicate/whewchews.ts b/contains-duplicate/whewchews.ts new file mode 100644 index 000000000..7a939eac4 --- /dev/null +++ b/contains-duplicate/whewchews.ts @@ -0,0 +1,18 @@ +function containsDuplicate(nums: number[]): boolean { + const dict: Set = new Set(); + + // O(n) + for (let i = 0; i <= nums.length; i++) { + const n = nums[i]; + + // O(1) + if (dict.has(n)) return true; + // O(1) + dict.add(n); + } + + return false; +} + +// TC: O(N) +// SC: O(N) diff --git a/counting-bits/flynn.cpp b/counting-bits/flynn.cpp new file mode 100644 index 000000000..5067bda98 --- /dev/null +++ b/counting-bits/flynn.cpp @@ -0,0 +1,22 @@ +/** + * Time complexity: O(N) + * + * Space complexity: O(1) + */ + +class Solution { +public: + vector countBits(int n) { + vector res; + res.push_back(0); + + int i = 1, j = 1; + while (i <= n) { + res.push_back(res[i - j] + 1); + i++; + if (i == j * 2) j *= 2; + } + + return res; + } +}; diff --git a/counting-bits/jdalma.kt b/counting-bits/jdalma.kt new file mode 100644 index 000000000..13d93e7fb --- /dev/null +++ b/counting-bits/jdalma.kt @@ -0,0 +1,62 @@ +package leetcode_study + +import io.kotest.matchers.shouldBe +import org.junit.jupiter.api.Test + +class `counting-bits` { + + fun countBits(n: Int): IntArray { + return usingDPAndLeastSignificantBit(n) + } + + // 1. 입력받은 정수만큼 순회하며 bit 카운트 + // 시간복잡도: O(n * log(n)), 공간복잡도: O(1) + private fun usingBinary(n: Int): IntArray { + fun binary(n: Int): Int { + var calc = n + var count = 0 + while(calc > 0) { + if (calc % 2 != 0) { + count++ + } + calc /= 2 + } + return count + } + + return (0 .. n).map { binary(it) }.toIntArray() + } + + // 2. MSB, 즉 최상위 비트를 활용하여 십진수가 두 배가 될때마다 MSB를 갱신하여 이전의 결과를 활용 + // 시간복잡도: O(n), 공간복잡도: O(n) + private fun usingDPAndMostSignificantBit(n: Int): IntArray { + val dp = IntArray(n + 1) + var msb = 1 + + for (index in 1 .. n) { + if (index == msb shl 1) { + msb = index + } + dp[index] = 1 + dp[index - msb] + } + + return dp + } + + // 3. 최하위 비트를 제거한 결과를 재활용한다. (최하위 비트를 제거한 결과) + (현재 십진수의 최하위비트) + // 시간복잡도: O(n), 공간복잡도: O(n) + private fun usingDPAndLeastSignificantBit(n: Int): IntArray { + val dp = IntArray(n + 1) + for (index in 1 .. n) { + dp[index] = dp[index shr 1] + (index and 1) + } + + return dp + } + + @Test + fun `정수가 주어지면 각 i(0 ~ i)에 대해 이진 표현에서 1의 개수를 저장하는 배열을 반환한다`() { + countBits(2) shouldBe intArrayOf(0,1,1) + countBits(5) shouldBe intArrayOf(0,1,1,2,1,2) + } +} diff --git a/decode-ways/flynn.cpp b/decode-ways/flynn.cpp new file mode 100644 index 000000000..05d9b6eec --- /dev/null +++ b/decode-ways/flynn.cpp @@ -0,0 +1,53 @@ +/** + * For the length of the given string N, + * + * Time complexity: O(N) + * + * Space complexity: O(N) + */ + +class Solution { +public: + int numDecodings(string s) { + if (s[0] == '0') return 0; + + int memo[s.size() + 1]; + + fill(memo, memo + s.size() + 1, 0); + memo[0] = 1; + memo[1] = 1; + + for (int i = 2; i <= s.size(); i++) { + int s_i = i - 1; + if (s[s_i] != '0') memo[i] = memo[i - 1]; + int two_digits = stoi(s.substr(s_i - 1, 2)); + if (10 <= two_digits && two_digits <= 26) memo[i] += memo[i - 2]; + } + + return memo[s.size()]; + } +}; + +/** + * Space complexity O(1) solution + */ + +// class Solution { +// public: +// int numDecodings(string s) { +// if (s[0] == '0') return 0; + +// int one_digit_memo = 1, two_digit_memo = 1; + +// for (int i = 1; i < s.size(); i++) { +// int tmp = 0; +// if (s[i] != '0') tmp = one_digit_memo; +// int two_digits = stoi(s.substr(i - 1, 2)); +// if (10 <= two_digits && two_digits <= 26) tmp += two_digit_memo; +// two_digit_memo = one_digit_memo; +// one_digit_memo = tmp; +// } + +// return one_digit_memo; +// } +// }; diff --git a/decode-ways/jdalma.kt b/decode-ways/jdalma.kt new file mode 100644 index 000000000..75808315b --- /dev/null +++ b/decode-ways/jdalma.kt @@ -0,0 +1,95 @@ +package leetcode_study + +import io.kotest.matchers.shouldBe +import org.junit.jupiter.api.Test + +class `decode-ways` { + + fun numDecodings(s: String): Int { + return usingOptimizedDP(s) + } + + /** + * 1. 문자열의 첫 인덱스부터 DFS로 확인하면서 결과를 증가시킨다. → 시간초과 + * 0부터 시작하는 문자열은 존재하지 않기에 바로 0으로 반환하고 그 뒤 숫자부터 DFS를 연이어 실행한다. + * 시간복잡도: O(2^n), 공간복잡도: O(n) + */ + private fun usingDfs(s: String): Int { + fun dfs(index: Int): Int = + if (index == s.length) 1 + else if (s[index] == '0') 0 + else if (index + 1 < s.length && (s[index] == '1' || (s[index] == '2' && s[index + 1] < '7')) ) + dfs(index + 1) + dfs(index + 2) + else dfs(index + 1) + + return dfs(0) + } + + /** + * 2. 1번 풀이에서 중복되는 연산에 top-down 방향으로 메모이제이션 적용 + * 시간복잡도: O(n), 공간복잡도: O(n) + */ + private fun usingMemoization(s: String): Int { + fun dfs(index: Int, mem: IntArray): Int { + println(index) + mem[index] = if (index == s.length) 1 + else if (s[index] == '0') 0 + else if (mem[index] != 0) mem[index] + else if (index + 1 < s.length && (s[index] == '1' || (s[index] == '2' && s[index + 1] < '7')) ) + dfs(index + 1, mem) + dfs(index + 2, mem) + else dfs(index + 1, mem) + + return mem[index] + } + return dfs(0, IntArray(s.length + 1) { 0 }) + } + + /** + * 3. 마지막 숫자부터 bottom-up 방향 DP + * 시간복잡도: O(n), 공간복잡도: O(n) + */ + private fun usingDP(s: String): Int { + val dp = IntArray(s.length + 1).apply { + this[s.length] = 1 + } + + (s.length - 1 downTo 0).forEach { index -> + if (s[index] == '0') dp[index] = 0 + else if(index + 1 < s.length && (s[index] == '1' || (s[index] == '2' && s[index + 1] < '7'))) + dp[index] = dp[index + 1] + dp[index + 2] + else dp[index] = dp[index + 1] + } + + return dp[0] + } + + /** + * 4. 배열을 사용하지 않고 DP 적용 + * 시간복잡도: O(n), 공간복잡도: O(1) + */ + private fun usingOptimizedDP(s: String): Int { + var (memo, result) = 0 to 1 + + (s.length - 1 downTo 0).forEach { index -> + var tmp = if (s[index] == '0') 0 else result + + if (index + 1 < s.length && (s[index] == '1' || (s[index] == '2' && s[index + 1] < '7'))) { + tmp += memo + } + memo = result + result = tmp + } + + return result + } + + @Test + fun `입력받은 문자열의 디코딩 가능한 경우의 수를 반환한다`() { + numDecodings("12") shouldBe 2 + numDecodings("226") shouldBe 3 + numDecodings("06") shouldBe 0 + numDecodings("1011") shouldBe 2 + numDecodings("10112266") shouldBe 8 + numDecodings("1025") shouldBe 2 + } +} diff --git a/encode-and-decode-strings/flynn.cpp b/encode-and-decode-strings/flynn.cpp new file mode 100644 index 000000000..955572279 --- /dev/null +++ b/encode-and-decode-strings/flynn.cpp @@ -0,0 +1,57 @@ +/** + * For the number of given strings N, and the length of the longest string M, + * + * Encode + * - Time complexity: O(N) + * - Space complexity: O(1) + * + * Decode + * - Time complexity: O(NM) + * - Space complexity: O(M) + */ + +class Codec { +public: + + // Encodes a list of strings to a single string. + string encode(vector& strs) { + string res = ""; + for (auto str : strs) { + res += to_string(str.size()); + res.push_back('.'); + res += str; + } + return res; + } + + // Decodes a single string to a list of strings. + vector decode(string s) { + vector res; + + auto it = s.begin(); + while (it != s.end()) { + int str_size = 0; + string tmp = ""; + + while (*it != '.') { + str_size = str_size * 10 + (*it - '0'); + it++; + } + + it++; + + for (int i = 0; i < str_size; i++) { + tmp.push_back(*it); + it++; + } + + res.push_back(tmp); + } + + return res; + } +}; + +// Your Codec object will be instantiated and called as such: +// Codec codec; +// codec.decode(codec.encode(strs)); diff --git a/encode-and-decode-strings/jdalma.kt b/encode-and-decode-strings/jdalma.kt new file mode 100644 index 000000000..724700236 --- /dev/null +++ b/encode-and-decode-strings/jdalma.kt @@ -0,0 +1,39 @@ +package leetcode_study + +import io.kotest.matchers.equals.shouldBeEqual +import org.junit.jupiter.api.Test + +/** + * 인코딩과 디코딩을 해결할 때 구분자를 256개의 ASCII 문자 중 하나를 사용해야한다면 아래와 같은 방법을 사용할 수 있다. + * 시간복잡도: O(n), 공간복잡도: O(1) + */ +class `encode-and-decode-strings` { + + private val DELIMITER = ":" + + fun encode(strings: List): String { + return strings.joinToString(separator = "") { e -> "${e.length}$DELIMITER$e" } + } + + fun decode(string: String): List { + var index = 0 + val result = mutableListOf() + while (index < string.length) { + val delimiterIndex = string.indexOf(DELIMITER, startIndex = index) + val size = string.substring(index, delimiterIndex).toInt() + result.add(string.substring(delimiterIndex + 1, delimiterIndex + size + 1)) + index = delimiterIndex + size + 1 + } + return result + } + + @Test + fun `문자열 목록을 하나의 문자열로 인코딩한다`() { + encode(listOf("leet","co:de","l:o:v:e","you")) shouldBeEqual "4:leet5:co:de7:l:o:v:e3:you" + } + + @Test + fun `문자열을 문자열 목록으로 디코딩한다`() { + decode("4:leet5:co:de7:l:o:v:e3:you") shouldBeEqual listOf("leet","co:de","l:o:v:e","you") + } +} diff --git a/kth-smallest-element-in-a-bst/f-exuan21.java b/kth-smallest-element-in-a-bst/f-exuan21.java new file mode 100644 index 000000000..95433a739 --- /dev/null +++ b/kth-smallest-element-in-a-bst/f-exuan21.java @@ -0,0 +1,50 @@ +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + + private int count = 0; + private TreeNode resultNode = null; + + public int kthSmallest(TreeNode root, int k) { + searchNode(root, k); + return resultNode.val; + } + + public void searchNode(TreeNode node, int k) { + + if(resultNode != null) return; + + if(node.left != null) { + searchNode(node.left, k); + } + + count++; + + if(count == k) { + resultNode = node; + return; + } + + if(node.right != null) { + searchNode(node.right, k); + } + } +} + +// n : 노드 개수 +// time : O(n) 최악의 경우 모든 노드를 탐색해야함 +// space : O(n) 최악의 경우 한 쪽으로 노드가 치우쳐져 있음 +// -> 재귀 호출이 이루어지므로 스택에 쌓임 -> 한 쪽으로 쏠려 있으면 트리의 높이가 n이 됨 (트리의 최대 높이가 스택의 최대 깊이) diff --git a/kth-smallest-element-in-a-bst/hajunyoo.py b/kth-smallest-element-in-a-bst/hajunyoo.py new file mode 100644 index 000000000..3deb0e4a6 --- /dev/null +++ b/kth-smallest-element-in-a-bst/hajunyoo.py @@ -0,0 +1,30 @@ +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +from collections import defaultdict + + +class Solution: + # Time complexity: O(n) + def kthSmallest(self, root: Optional[TreeNode], k: int) -> int: + self.count = 0 + self.result = 0 + + def dfs(node): + if not node: + return + + dfs(node.left) + + self.count += 1 + if self.count == k: + self.result = node.val + return + + dfs(node.right) + + dfs(root) + return self.result diff --git a/kth-smallest-element-in-a-bst/whewchews.ts b/kth-smallest-element-in-a-bst/whewchews.ts new file mode 100644 index 000000000..65b632ec8 --- /dev/null +++ b/kth-smallest-element-in-a-bst/whewchews.ts @@ -0,0 +1,36 @@ +/** + * Definition for a binary tree node. + * class TreeNode { + * val: number + * left: TreeNode | null + * right: TreeNode | null + * constructor(val?: number, left?: TreeNode | null, right?: TreeNode | null) { + * this.val = (val===undefined ? 0 : val) + * this.left = (left===undefined ? null : left) + * this.right = (right===undefined ? null : right) + * } + * } + */ + +function kthSmallest(root: TreeNode | null, k: number): number { + // SC: O(N) + const values: number[] = []; + + // TC: O(N) + const dfs = (node: TreeNode | null) => { + if (node == null) return; + dfs(node.left); + values.push(node.val); + dfs(node.right); + }; + + // SC: O(h) + // h: the height of the tree + dfs(root); + + // TC: O(1) + return values[k - 1]; +} + +// TC: O(N) +// SC: O(N) diff --git a/number-of-1-bits/0-chan.ts b/number-of-1-bits/0-chan.ts new file mode 100644 index 000000000..17b8fdba2 --- /dev/null +++ b/number-of-1-bits/0-chan.ts @@ -0,0 +1,10 @@ +/** + * time complexity : O(logn) + * space complexity : O(logn) + */ +function hammingWeight(n: number): number { + const MAX_NUM = 2147483648 - 1; + + const bitwiseOperated = (n & MAX_NUM).toString(2); + return bitwiseOperated.replaceAll('0', '').length; +}; diff --git a/number-of-1-bits/GUMUNYEONG.js b/number-of-1-bits/GUMUNYEONG.js new file mode 100644 index 000000000..02805653b --- /dev/null +++ b/number-of-1-bits/GUMUNYEONG.js @@ -0,0 +1,17 @@ +/** + * @param {number} n + * @return {number} + */ +var hammingWeight = function (n) { + let i = "1"; + + while (n > 2) { + n % 2 ? i += "1" : ""; + n = Math.floor(n / 2); + } + + return i.length; +}; + +// TC: O(log n) +// SC: O(1) diff --git a/number-of-1-bits/f-exuan21.java b/number-of-1-bits/f-exuan21.java new file mode 100644 index 000000000..9c06c4329 --- /dev/null +++ b/number-of-1-bits/f-exuan21.java @@ -0,0 +1,16 @@ +// time : O(1) +// space : O(1) + +class Solution { + public int hammingWeight(int n) { + int count = 0; + + while(n != 0) { + if((n&1) == 1) count++; + n = n >> 1; + } + + return count; + } +} + diff --git a/number-of-1-bits/hajunyoo.py b/number-of-1-bits/hajunyoo.py new file mode 100644 index 000000000..b8e0e07fc --- /dev/null +++ b/number-of-1-bits/hajunyoo.py @@ -0,0 +1,9 @@ +class Solution: + # Time complexity: O(log n) + def hammingWeight(self, n: int) -> int: + cnt = 0 + while n > 0: + if n % 2 == 1: + cnt += 1 + n = n // 2 + return cnt diff --git a/number-of-1-bits/whewchews.ts b/number-of-1-bits/whewchews.ts new file mode 100644 index 000000000..2e7ccf03c --- /dev/null +++ b/number-of-1-bits/whewchews.ts @@ -0,0 +1,15 @@ +function hammingWeight(n: number): number { + // SC: log(1) + let val = 0; + while (n > 0) { + val += n % 2; + + // TC: log₂(n) + n = Math.floor(n / 2); + } + + return val; +} + +// TC: O(log N) +// SC: O(1) diff --git a/palindromic-substrings/hajunyoo.py b/palindromic-substrings/hajunyoo.py new file mode 100644 index 000000000..08982c61b --- /dev/null +++ b/palindromic-substrings/hajunyoo.py @@ -0,0 +1,19 @@ +class Solution: + # Time complexity: O(n^2) = O(n) * O(n) + def countSubstrings(self, s: str) -> int: + self.count = 0 + n = len(s) + + def two_pointer_expand(left, right): + while left >= 0 and right < n and s[left] == s[right]: + self.count += 1 + left -= 1 + right += 1 + + for i in range(0, n): + # 1, 3, 5 ... + two_pointer_expand(i, i) + # 2, 4, 6 ... + two_pointer_expand(i, i + 1) + + return self.count diff --git a/palindromic-substrings/whewchews.ts b/palindromic-substrings/whewchews.ts new file mode 100644 index 000000000..fa9e302f1 --- /dev/null +++ b/palindromic-substrings/whewchews.ts @@ -0,0 +1,35 @@ +function countSubstrings(s: string): number { + // SC: O(N^2) + const dict: Map = new Map(); + const n = s.length; + + // TC: O(N^2) + for (let start = n; start >= 0; start--) { + for (let end = start; end < n; end++) { + if (start === end) { + dict.set(`${start}:${end}`, true); + } else if (start + 1 === end) { + dict.set(`${start}:${end}`, s[start] === s[end]); + } else { + const flag = s[start] === s[end]; + const mid = dict.get(`${start + 1}:${end - 1}`); + dict.set(`${start}:${end}`, flag && mid); + } + } + } + + let cnt = 0; + + // TC: O(N^2) + // SC: O(1) + for (const v of dict.values()) { + if (v) { + cnt++; + } + } + + return cnt; +} + +// TC: O(N^2) +// SC: O(N^2) diff --git a/top-k-frequent-elements/GUMUNYEONG.js b/top-k-frequent-elements/GUMUNYEONG.js new file mode 100644 index 000000000..0ac4a6f09 --- /dev/null +++ b/top-k-frequent-elements/GUMUNYEONG.js @@ -0,0 +1,22 @@ +/** + * @param {number[]} nums + * @param {number} k + * @return {number[]} + */ +var topKFrequent = function (nums, k) { + let result = []; + let fObj = {}; + + for (let i = 0; i < nums.length; i++) { + const n = nums[i]; + fObj[n] ? fObj[n]++ : fObj[n] = 1; + } + + Object + .entries(fObj) + .sort((a, b) => b[1] - a[1]) + .slice(0, k) + .filter(v => result.push(v[0])); + + return result; +}; diff --git a/top-k-frequent-elements/f-exuan21.java b/top-k-frequent-elements/f-exuan21.java new file mode 100644 index 000000000..412b6fe93 --- /dev/null +++ b/top-k-frequent-elements/f-exuan21.java @@ -0,0 +1,39 @@ + +class Solution { + public int[] topKFrequent(int[] nums, int k) { + Map map = new HashMap<>(); + for(int num : nums) { + map.put(num, map.getOrDefault(num, 0) + 1); + } + + PriorityQueue> queue = new PriorityQueue<>( + (a, b) -> Integer.compare(b.getValue(), a.getValue()) + ); + + for(Map.Entry entry : map.entrySet()) { + queue.offer(entry); + } + + int[] res = new int[k]; + + for(int i = 0; i < k; i++) { + res[i] = queue.poll().getKey(); + } + + return res; + } +} + +// n : nums의 길이 +// m : nums에서 서로 다른 숫자의 개수 + +// time : O(n) + O(m*logm) + O(k*logm) = O(n + m*logm + k*logm) +// 최악의 경우, nums 가 다 unique 하기 때문에 n == m == k 가 됨 +// 따라서, O(n*logn) + +// space : O(m) + O(m) + O(k) = O(m + k) +// 최악의 경우 n == m == k 가 됨 +// 따라서, O(n) + + + diff --git a/top-k-frequent-elements/hajunyoo.py b/top-k-frequent-elements/hajunyoo.py new file mode 100644 index 000000000..a26351c77 --- /dev/null +++ b/top-k-frequent-elements/hajunyoo.py @@ -0,0 +1,20 @@ +from collections import defaultdict +from typing import List + + +class Solution: + # Time complexity: O(nlogn) -> O(n) + O(nlogn) + O(k) + def topKFrequent(self, nums: List[int], k: int) -> List[int]: + counter_dict = defaultdict(int) + + for n in nums: + counter_dict[n] += 1 + + count_list = [] + for key, val in counter_dict.items(): + count_list.append((key, val)) + + count_list.sort(key=lambda x: x[1], reverse=True) + answer = [a for a, b in count_list[:k]] + + return answer diff --git a/top-k-frequent-elements/whewchews.ts b/top-k-frequent-elements/whewchews.ts new file mode 100644 index 000000000..633a6498a --- /dev/null +++ b/top-k-frequent-elements/whewchews.ts @@ -0,0 +1,28 @@ +function topKFrequent(nums: number[], k: number): number[] { + const dict: Map = new Map(); + + // TC: O(N) + // SC: O(N) + nums.forEach((n) => { + if (!dict.has(n)) dict.set(n, 1); + else { + dict.set(n, dict.get(n) + 1); + } + }); + + // TC: O(N) + // SC: O(N) + const buckets: number[][] = Array(nums.length + 1) + .fill(0) + .map((_) => []); + Array.from(dict.entries()).forEach(([num, cnt]) => { + buckets[cnt].push(num); + }); + + // TC: O(N) + O(k) = O(N) + // SC: O(N) + return buckets.flat().slice(-k); +} + +// TC: O(N) +// SC: O(N) diff --git a/valid-anagram/flynn.cpp b/valid-anagram/flynn.cpp new file mode 100644 index 000000000..b6df7007e --- /dev/null +++ b/valid-anagram/flynn.cpp @@ -0,0 +1,22 @@ +/** + * For length of given strings N, + * + * Time complexity: O(N) + * - iteration for given strings + * + * Space complexity: O(1) + * - the size of the container `count` is constant + */ + +class Solution { +public: + bool isAnagram(string s, string t) { + int count[26] = {0}; + + for (auto c : s) count[c - 'a']++; + for (auto c : t) count[c - 'a']--; + + for (int i = 0; i < 26; i++) if (count[i]) return false; + return true; + } +}; diff --git a/valid-anagram/jdalma.kt b/valid-anagram/jdalma.kt new file mode 100644 index 000000000..4c460228c --- /dev/null +++ b/valid-anagram/jdalma.kt @@ -0,0 +1,55 @@ +package leetcode_study + +import org.junit.jupiter.api.Test + +class `valid-anagram` { + + fun isAnagram(s: String, t: String): Boolean { + return usingArray(s, t) + } + + // 1. 두 문자열을 정렬하여 비교한다. + // 시간복잡도: O(n * log(n)), 공간복잡도: O(n) + private fun usingSort(s: String, t: String): Boolean { + val sorted1 = s.toCharArray().apply { this.sort() } + val sorted2 = t.toCharArray().apply { this.sort() } + + return sorted1.contentEquals(sorted2) + } + + // 2. 배열에 문자 수 가감 + // 시간복잡도: O(n), 공간복잡도: O(n) + private fun usingArray(s: String, t: String): Boolean { + if (s.length != t.length) { + return false + } + + /* 해시맵 사용 + val map: Map = mutableMapOf().apply { + (s.indices).forEach { index -> + this[s[index]] = this.getOrDefault(s[index], 0) + 1 + this[t[index]] = this.getOrDefault(t[index], 0) - 1 + } + } + return map.values.find { it > 0 } == null + */ + + return IntArray(26).apply { + for (index in s.indices) { + this[s[index] - 'a'] = this[s[index] - 'a'] + 1 + this[t[index] - 'a'] = this[t[index] - 'a'] - 1 + } + }.find { it > 0 } == null + } + + @Test + fun `입력받은 두 문자열이 애너그램이라면 참을 반환한다`() { + isAnagram("anagram", "nagaram") + isAnagram("test", "estt") + } + + @Test + fun `입력받은 두 문자열이 애너그램이 아니라면 거짓을 반환한다`() { + isAnagram("cat", "rat") + } +}