From 8d8e4ac5b8610b525c6ea705e059a61bd4bb9ce5 Mon Sep 17 00:00:00 2001 From: Changhoon Sung Date: Sat, 26 Jul 2025 19:28:14 -0700 Subject: [PATCH] Revert "[changhoon-sung] WEEK 01 solutions" --- contains-duplicate/changhoon-sung.cpp | 33 -------- .../changhoon-sung.cpp | 83 ------------------- top-k-frequent-elements/changhoon-sung.cpp | 54 ------------ two-sum/changhoon-sung.cpp | 33 -------- 4 files changed, 203 deletions(-) delete mode 100644 contains-duplicate/changhoon-sung.cpp delete mode 100644 longest-consecutive-sequence/changhoon-sung.cpp delete mode 100644 top-k-frequent-elements/changhoon-sung.cpp delete mode 100644 two-sum/changhoon-sung.cpp diff --git a/contains-duplicate/changhoon-sung.cpp b/contains-duplicate/changhoon-sung.cpp deleted file mode 100644 index 77a851fc8..000000000 --- a/contains-duplicate/changhoon-sung.cpp +++ /dev/null @@ -1,33 +0,0 @@ -/* -contains-duplicate -요구사항: 주어진 입력에서 중복된 원소가 존재하는지 여부를 참/거짓으로 반환. -접근 1: 가장 단순한 방법은 다른 vector input_list를 두고, 입력 반복마다 전체 스캔해서 중복 여부를 체크하는 것입니다. - input_list의 스캔 비용 O(N)을 모든 입력에 대해 반복하므로 총 시간 복잡도는 O(N^2)입니다. - 공간 복잡도는 입력과 같으므로 O(N)입니다. -접근 2: 원소의 존재 여부를 더 빠르게 체크하는 방법은 set을 사용하는 것입니다. - set은 중복 원소를 검출하는데 효과적인 자료구조로, 내부 구현에 따라 비용과 특성이 다릅니다. - - C++에서 ordered_{set, map}은 정렬을 유지하는 search tree를 사용하므로 쿼리와 삽입, 삭제 비용이 평균 O(log(N))입니다. - search tree의 구현에 따라 최악의 경우 쿼리 비용이 O(N)까지 증가할 수 있습니다. - - C++에서 unordered_{set, map}은 해시 및 버킷 구조를 사용합니다. 이 경우, 평균적인 쿼리, 삽입, 삭제 비용은 O(1)입니다. - 우리의 요구사항은 입력에 대한 별도의 정보를 저장할 필요가 없으므로 unordered_set으로 중복 검사를 수행하는 것이 비용 효율적입니다. - 최악의 경우 모든 입력을 확인해야 합니다. N개의 입력에 대해 각각 쿼리와 입력을 수행하므로, 총 시간 복잡도는 O(N), 공간 복잡도도 입력만큼인 O(N)입니다. -*/ - -#include -#include - -class Solution { - public: - bool containsDuplicate(std::vector& nums) { - std::unordered_set s; - - for (auto it = nums.begin(); it != nums.end(); it++) { - int x = *it; - if (s.find(x) != s.end()) - return true; - else - s.insert(x); - } - return false; - } -}; diff --git a/longest-consecutive-sequence/changhoon-sung.cpp b/longest-consecutive-sequence/changhoon-sung.cpp deleted file mode 100644 index 4bcbfb837..000000000 --- a/longest-consecutive-sequence/changhoon-sung.cpp +++ /dev/null @@ -1,83 +0,0 @@ -/* -longest-consecutive-sequence -요구사항: 주어진 비정렬 배열에서 가장 긴 연속 수열의 길이를 O(N)에 구하라. -접근 1: 가장 단순한 방법은 정렬 후 스캔하여 가장 긴 연속 구간을 찾는 것이다. - 정렬 비용 O(NlogN)에 스캔 O(N)으로 총 O(NlogN)이다. 비용 초과. -접근 2: 정렬하지 않고, 인접한 연속 원소 수열을 '하나의 덩어리'로 취급하기 위해 union-find할 수 있다. - 중복 원소는 무시한다. - 입력 x에 대해 x-1의 유니온을 찾는다. - 그러한 유니온이 존재하면 x를 추가한다. 그 이후 x+1 유니온이 존재하면 통합한다. - 그러한 유니온이 존재하지 않으면 x+1 유니온을 찾는다. - 그러한 유니온이 존재하면 x를 추가한다. - 인접한 앞 뒤 유니온이 존재하지 않으면 입력을 새로운 root union으로 초기화한다. - - 비용: - 경로 압축과 높이 최소화 최적화를 적용하면, find 비용은 O(α(N))으로 최대 입력 N=10^5에 대해서 한 자리 상수만큼 충분히 낮다. - 유니온의 통합 비용은 O(1)이다. - N개의 입력에 대해 O(1) 연산을 상수번 수행하므로 유니온 빌딩 시간 복잡도는 O(N)이다. - 사이즈 리스트를 스캔하여 가장 큰 유니온의 크기를 구한다. 이는 O(K) where K <= N이다. - 따라서 총 시간 복잡도는 O(N)이다. - 공간 복잡도도 입력 범위의 상수배이므로 O(N)이다. -*/ - -#include -#include - -#define max(a, b) ((a) > (b) ? (a) : (b)) - -class Solution { - public: - std::unordered_map parent; - std::unordered_map size; - - int find(int x) { - if (x == parent[x]) return x; - return parent[x] = find(parent[x]); // path compression - } - void union_sets(int x, int y) { - int px = find(x); - int py = find(y); - - if (px == py) return; - - // min height optimization - if (size[px] > size[py]) { - size[px] += size[py]; - parent[py] = px; - } else { - size[py] += size[px]; - parent[px] = py; - } - } - int longestConsecutive(std::vector& nums) { - // build union - for (auto it = nums.begin(); it != nums.end(); it++) { - int x = *it; - - if (parent.find(x) != parent.end()) - continue; - - if (parent.find(x - 1) != parent.end()) { - parent[x] = find(x - 1); - size[parent[x]]++; - if (parent.find(x + 1) != parent.end()) { - union_sets(x, x + 1); - } - } else if (parent.find(x + 1) != parent.end()) { - parent[x] = find(x + 1); - size[parent[x]]++; - } else { - parent[x] = x; - size[x] = 1; - } - } - - // find largest set - int max_size = 0; - for(auto it = size.begin(); it != size.end(); it++) { - auto sz = it->second; - max_size = max(max_size, sz); - } - return max_size; - } -}; diff --git a/top-k-frequent-elements/changhoon-sung.cpp b/top-k-frequent-elements/changhoon-sung.cpp deleted file mode 100644 index b0a066116..000000000 --- a/top-k-frequent-elements/changhoon-sung.cpp +++ /dev/null @@ -1,54 +0,0 @@ -/* -top-k-frequent-elements -요구사항: 주어진 입력에 대해 k번째 빈도를 가지는 원소를 모두 반환하라. -접근 1: 가장 단순한 접근법은 모든 원소에 대해 map으로 빈도를 세는 것입니다. - ordered_map에서 삽입 비용은 자료구조 길이가 K일 때 O(logK)으로, N회 삽입 연산으로 O(NlogK) 인데, - 이때 모든 원소가 유일한 경우 K=N이므로 최악의 경우 O(NlogN) 입니다. - 그리고 정렬된 맵에서 k 위치를 찾는 lower_bound 연산 비용은 O(logN)으로, 총 O(NlogN)입니다. - unordered_map은 삽입 비용이 O(1)이지만, N회 삽입 연산 이후, k를 찾기 위해 카운트를 정렬해야 한다는 점에서 - 보편적인 정렬 비용 O(Nlog(N))이 발생합니다. -접근 2: 총 비용이 O(NlogN)보다 낮아지려면 정렬 자료구조의 입력 비용이 O(logN)보다 낮거나, - 정렬 비용이 O(NlogN)보다 낮아야 합니다. - 한 가지 아이디어는 정렬 자료구조의 입력을 비용을 낮추는 필터 및 셋 기반 블랙리스트를 도입할 수 있다는 것입니다. - - k초과 원소 필터: k보다 높은 빈도를 가지는 원소를 자료구조에서 제거 및 블랙리스트 - - k도달 불가 원소 필터: 현재 조회중인 입력에 대해 남은 원소의 수가 모두 해당 원소더라도 k에 미치지 못 하는 경우 제거 및 블랙리스트 - 필터에 해당하는 원소가 M개일 때, 필터 비용은 해시셋 조회 O(1), 삽입 비용은 O(log(N-M))으로 - 모든 입력에 대한 총 비용은 O(Nlog(N-M))입니다. - 공간 복잡도는 최악의 경우에도 입력의 상수배이므로 O(N)입니다. -접근 3: 우리가 원하는 것이 k 빈도 딱 하나라는 것을 고려하면, 해당 빈도를 키로 원소를 반환받는 구조를 생각해볼 수 있습니다. - 아이디어는 원소-카운팅맵으로 전체 입력에 대해 비용 O(N)으로 카운팅하고, 이를 바탕으로 카운팅-원소 해시맵으로 <빈도, 원소셋>을 빌드하는겁니다. - 해당 빌드 비용은 최대 N개 원소에 대해 조회+삽입 O(1) 이므로 O(N)입니다. - 필요한 정보는 카운팅-원소 해시맵으로 O(1) 조회 및 O(N) 반환합니다. - 공간 복잡도는 최악의 경우에도 입력의 상수배이므로 O(N)입니다. -*/ - -#include -#include -#include - -class Solution { -public: - std::vector topKFrequent(std::vector& nums, int k) { - std::unordered_map elementCountMap; - std::unordered_map> countElementMap; - std::vector ans; - - for(auto it = nums.begin(); it != nums.end(); it++) { - int x = *it; - elementCountMap[x]++; - } - - for(auto it = elementCountMap.begin(); it != elementCountMap.end(); it++) { - int x = it->first; - int count = it->second; - countElementMap[count].insert(x); - } - - const std::unordered_set& kFreqSet = countElementMap[k]; // avoid copy - for(auto it = kFreqSet.begin(); it != kFreqSet.end(); it++) { - int x = *it; - ans.push_back(x); - } - return ans; - } -}; diff --git a/two-sum/changhoon-sung.cpp b/two-sum/changhoon-sung.cpp deleted file mode 100644 index e0ab60b6e..000000000 --- a/two-sum/changhoon-sung.cpp +++ /dev/null @@ -1,33 +0,0 @@ -/* -two-sum -요구사항: 주어진 배열에서 두 수의 합이 타겟을 만들 수 있는지 확인하고, 해당 원소를 반환하라. -접근 1: 가장 단순한 방법은 입력에 대해 모든 combination을 확인하는 것입니다. - 시간 복잡도는 입력 스캔 O(N)을 N개의 원소에 대해 수행하므로 O(N^2)입니다. - 공간 복잡도는 입력 레퍼런스를 그대로 사용하며, 추가로 저장 및 관리하는 자료구조가 없으므로 O(1)입니다. -접근 2: 한 가지 아이디어는 임의의 입력 X에 대해 합이 `target`이 되도록 하는 pair는 유일하게 결정된다는 것입니다. - 따라서, 합을 구하는 대신, 주어진 입력에 대한 페어 존재 유무를 검사하는 것으로 문제를 전환할 수 있습니다. - 수의 범위가 충분히 크므로, 배열 대신 해시 기반 unordered_set으로 페어 존재 여부 쿼리 및 삽입을 O(1)에 수행할 수 있습니다. - 최악의 경우, 모든 입력에 대해 unordered_set 쿼리 및 삽입을 수행하므로 총 시간 복잡도는 O(N), 공간 복잡도는 입력과 같으므로 O(N)입니다. -*/ - -#include -#include - -class Solution { -public: - std::vector twoSum(std::vector& nums, int target) { - std::unordered_set s; - - for(auto it = nums.begin(); it != nums.end(); it++) { - int x = *it; - if (s.find(target - x) != s.end()) { - return std::vector(x, target-x); - } else { - s.insert(x); - } - } - - // 문제의 조건에서 단 하나의 솔루션이 반드시 존재한다고 가정하므로, 이곳에 도달하지 않음. - throw std::runtime_error("No two sum solution found"); - } -};