From e953e4e2c7f37c689fea987e432164f4dc48d3dd Mon Sep 17 00:00:00 2001 From: farmerboy95 Date: Mon, 18 Dec 2023 23:41:15 +0800 Subject: [PATCH 1/2] Add two-sum --- docs/LeetCode/two-sum.md | 53 ++++++++++++++++++++++++++++++++++++++++ docs/navigation.md | 1 + 2 files changed, 54 insertions(+) create mode 100644 docs/LeetCode/two-sum.md diff --git a/docs/LeetCode/two-sum.md b/docs/LeetCode/two-sum.md new file mode 100644 index 0000000..b9bedb9 --- /dev/null +++ b/docs/LeetCode/two-sum.md @@ -0,0 +1,53 @@ +# Lớp bài tập Two-Sum + +## Nguồn + +Bài viết này được dựa trên editorial các bài tập có dạng Two-Sum trên LeetCode, nghĩa là các bài toán có dạng: Cho một mảng các số nguyên và một số nguyên `target`, hãy tìm $X$ số trong mảng có tổng theo một tiêu chí nào đó so với `target`. + +Code của các bài toán này có thể xem tại phần Luyện tập ở dưới cùng. + +## LeetCode 1 - Two Sum + +Cho một mảng các số nguyên `nums` có $N$ phần tử và một số nguyên `target`, hãy trả về chỉ số của hai số trong mảng có tổng bằng `target`. Bạn có thể giả sử rằng mỗi input chỉ có một lời giải duy nhất và bạn không thể dùng cùng một phần tử hai lần. Bạn có thể trả về kết quả theo bất kì thứ tự nào. + +Giới hạn: $2 \le N \le 10^4$, $-10^9 \le nums_i \le 10^9$, $-10^9 \le target \le 10^9$ + +Chi tiết đề bài có thể xem tại [đây](https://leetcode.com/problems/two-sum/). + +??? tip "Lời giải" + Ngoài cách duyệt trâu $O(N^2)$ ra, ta có thể để ý rằng khi xét một số $x$ (ở vị trí $i$) trong mảng, ta chỉ cần tìm xem có tồn tại một số $y$ trong mảng sao cho $x + y = target$ hay $y = target - x$. Ta có thể duy trì một hash table chứa các số nằm trước vị trí $i$ để tìm kiếm $y$ trong $O(1)$, nên nhớ là trung bình $O(1)$. Như vậy, với cách này độ phức tạp thời gian là $O(N)$, độ phức tạp bộ nhớ là $O(N)$. + + Ngoài ra ta còn có thể sắp xếp mảng trước, sau đó đặt 2 con trỏ ở đầu và cuối mảng, nếu tổng giá trị của hai con trỏ nhỏ hơn `target` thì ta tăng con trỏ ở đầu, ngược lại ta giảm con trỏ ở cuối. Độ phức tạp thời gian là $O(N \log N)$, độ phức tạp bộ nhớ là $O(1)$. + +## LeetCode 15 - 3Sum + +Cho một mảng các số nguyên `nums` có $N$ phần tử, hãy trả về tất cả các bộ ba số không trùng nhau trong mảng có tổng bằng 0. Kết quả không được chứa các bộ ba số trùng nhau về mặt giá trị. + +Giới hạn: $3 \le N \le 3000$, $-10^5 \le nums_i \le 10^5$ + +Chi tiết đề bài có thể xem tại [đây](https://leetcode.com/problems/3sum/). + +??? tip "Lời giải" + Với mỗi số $x$ (ở vị trí $i$) trong mảng, ta tìm kiếm xem bên phải nó có tồn tại một bộ đôi $(y, z)$ trong mảng sao cho $y + z = -x$ hay không. Đến đây bài toán lại trở về với bài toán Two Sum. Tuy nhiên, ta không xét các số $x$ bằng nhau vì nó sẽ tạo ra các bộ ba giống nhau. Để bớt thời gian xét bằng nhau thì ta có thể sắp xếp mảng trước, sau đó ta chỉ xét số $x$ một lần khi nó khác số $x$ ở vị trí trước đó. Còn về việc tìm $y$ và $z$. Ta có thể tìm bằng hash table hoặc 2 con trỏ như ở bài Two Sum, tuy nhiên sau khi tìm được một bộ ba, ta phải tránh trùng lặp bằng cách cho di chuyển $y$ hoặc $z$ (tuỳ theo cách làm) sang một giá trị mới. Độ phức tạp thời gian là $O(N^2)$, độ phức tạp bộ nhớ là $O(N)$. + + Ta cũng có thể làm cách không sắp xếp. Đáp án của chúng ta sẽ là một `set` luôn, ta cũng duy trì một hashset để tránh trùng lặp $x$. Khi duyệt tìm $z$ ở bên phải $x$, ta lưu lại xem ta có thấy $y = -x - z$ trong một hash table `seen` hay chưa, trick ở đây là ta lưu `seen[nums[j]] = i` để không phải xoá hash table sau mỗi lần duyệt $x$. Ta cũng không cần phải để ý xem $y$ và $z$ có khả năng trùng không vì nếu trùng thì set đáp án sẽ lo. Độ phức tạp thời gian là $O(N^2)$, độ phức tạp bộ nhớ là $O(N)$. + +## LeetCode 16 - 3Sum Closest + +## LeetCode 259 - 3Sum Smaller + +## LeetCode 18 - 4Sum + +## LeetCode 454 - 4Sum II + +## Luyện tập + +| Problem | Status | Submission | Code | Date | +| :---: | :-----------: | :---: | :---: | :---: | +| [LC 1 - Two Sum](https://leetcode.com/problems/two-sum/) - Hash Table Approach | :white_check_mark: | Unavailable public link | [Code]() | | +| [LC 1 - Two Sum](https://leetcode.com/problems/two-sum/) - 2-pointer Approach | :white_check_mark: | Unavailable public link | [Code]() | | +| [LC 15 - 3Sum](https://leetcode.com/problems/3sum/) - Hash Table Approach | :white_check_mark: | Unavailable public link | [Code]() | | +| [LC 15 - 3Sum](https://leetcode.com/problems/3sum/) - 2-pointer Approach | :white_check_mark: | Unavailable public link | [Code]() | | +| [LC 15 - 3Sum](https://leetcode.com/problems/3sum/) - No-sort Approach | :white_check_mark: | Unavailable public link | [Code]() | | +| | :white_check_mark: | Unavailable public link | [Code]() | | +| | :white_check_mark: | Unavailable public link | [Code]() | | diff --git a/docs/navigation.md b/docs/navigation.md index 9247f2d..efea06b 100644 --- a/docs/navigation.md +++ b/docs/navigation.md @@ -49,6 +49,7 @@ comment: false - [Lấy mẫu hồ chứa (Reservoir Sampling)](LeetCode/reservoir-sampling.md) - [Tìm kiếm trên mảng đã sắp xếp nhưng đã xoay](LeetCode/search-in-rotated-sorted-array.md) - [Tính toán biểu thức từ xâu](LeetCode/basic-calculator.md) + - [Lớp bài tập Two-Sum](LeetCode/two-sum.md) - Nguồn khác - Phân tích Độ phức tạp - [Các ký hiệu tiệm cận](misc/complexity-analysis/asymptotic-notations.md) From 898d88f55110942c59c9cfd53803d103c982e026 Mon Sep 17 00:00:00 2001 From: farmerboy95 Date: Wed, 20 Dec 2023 17:44:56 +0800 Subject: [PATCH 2/2] Complete two-sum --- docs/LeetCode/two-sum.md | 66 +++++++++++++++++++++++++++++++++------- 1 file changed, 55 insertions(+), 11 deletions(-) diff --git a/docs/LeetCode/two-sum.md b/docs/LeetCode/two-sum.md index b9bedb9..eaf5521 100644 --- a/docs/LeetCode/two-sum.md +++ b/docs/LeetCode/two-sum.md @@ -10,20 +10,20 @@ Code của các bài toán này có thể xem tại phần Luyện tập ở dư Cho một mảng các số nguyên `nums` có $N$ phần tử và một số nguyên `target`, hãy trả về chỉ số của hai số trong mảng có tổng bằng `target`. Bạn có thể giả sử rằng mỗi input chỉ có một lời giải duy nhất và bạn không thể dùng cùng một phần tử hai lần. Bạn có thể trả về kết quả theo bất kì thứ tự nào. -Giới hạn: $2 \le N \le 10^4$, $-10^9 \le nums_i \le 10^9$, $-10^9 \le target \le 10^9$ +Giới hạn: $2 \le N \le 10^4$, $-10^9 \le nums_i \le 10^9$, $-10^9 \le target \le 10^9$. Chi tiết đề bài có thể xem tại [đây](https://leetcode.com/problems/two-sum/). ??? tip "Lời giải" Ngoài cách duyệt trâu $O(N^2)$ ra, ta có thể để ý rằng khi xét một số $x$ (ở vị trí $i$) trong mảng, ta chỉ cần tìm xem có tồn tại một số $y$ trong mảng sao cho $x + y = target$ hay $y = target - x$. Ta có thể duy trì một hash table chứa các số nằm trước vị trí $i$ để tìm kiếm $y$ trong $O(1)$, nên nhớ là trung bình $O(1)$. Như vậy, với cách này độ phức tạp thời gian là $O(N)$, độ phức tạp bộ nhớ là $O(N)$. - Ngoài ra ta còn có thể sắp xếp mảng trước, sau đó đặt 2 con trỏ ở đầu và cuối mảng, nếu tổng giá trị của hai con trỏ nhỏ hơn `target` thì ta tăng con trỏ ở đầu, ngược lại ta giảm con trỏ ở cuối. Độ phức tạp thời gian là $O(N \log N)$, độ phức tạp bộ nhớ là $O(1)$. + Ngoài ra ta còn có thể tạo một mảng khác lưu giá trị và vị trí của các phần tử trong mảng ban đầu, sắp xếp nó theo thứ tự tăng dần giá trị, sau đó đặt 2 con trỏ ở đầu và cuối mảng, nếu tổng giá trị của hai con trỏ nhỏ hơn `target` thì ta tăng con trỏ ở đầu, ngược lại ta giảm con trỏ ở cuối. Độ phức tạp thời gian là $O(N \log N)$, độ phức tạp bộ nhớ là $O(N)$. ## LeetCode 15 - 3Sum -Cho một mảng các số nguyên `nums` có $N$ phần tử, hãy trả về tất cả các bộ ba số không trùng nhau trong mảng có tổng bằng 0. Kết quả không được chứa các bộ ba số trùng nhau về mặt giá trị. +Cho một mảng các số nguyên `nums` có $N$ phần tử, hãy trả về tất cả các bộ ba số không trùng vị trí với nhau trong mảng có tổng bằng 0. Kết quả không được chứa các bộ ba số trùng nhau về mặt giá trị, ví dụ nếu có hai bộ ba $[1, 2, 3]$ thì chỉ tính một lần. -Giới hạn: $3 \le N \le 3000$, $-10^5 \le nums_i \le 10^5$ +Giới hạn: $3 \le N \le 3000$, $-10^5 \le nums_i \le 10^5$. Chi tiết đề bài có thể xem tại [đây](https://leetcode.com/problems/3sum/). @@ -34,20 +34,64 @@ Chi tiết đề bài có thể xem tại [đây](https://leetcode.com/problems/ ## LeetCode 16 - 3Sum Closest +Cho một mảng các số nguyên `nums` có $N$ phần tử và một số nguyên `target`, hãy trả về tổng của 3 số trong mảng có tổng gần nhất với `target`. Giả sử rằng mỗi input chỉ có một lời giải duy nhất. + +Giới hạn: $3 \le N \le 500$, $-10^3 \le nums_i \le 10^3$, $-10^4 \le target \le 10^4$. + +??? tip "Lời giải" + Đầu tiên ta sắp xếp lại mảng, sau đó với mỗi giá trị $x$ (ở vị trí $i$), ta tìm xem trong phần bên phải của $x$ có $y$ và $z$ sao cho $y + z$ gần nhất với $target - x$ hay không. Để tìm $y$ và $z$ ta có thể dùng 2 con trỏ như ở bài Two Sum. Độ phức tạp thời gian là $O(N^2)$, độ phức tạp bộ nhớ là $O(\log N)$ hoặc $O(N)$, tuỳ vào thuật toán sắp xếp. + + Ngoài ra, ta cũng có thể chặt nhị phân tìm $y$ giữa $x$ và $z$ sau khi sắp xếp mảng. Độ phức tạp thời gian là $O(N^2 \log N)$, độ phức tạp bộ nhớ là $O(\log N)$ hoặc $O(N)$, tuỳ vào thuật toán sắp xếp. + ## LeetCode 259 - 3Sum Smaller +Cho một mảng các số nguyên `nums` có $N$ phần tử và một số nguyên `target`, hãy trả về số bộ ba số trong mảng có tổng nhỏ hơn `target`. + +Giới hạn: $0 \le N \le 3500$, $-100 \le nums_i \le 100$, $-100 \le target \le 100$. + +??? tip "Lời giải" + Ta cũng có hai cách như bài trước, và đều phải sắp xếp mảng trước khi tính toán. + + Đầu tiên, với chặt nhị phân, ta tìm $y$ lớn nhất làm tổng nhỏ hơn `target`, rồi thêm đoạn từ $i+1$ đến vị trí của $y$ vào kết quả. Độ phức tạp thời gian là $O(N^2 \log N)$, độ phức tạp bộ nhớ là $O(\log N)$ hoặc $O(N)$, tuỳ vào thuật toán sắp xếp. + + Với 2 con trỏ, ta đặt chúng ở $i+1$ và cuối mảng, nếu tổng 3 số nhỏ hơn target thì ta có thể thêm đoạn từ con trỏ trái đến con trỏ phải vào kết quả, sau đó ta tăng con trỏ trái, ngược lại ta giảm con trỏ phải. Độ phức tạp thời gian là $O(N^2)$, độ phức tạp bộ nhớ là $O(\log N)$ hoặc $O(N)$, tuỳ vào thuật toán sắp xếp. + ## LeetCode 18 - 4Sum +Cho một mảng các số nguyên `nums` có $N$ phần tử và một số nguyên `target`, hãy trả về tất cả các bộ 4 số (riêng biệt về vị trí) trong mảng có tổng bằng `target`. Kết quả không được chứa các bộ 4 số trùng nhau về mặt giá trị. + +Giới hạn: $4 \le N \le 200$, $-10^9 \le nums_i \le 10^9$, $-10^9 \le target \le 10^9$. + +??? tip "Lời giải" + Ta giải y hệt bài 3Sum, có điều ta cần duyệt 2 vòng lặp ngoài nhau để xét $x$ và $y$. Hai số còn lại ta có thể tìm bằng hash table hoặc 2 con trỏ. Độ phức tạp thời gian là $O(N^3)$, độ phức tạp bộ nhớ là $O(N)$. + ## LeetCode 454 - 4Sum II +Cho 4 mảng các số nguyên `A`, `B`, `C`, `D` có $N$ phần tử, hãy trả về số bộ 4 vị trí trong 4 mảng có tổng bằng 0. + +Giới hạn: $0 \le N \le 200$, $-2^{28} \le A_i, B_i, C_i, D_i \le 2^{28}$. + +??? tip "Lời giải" + Ta dùng hash table để lưu số các tổng của 2 số trong 2 mảng đầu tiên. Sau đó làm tương tự với 2 mảng còn lại, khi xét thì ta tìm xem trong hash table có âm của tổng đang xét hay không, nếu có thì thêm vào đáp án. Độ phức tạp thời gian là $O(N^2)$, độ phức tạp bộ nhớ là $O(N^2)$. + + Nếu người ta hỏi $k$ mảng thì sao? Ta chia số mảng này ra hai phần, một phần có $\lfloor k/2 \rfloor$ mảng, một phần có $\lceil k/2 \rceil$ mảng. Sau đó ta dùng hash table cho một phần, rồi duyệt phần còn lại y hệt như trên. Độ phức tạp thời gian là $O(N^{\lceil k/2 \rceil})$, độ phức tạp bộ nhớ là $O(N^{\lceil k/2 \rceil})$. + ## Luyện tập +Dưới đây là code mẫu của toàn bộ các cách giải các bài toán trên. + | Problem | Status | Submission | Code | Date | | :---: | :-----------: | :---: | :---: | :---: | -| [LC 1 - Two Sum](https://leetcode.com/problems/two-sum/) - Hash Table Approach | :white_check_mark: | Unavailable public link | [Code]() | | -| [LC 1 - Two Sum](https://leetcode.com/problems/two-sum/) - 2-pointer Approach | :white_check_mark: | Unavailable public link | [Code]() | | -| [LC 15 - 3Sum](https://leetcode.com/problems/3sum/) - Hash Table Approach | :white_check_mark: | Unavailable public link | [Code]() | | -| [LC 15 - 3Sum](https://leetcode.com/problems/3sum/) - 2-pointer Approach | :white_check_mark: | Unavailable public link | [Code]() | | -| [LC 15 - 3Sum](https://leetcode.com/problems/3sum/) - No-sort Approach | :white_check_mark: | Unavailable public link | [Code]() | | -| | :white_check_mark: | Unavailable public link | [Code]() | | -| | :white_check_mark: | Unavailable public link | [Code]() | | +| [LC 1 - Two Sum](https://leetcode.com/problems/two-sum/) - Hash Table Approach | :white_check_mark: | Unavailable public link | [Code](https://github.com/farmerboy95/CompetitiveProgramming/blob/master/LeetCode/LC1-two-sum(HashTable).cpp) | 20/12/2023 | +| [LC 1 - Two Sum](https://leetcode.com/problems/two-sum/) - 2-pointer Approach | :white_check_mark: | Unavailable public link | [Code](https://github.com/farmerboy95/CompetitiveProgramming/blob/master/LeetCode/LC1-two-sum(2-pointer).cpp) | 20/12/2023 | +| [LC 15 - 3Sum](https://leetcode.com/problems/3sum/) - Hash Table Approach | :white_check_mark: | Unavailable public link | [Code](https://github.com/farmerboy95/CompetitiveProgramming/blob/master/LeetCode/LC15-3sum(HashTable).cpp) | 20/12/2023 | +| [LC 15 - 3Sum](https://leetcode.com/problems/3sum/) - 2-pointer Approach | :white_check_mark: | Unavailable public link | [Code](https://github.com/farmerboy95/CompetitiveProgramming/blob/master/LeetCode/LC15-3sum(2-pointer).cpp) | 20/12/2023 | +| [LC 15 - 3Sum](https://leetcode.com/problems/3sum/) - No-sort Approach | :white_check_mark: | Unavailable public link | [Code](https://github.com/farmerboy95/CompetitiveProgramming/blob/master/LeetCode/LC15-3sum(NoSort).cpp) | 20/12/2023 | +| [LC 16 - 3Sum Closest](https://leetcode.com/problems/3sum-closest/) - 2-pointer Approach | :white_check_mark: | Unavailable public link | [Code](https://github.com/farmerboy95/CompetitiveProgramming/blob/master/LeetCode/LC16-3sum-closest(2-pointer).cpp) | 20/12/2023 | +| [LC 16 - 3Sum Closest](https://leetcode.com/problems/3sum-closest/) - Binary Search Approach | :white_check_mark: | Unavailable public link | [Code](https://github.com/farmerboy95/CompetitiveProgramming/blob/master/LeetCode/LC16-3sum-closest(BinarySearch).cpp) | 20/12/2023 | +| [LC 259 - 3Sum Smaller](https://leetcode.com/problems/3sum-smaller/) - 2-pointer Approach | :white_check_mark: | Unavailable public link | [Code](https://github.com/farmerboy95/CompetitiveProgramming/blob/master/LeetCode/LC259-3sum-smaller(2-pointer).cpp) | 20/12/2023 | +| [LC 259 - 3Sum Smaller](https://leetcode.com/problems/3sum-smaller/) - Binary Search Approach | :white_check_mark: | Unavailable public link | [Code](https://github.com/farmerboy95/CompetitiveProgramming/blob/master/LeetCode/LC259-3sum-smaller(BinarySearch).cpp) | 20/12/2023 | +| [LC 18 - 4Sum](https://leetcode.com/problems/4sum/) - Hash Table Approach | :white_check_mark: | Unavailable public link | [Code](https://github.com/farmerboy95/CompetitiveProgramming/blob/master/LeetCode/LC18-4sum(HashTable).cpp) | 20/12/2023 | +| [LC 18 - 4Sum](https://leetcode.com/problems/4sum/) - 2-pointer Approach | :white_check_mark: | Unavailable public link | [Code](https://github.com/farmerboy95/CompetitiveProgramming/blob/master/LeetCode/LC18-4sum(2-pointer).cpp) | 20/12/2023 | +| [LC 454 - 4Sum II](https://leetcode.com/problems/4sum-ii/) - 4Sum Approach | :white_check_mark: | Unavailable public link | [Code](https://github.com/farmerboy95/CompetitiveProgramming/blob/master/LeetCode/LC454-4sum-ii(4sum).cpp) | 20/12/2023 | +| [LC 454 - 4Sum II](https://leetcode.com/problems/4sum-ii/) - k-Sum Approach | :white_check_mark: | Unavailable public link | [Code](https://github.com/farmerboy95/CompetitiveProgramming/blob/master/LeetCode/LC454-4sum-ii(ksum).cpp) | 20/12/2023 |