Lập Trình Viên Số Khổ
Senior Member
Nay em lại phải xem giải, kinh nghiệm rút ra là dù bắt O(n), thì bên for trong vẫn dùng while đc. Vì nếu các while ở mỗi loop không overlap thì tối phức tạp vẫn O(n)
class Solution {
public:
int longestConsecutive(vector<int>& nums) {
set<int> mySet(nums.begin(), nums.end());
int ret = 0;
int prev = INT_MIN;
int curLen = 0;
for (auto x : mySet){
if (x == prev + 1) curLen++;
else curLen = 1;
ret = max(ret, curLen);
prev = x;
}
return ret;
}
};
class Solution {
public:
int longestConsecutive(vector<int>& nums) {
map<int, int> startToEnd;
for (auto x : nums) {
auto it = startToEnd.upper_bound(x);
if (it != startToEnd.end() && it != startToEnd.begin()
&& prev(it)->second == x && it->first == x + 1) {
prev(it)->second = it->second;
startToEnd.erase(it);
} else if (it != startToEnd.end() && it->first == x + 1) {
startToEnd[x] = it->second;
startToEnd.erase(it);
} else if (it != startToEnd.begin() && prev(it)->second == x) {
prev(it)->second++;
} else if (it == startToEnd.begin() || prev(it)->second < x) {
startToEnd[x] = x+1;
}
}
int ret = 0;
for (auto x : startToEnd){
ret = max(ret, x.second - x.first);
}
return ret;
}
};
class Solution {
public:
int longestConsecutive(vector<int>& nums) {
unordered_map<int, bool> visited;
for (auto x : nums) visited[x] = false;
int ret = 0;
for (auto x : visited){
if (x.second) continue;
int e = x.first;
int curLen = 1;
while (visited.find(e+1) != visited.end() && !visited[e+1]){
visited[e+1] = true;
e++;
curLen++;
}
e = x.first;
while (visited.find(e-1) != visited.end() && !visited[e-1]){
visited[e-1] = true;
e--;
curLen++;
}
visited[e]=true;
ret = max(ret, curLen);
}
return ret;
}
};
Đề bài yêu cầu O(n) mà thímBài này quen nên solution của em là Brute force cơ bản + phân tích kiểu dây chun để lấy On:
- Sort array, streak và maxStreak nên là 1 (dù cỡ nào cũng min là 1, nếu arr empty thì res là 0)
- Xét từ 1 -> length - 1, kiểm tra x != x[i-1] và x = x[i-1] + 1 hay không, nếu có thì streak tăng 1, còn không thì so sánh Max giữa streak và maxStreak , streak sẽ quay về 1, tương tự xét vòng mảng mới while-xx
[*]Kết quả sẽ là 1 lần nữa so sánh Max streak và maxStreak
Java:public int longestConsecutive(int[] nums) { int length = nums.length; Arrays.sort(nums); if (length == 0) { return 0; } int streak = 1, maxStreak = 1; for (int i = 1; i < length; i++) { if (nums[i] != nums[i-1]) { if (nums[i -1] +1 == nums[i]) { streak += 1; } else { maxStreak = Math.max(maxStreak, streak); streak = 1; } } else { continue; } } return Math.max(maxStreak, streak); }
10^5 thì O(NlogN) cũng không bị TLE nhé,Nay em lại phải xem giải, kinh nghiệm rút ra là dù bắt O(n), thì bên for trong vẫn dùng while đc. Vì nếu các while ở mỗi loop không overlap thì tối phức tạp vẫn O(n)
Chà vậy mình sai rồi, chắc dùng Hashset với while lồng trong for thửĐề bài yêu cầu O(n) mà thím
đề nó bắt O(N) bác ơi, cách bác có sort sẵn theo cái black tree gì là sai rồi ;v. Nhưng chắc bọn leetcode ra đề bắt O(N) nhưng vẫn cho phép NlogN, đáng lẽ phải cho 10^910^5 thì O(NlogN) cũng được nha
Uhm, O(NlogN) vẫn pass, beat 80%, . Để update thêm cách O(n).đề nó bắt O(N) bác ơi, cách bác có sort sẵn theo cái black tree gì là sai rồi ;v. Nhưng chắc bọn leetcode ra đề bắt O(N) nhưng vẫn cho phép NlogN, đáng lẽ phải cho 10^9
Bài này giống bài của bác Fire of Heart, constraint không gắt bằng thôi
Solution 1:
Time complexity: O(nlogn)
Space complexity: O(1)
- Nếu liền nhau => tiếp tục so sánh theo cặp
- Sort mảng, sau đó so sánh phần tử hiện tại với phần tử kế tiếp.
- Với hai phần tử:
- Nếu bằng nhau => tăng vị trí của phần tử kế tiếp lên một đơn vị
- Nếu không bằng => break
Vì con trỏ đến phần tử kế tiếp đã được process nên không cần thiết phải process lại lần nữa, cuối vòng lặp sẽ trỏ con trỏ hiện tại tới con trỏ phần tử kếJava:class Solution { public int longestConsecutive(int[] nums) { if (nums.length <= 1) return nums.length; Arrays.sort(nums); int max = 1; int curPos = 0; while (curPos < nums.length) { int nextPos = curPos + 1; int count = 1; int curVal = nums[curPos]; while (nextPos < nums.length) { if (nums[nextPos] - 1 == curVal) { curVal = nums[nextPos]; ++count; ++nextPos; } else if (nums[nextPos] == curVal) ++nextPos; else break; } curPos = nextPos; max = Math.max(max, count); } return max; } }
Solution 2:
Time complexity: O(n)
Space complexity: O(n)
- Nếu tồn tại trong set:
- Tạo một cái set để add hết tất cả phần tử unique vào set
- Process từng phần tử trong mảng ban đầu:
- Tạo một biến tạm bằng phần tử hiện tại rồi tăng dần + giảm dần, tổng số phần tử thu được là tổng của hiệu số phần tử - biến tạm (đối với giảm dần) và hiệu số biến tạm - phần tử (đối với tăng dần)
- Với mỗi phần tử tăng/giảm, nếu thỏa mãn, loại khỏi set ban đầu
- Nếu không tồn tại, bỏ qua
Vì phần tử thỏa mãn đã được loại nên không cần phải process đối với phần tử trùng lặpJava:class Solution { public int longestConsecutive(int[] nums) { if (nums.length <= 1) return nums.length; Set<Integer> set = new HashSet<>(); for (int num : nums) { set.add(num); } int max = 1; for (int num : nums) { if (set.contains(num)) { set.remove(num); int cur = num; while (set.remove(cur - 1)) { --cur; } int count = num - cur; cur = num; while (set.remove(cur + 1)) { ++cur; } count += cur - num; max = Math.max(count + 1, max); } } return max; } }
Vậy mà thời gian chạy của cách 1 toàn nhanh hơn cách 2
radix sort là O(N) vậy thím làm được mọi bài cần sort mà chỉ cần O(N) với radix sort à, thế ảo quá nhỉbài LCS này cần O(n) nên nghĩ đến sử dụng radix sort
trong related topic có union find nên em cài đặt thửC++:class Solution { public: int longestConsecutive(vector<int>& nums) { if (nums.size() == 0) { return 0; } for (int &d : nums) { d += 1000000000; // nums[i] in [0...2e9] } vector<vector<int>> buckets(10, vector<int>()); // 10 buckets for sorting [0...9] long d1 = 10, d2 = 1; for (int i = 0; i < 10; i++) { for (int &d : nums) { buckets[(d % d1) / d2].push_back(d); } int j = 0; for (auto &bucket : buckets) { for (int &d : bucket) { nums[j++] = d; } bucket.clear(); } d1 *= 10; d2 *= 10; } int ans = 0; int c = 0; for (int i = 1; i < nums.size(); i++) { if (nums[i] == nums[i - 1] + 1) { c++; } else if (nums[i] == nums[i - 1]) { continue; } else { ans = max(ans, c + 1); c = 0; } } ans = max(ans, c + 1); return ans; } };
C++:class Solution { public: inline int findParent(vector<int> &p, int i) { while (p[i] != -1) { i = p[i]; } return i; } int longestConsecutive(vector<int>& nums) { unordered_map<int, int> m; vector<int> l(nums.size(), 1), p(nums.size(), -1); // l[i] is length of CS from nums[i], p[i] is index of parent of index i int ans = 0; for (int i = 0; i < nums.size(); i++) { if (m.find(nums[i]) == m.end()) { m[nums[i]] = i; l[i] = 1; if (m.find(nums[i] + 1) != m.end()) { p[m[nums[i] + 1]] = i; l[i] += l[m[nums[i] + 1]]; } ans = max(ans, l[i]); if (m.find(nums[i] - 1) != m.end()) { int p0 = findParent(p, m[nums[i] - 1]); p[i] = p0; l[p0] += l[i]; ans = max(ans, l[p0]); } } } return ans; } };
Lượng thời gian là tổng thời gian của các test case đó. Nên có mấy cái số lượng element nó nhỏ thì built-in sort tối ưu
Mà ở dưới cũng sử dụng kha khá các operations của set nên cũng ngốn thời gian
radix sort là O(N) vậy thím làm được mọi bài cần sort mà chỉ cần O(N) với radix sort à, thế ảo quá nhỉ
Hầu hết các sort ngon ngon đều là O(N) còn space của radix là O(N+K) với K là số lượng digits, thì với max của phần tử trong dãy giới hạn 10^18 sẽ có SC là O(N + 18). Thêm có 18, quá ngon rồi bác, hay em sai gì nhỉ @@Radix/Counting sort cần O(n) space nên nếu mảng quá lớn thì sẽ ko hiệu quả bằng quicksort O(1) space.
Radix và Counting sort chỉ cho non negative integers thôi bác, còn các loại khác là generalHầu hết các sort ngon ngon đều là O(N) còn space của radix là O(N+K) với K là số lượng digits, thì với max của phần tử trong dãy giới hạn 10^18 sẽ có SC là O(N + 18). Thêm có 18, quá ngon rồi bác, hay em sai gì nhỉ @@
View attachment 1247386
thế cũng ngon rồi bác, thấy bài trên leetcode toàn số nguyên, nhưng đa phần cho phép O(NlogN), nếu bài nào bắt O(N) thì radix sort quá ngonRadix và Counting sort chỉ cho non negative integers thôi bác, còn các loại khác là general
O(n) < O(nlogn) chỉ đúng khi n đủ lớn. Và trong case sort này thì n đủ lớn chắc phải tầm 10^9 trở lên. Ít khi nào có test case được to vậy, nên dùng radix sort thì chỉ để ăn gian O(n) lý thuyết thôi chứ runtime hầu như là chậm hơn built-in sort.Lượng thời gian là tổng thời gian của các test case đó. Nên có mấy cái số lượng element nó nhỏ thì built-in sort tối ưu
Mà ở dưới cũng sử dụng kha khá các operations của set nên cũng ngốn thời gian
radix sort là O(N) vậy thím làm được mọi bài cần sort mà chỉ cần O(N) với radix sort à, thế ảo quá nhỉ
Việc ngon thì đầy rồi. Nhưng không phải nhờ thuật toán hay cày LC.Hỏi thật các bác học thuật toán đã kiếm được việc ngon chưa? Em làm hơn 400 bài leetcode, hơn 500 bài các trang khác mà đi phỏng vấn chả ai hỏi.
Có hỏi cũng toàn lý thuyết.
Hỏi thật các bác học thuật toán đã kiếm được việc ngon chưa? Em làm hơn 400 bài leetcode, hơn 500 bài các trang khác mà đi phỏng vấn chả ai hỏi.
Có hỏi cũng toàn lý thuyết.
Hỏi thật các bác học thuật toán đã kiếm được việc ngon chưa? Em làm hơn 400 bài leetcode, hơn 500 bài các trang khác mà đi phỏng vấn chả ai hỏi.
Có hỏi cũng toàn lý thuyết.
Cũng có thể là do bạn apply chưa đúng chỗ. K phải chỗ nào cũng yêu cầu phải strong về DSA. Nên lần sau apply vào đâu thì nhớ đọc kỹ JD.Hỏi thật các bác học thuật toán đã kiếm được việc ngon chưa? Em làm hơn 400 bài leetcode, hơn 500 bài các trang khác mà đi phỏng vấn chả ai hỏi.
Có hỏi cũng toàn lý thuyết.
30% thì nhiều quá, mình thấy đếm trên đầu ngón tay các công ty có 1 vòng thuật toán.Việc ngon thì đầy rồi. Nhưng không phải nhờ thuật toán hay cày LC.
Thuật toán chỉ là bước screening, đánh giá đầu vào thôi. Chiếm không đến 30% kết quả phỏng vấn chung. Nó là điều kiện cần chứ không phải là điều kiện đủ.
Để hôm nào rảnh tạo thêm topic cho ae bàn về phỏng vấn. Interviewer care cái gì, với mỗi level thì mình cần tập trung phần nà
em cũng hơn 400 câu mà có thấy Google nào đâuBác làm 400 bài Leetcode. Vậy chắc qua Google làm được rồi còn gì.