From 0294dc262bd2fdf0231d63bf5605e4fcc0e35952 Mon Sep 17 00:00:00 2001 From: Thuan Le Date: Thu, 12 Jan 2023 19:36:32 -0800 Subject: [PATCH 01/45] create 0344-reverse-string-csharp --- csharp/0344-reverse-string.cs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 csharp/0344-reverse-string.cs diff --git a/csharp/0344-reverse-string.cs b/csharp/0344-reverse-string.cs new file mode 100644 index 000000000..4b8abc569 --- /dev/null +++ b/csharp/0344-reverse-string.cs @@ -0,0 +1,14 @@ +class Solution +{ + public void ReverseString(char[] s) + { + int leftPointer = 0; + int rightPointer = s.Length - 1; + while (leftPointer < rightPointer) + { + char temp = s[leftPointer]; + s[leftPointer++] = s[rightPointer]; + s[rightPointer--] = temp; + } + } +} \ No newline at end of file From c4ff1cc35c6e65f2cbbed8d8d99b984b352d0c3d Mon Sep 17 00:00:00 2001 From: Sri Hari <94112314+Srihari2222@users.noreply.github.com> Date: Thu, 28 Nov 2024 18:24:39 +0530 Subject: [PATCH 02/45] Batch-3/Neetcode-150/Added hints (#3750) --- articles/pacific-atlantic-water-flow.md | 2 +- hints/clone-graph.md | 31 ++++++++++++++++++++ hints/count-number-of-islands.md | 23 +++++++++++++++ hints/course-schedule.md | 31 ++++++++++++++++++++ hints/design-twitter-feed.md | 39 +++++++++++++++++++++++++ hints/islands-and-treasure.md | 31 ++++++++++++++++++++ hints/max-area-of-island.md | 31 ++++++++++++++++++++ hints/pacific-atlantic-water-flow.md | 31 ++++++++++++++++++++ hints/rotting-fruit.md | 31 ++++++++++++++++++++ hints/surrounded-regions.md | 31 ++++++++++++++++++++ 10 files changed, 280 insertions(+), 1 deletion(-) create mode 100644 hints/clone-graph.md create mode 100644 hints/count-number-of-islands.md create mode 100644 hints/course-schedule.md create mode 100644 hints/design-twitter-feed.md create mode 100644 hints/islands-and-treasure.md create mode 100644 hints/max-area-of-island.md create mode 100644 hints/pacific-atlantic-water-flow.md create mode 100644 hints/rotting-fruit.md create mode 100644 hints/surrounded-regions.md diff --git a/articles/pacific-atlantic-water-flow.md b/articles/pacific-atlantic-water-flow.md index a866accf0..1b718571a 100644 --- a/articles/pacific-atlantic-water-flow.md +++ b/articles/pacific-atlantic-water-flow.md @@ -1,4 +1,4 @@ -## 1. Backtracking +## 1. Brute Force (Backtracking) ::tabs-start diff --git a/hints/clone-graph.md b/hints/clone-graph.md new file mode 100644 index 000000000..1e332bf4a --- /dev/null +++ b/hints/clone-graph.md @@ -0,0 +1,31 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution with O(V + E) time and O(E) space, where V is the number of vertices and E is the number of edges in the given graph. +

+
+ +
+
+ Hint 1 +

+ We are given only the reference to the node in the graph. Cloning the entire graph means we need to clone all the nodes as well as their child nodes. We can't just clone the node and its neighbor and return the node. We also need to clone the entire graph. Can you think of a recursive way to do this, as we are cloning nodes in a nested manner? Also, can you think of a data structure that can store the nodes with their cloned references? +

+
+ +
+
+ Hint 2 +

+ We can use the Depth First Search (DFS) algorithm. We use a hash map to map the nodes to their cloned nodes. We start from the given node. At each step of the DFS, we create a node with the current node's value. We then recursively go to the current node's neighbors and try to clone them first. After that, we add their cloned node references to the current node's neighbors list. Can you think of a base condition to stop this recursive path? +

+
+ +
+
+ Hint 3 +

+ We stop this recursive path when we encounter a node that has already been cloned or visited. This DFS approach creates an exact clone of the given graph, and we return the clone of the given node. +

+
\ No newline at end of file diff --git a/hints/count-number-of-islands.md b/hints/count-number-of-islands.md new file mode 100644 index 000000000..a8cb92e6f --- /dev/null +++ b/hints/count-number-of-islands.md @@ -0,0 +1,23 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution with O(m * n) time and O(m * n) space, where m is the number of rows and n is the number of columns in the grid. +

+
+ +
+
+ Hint 1 +

+ An island is a group of 1's in which every 1 is reachable from any other 1 in that group. Can you think of an algorithm that can find the number of groups by visiting a group only once? Maybe there is a recursive way of doing it. +

+
+ +
+
+ Hint 2 +

+ We can use the Depth First Search (DFS) algorithm to traverse each group independently. We iterate through each cell of the grid. When we encounter a 1, we perform a DFS starting at that cell and recursively visit every other 1 that is reachable. During this process, we mark the visited 1's as 0 to ensure we don't revisit them, as they belong to the same group. The number of groups corresponds to the number of islands. +

+
\ No newline at end of file diff --git a/hints/course-schedule.md b/hints/course-schedule.md new file mode 100644 index 000000000..1660429b5 --- /dev/null +++ b/hints/course-schedule.md @@ -0,0 +1,31 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution with O(V + E) time and O(V + E) space, where V is the number of courses (nodes) and E is the number of prerequisites (edges). +

+
+ +
+
+ Hint 1 +

+ Consider the problem as a graph where courses represent the nodes, and prerequisite[i] = [a, b] represents a directed edge from a to b. We need to determine whether the graph contains a cycle. Why? Because if there is a cycle, it is impossible to complete the courses involved in the cycle. Can you think of an algorithm to detect cycle in a graph? +

+
+ +
+
+ Hint 2 +

+ We can use the Depth First Search (DFS) algorithm to detect a cycle in a graph. We iterate over each course, run a DFS from that course, and first try to finish its prerequisite courses by recursively traversing through them. To detect a cycle, we initialize a hash set called path, which contains the nodes visited in the current DFS call. If we encounter a course that is already in the path, we can conclude that a cycle is detected. How would you implement it? +

+
+ +
+
+ Hint 3 +

+ We run a DFS starting from each course by initializing a hash set, path, to track the nodes in the current DFS call. At each step of the DFS, we return false if the current node is already in the path, indicating a cycle. We recursively traverse the neighbors of the current node, and if any of the neighbor DFS calls detect a cycle, we immediately return false. Additionally, we clear the neighbors list of a node when no cycle is found from that node to avoid revisiting those paths again. +

+
\ No newline at end of file diff --git a/hints/design-twitter-feed.md b/hints/design-twitter-feed.md new file mode 100644 index 000000000..4dcbd38e4 --- /dev/null +++ b/hints/design-twitter-feed.md @@ -0,0 +1,39 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution with O(n) time for each getNewsFeed() function call, O(1) time for the remaining methods, and O((N * m) + (N * M) + n) space, where n is the number of followeeIds associated with the userId, m is the maximum number of tweets by any user, N is the total number of userIds, and M is the maximum number of followees for any user. +

+
+ +
+
+ Hint 1 +

+ Can you think of a data structure to store all the information, such as userIds and corresponding followeeIds, or userIds and their tweets? Maybe you should think of a hash data structure in terms of key-value pairs. Also, can you think of a way to determine that a tweet was posted before another tweet? +

+
+ +
+
+ Hint 2 +

+ We use a hash map followMap to store userIds and their unique followeeIds as a hash set. Another hash map, tweetMap, stores userIds and their tweets as a list of (count, tweetId) pairs. A counter count, incremented with each tweet, tracks the order of tweets. The variable count is helpful in distinguishing the time of tweets from two users. This way of storing data makes the functions follow(), unfollow(), and postTweet() run in O(1). Can you think of a way to implement getNewsFeed()? Maybe consider a brute force approach and try to optimize it. +

+
+ +
+
+ Hint 3 +

+ A naive solution would involve taking the tweets of the userId and its followeeIds into a list, sorting them in descending order based on their count values, and returning the top 10 tweets as the most recent ones. Can you think of a more efficient approach that avoids collecting all tweets and sorting? Perhaps consider a data structure and leverage the fact that each user's individual tweets list is already sorted. +

+
+ +
+
+ Hint 4 +

+ We can use a Max-Heap to efficiently retrieve the top 10 most recent tweets. For each followee and the userId, we insert their most recent tweet from the tweetMap into the heap, along with the tweet's count and its index in the tweet list. This index is necessary because after processing a tweet, we can insert the next most recent tweet from the same user's list. By always pushing and popping tweets from the heap, we ensure that the 10 most recent tweets are retrieved without sorting all tweets. +

+
\ No newline at end of file diff --git a/hints/islands-and-treasure.md b/hints/islands-and-treasure.md new file mode 100644 index 000000000..b7ef502de --- /dev/null +++ b/hints/islands-and-treasure.md @@ -0,0 +1,31 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution with O(m * n) time and O(m * n) space, where m is the number of rows and n is the number of columns in the given grid. +

+
+ +
+
+ Hint 1 +

+ A brute force solution would be to iterate on each land cell and run a BFS from that cells to find the nearest treasure chest. This would be an O((m * n)^2) solution. Can you think of a better way? Sometimes it is not optimal to go from source to destination. +

+
+ +
+
+ Hint 2 +

+ We can see that instead of going from every cell to find the nearest treasure chest, we can do it in reverse. We can just do a BFS from all the treasure chests in grid and just explore all possible paths from those chests. Why? Because in this approach, the treasure chests self mark the cells level by level and the level number will be the distance from that cell to a treasure chest. We don't revisit a cell. This approach is called Multi-Source BFS. How would you implement it? +

+
+ +
+
+ Hint 3 +

+ We insert all the cells (row, col) that represent the treasure chests into the queue. Then, we process the cells level by level, handling all the current cells in the queue at once. For each cell, we mark it as visited and store the current level value as the distance at that cell. We then try to add the neighboring cells (adjacent cells) to the queue, but only if they have not been visited and are land cells. +

+
\ No newline at end of file diff --git a/hints/max-area-of-island.md b/hints/max-area-of-island.md new file mode 100644 index 000000000..a8502e52a --- /dev/null +++ b/hints/max-area-of-island.md @@ -0,0 +1,31 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution with O(m * n) time and O(m * n) space, where m is the number of rows and n is the number of columns in the grid. +

+
+ +
+
+ Hint 1 +

+ An island is a group of 1's in which every 1 is reachable from any other 1 in that group. Can you think of an algorithm that can find the number of groups by visiting a group only once? Maybe there is a recursive way of doing it. +

+
+ +
+
+ Hint 2 +

+ We can use the Depth First Search (DFS) algorithm to traverse each group by starting at a cell with 1 and recursively visiting all the cells that are reachable from that cell and are also 1. Can you think about how to find the area of that island? How would you implement this? +

+
+ +
+
+ Hint 3 +

+ We traverse the grid, and when we encounter a 1, we initialize a variable area. We then start a DFS from that cell to visit all connected 1's recursively, marking them as 0 to indicate they are visited. At each recursion step, we increment area. After completing the DFS, we update maxArea, which tracks the maximum area of an island in the grid, if maxArea < area. Finally, after traversing the grid, we return maxArea. +

+
\ No newline at end of file diff --git a/hints/pacific-atlantic-water-flow.md b/hints/pacific-atlantic-water-flow.md new file mode 100644 index 000000000..6ed584779 --- /dev/null +++ b/hints/pacific-atlantic-water-flow.md @@ -0,0 +1,31 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution with O(m * n) time and O(m * n) space, where m is the number of rows and n is the number of columns in the grid. +

+
+ +
+
+ Hint 1 +

+ A brute force solution would be to traverse each cell in the grid and run a BFS from each cell to check if it can reach both oceans. This would result in an O((m * n)^2) solution. Can you think of a better way? Maybe you should consider a reverse way of traversing. +

+
+ +
+
+ Hint 2 +

+ We can use the Depth First Search (DFS) algorithm starting from the border cells of the grid. However, we reverse the condition such that the next visiting cell should have a height greater than or equal to the current cell. For the top and left borders connected to the Pacific Ocean, we use a hash set called pacific and run a DFS from each of these cells, visiting nodes recursively. Similarly, for the bottom and right borders connected to the Atlantic Ocean, we use a hash set called atlantic and run a DFS. The required coordinates are the cells that exist in both the pacific and atlantic sets. How do you implement this? +

+
+ +
+
+ Hint 3 +

+ We perform DFS from the border cells, using their respective hash sets. During the DFS, we recursively visit the neighbouring cells that are unvisited and have height greater than or equal to the current cell's height and add the current cell's coordinates to the corresponding hash set. Once the DFS completes, we traverse the grid and check if a cell exists in both the hash sets. If so, we add that cell to the result list. +

+
\ No newline at end of file diff --git a/hints/rotting-fruit.md b/hints/rotting-fruit.md new file mode 100644 index 000000000..76ce34b5b --- /dev/null +++ b/hints/rotting-fruit.md @@ -0,0 +1,31 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution with O(m * n) time and O(m * n) space, where m is the number of rows and n is the number of columns in the given grid. +

+
+ +
+
+ Hint 1 +

+ The DFS algorithm is not suitable for this problem because it explores nodes deeply rather than level by level. In this scenario, we need to determine which oranges rot at each second, which naturally fits a level-by-level traversal. Can you think of an algorithm designed for such a traversal? +

+
+ +
+
+ Hint 2 +

+ We can use the Breadth First Search (BFS) algorithm. At each second, we rot the oranges that are adjacent to the rotten ones. So, we store the rotten oranges in a queue and process them in one go. The time at which a fresh orange gets rotten is the level at which it is visited. How would you implement it? +

+
+ +
+
+ Hint 3 +

+ We traverse the grid and store the rotten oranges in a queue. We then run a BFS, processing the current level of rotten oranges and visiting the adjacent cells of each rotten orange. We only insert the adjacent cell into the queue if it contains a fresh orange. This process continues until the queue is empty. The level at which the BFS stops is the answer. However, we also need to check whether all oranges have rotted by traversing the grid. If any fresh orange is found, we return -1; otherwise, we return the level. +

+
\ No newline at end of file diff --git a/hints/surrounded-regions.md b/hints/surrounded-regions.md new file mode 100644 index 000000000..d61a1db78 --- /dev/null +++ b/hints/surrounded-regions.md @@ -0,0 +1,31 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution with O(m * n) time and O(m * n) space, where m is the number of rows and n is the number of columns in the matrix. +

+
+ +
+
+ Hint 1 +

+ We observe that we need to capture the regions that are not connected to the O's on the border of the matrix. This means there should be no path connecting the O's on the border to any O's in the region. Can you think of a way to check the region connected to these border O's? +

+
+ +
+
+ Hint 2 +

+ We can use the Depth First Search (DFS) algorithm. Instead of checking the region connected to the border O's, we can reverse the approach and mark the regions that are reachable from the border O's. How would you implement this? +

+
+ +
+
+ Hint 3 +

+ We run the DFS from every 'O' on the border of the matrix, visiting the neighboring cells that are equal to 'O' recursively and marking them as '#' to avoid revisiting. After completing all the DFS calls, we traverse the matrix again and capture the cells where matrix[i][j] == 'O', and unmark the cells back to 'O' where matrix[i][j] == '#'. +

+
\ No newline at end of file From dd8c1d1e9b01005522bc076e6344bc843dbf586c Mon Sep 17 00:00:00 2001 From: rey Date: Fri, 29 Nov 2024 03:06:50 -0800 Subject: [PATCH 03/45] clarify log for 2d search (#3755) --- articles/search-2d-matrix.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/articles/search-2d-matrix.md b/articles/search-2d-matrix.md index 21be9f5e9..722cbe2b6 100644 --- a/articles/search-2d-matrix.md +++ b/articles/search-2d-matrix.md @@ -550,7 +550,7 @@ class Solution { ### Time & Space Complexity -* Time complexity: $O(\log m + \log n)$ +* Time complexity: $O(\log m + \log n)$ (which reduces to $O(\log(m * n))$) * Space complexity: $O(1)$ > Where $m$ is the number of rows and $n$ is the number of columns of matrix. From 986f35a352662a0060678bf02c5bd9c2d1b79b69 Mon Sep 17 00:00:00 2001 From: Sri Hari <94112314+Srihari2222@users.noreply.github.com> Date: Sat, 30 Nov 2024 13:14:07 +0530 Subject: [PATCH 04/45] Sri Hari: Batch-3/Neetcode-ALL/Added articles (#3751) * Batch-3/Neetcode-ALL/Added * Batch-3/Neetcode-All/Added --- articles/can-place-flowers.md | 178 ++++++ articles/concatenation-of-array.md | 135 +++++ articles/find-pivot-index.md | 284 ++++++++++ articles/is-anagram.md | 4 +- articles/is-subsequence.md | 513 ++++++++++++++++++ articles/isomorphic-strings.md | 201 +++++++ articles/length-of-last-word.md | 230 ++++++++ articles/longest-common-prefix.md | 503 +++++++++++++++++ articles/majority-element.md | 494 +++++++++++++++++ articles/maximum-number-of-balloons.md | 185 +++++++ articles/next-greater-element-i.md | 334 ++++++++++++ articles/pascals-triangle.md | 260 +++++++++ articles/remove-element.md | 233 ++++++++ ...nts-with-greatest-element-on-right-side.md | 154 ++++++ articles/unique-email-addresses.md | 197 +++++++ 15 files changed, 3903 insertions(+), 2 deletions(-) create mode 100644 articles/can-place-flowers.md create mode 100644 articles/concatenation-of-array.md create mode 100644 articles/find-pivot-index.md create mode 100644 articles/is-subsequence.md create mode 100644 articles/isomorphic-strings.md create mode 100644 articles/length-of-last-word.md create mode 100644 articles/longest-common-prefix.md create mode 100644 articles/majority-element.md create mode 100644 articles/maximum-number-of-balloons.md create mode 100644 articles/next-greater-element-i.md create mode 100644 articles/pascals-triangle.md create mode 100644 articles/remove-element.md create mode 100644 articles/replace-elements-with-greatest-element-on-right-side.md create mode 100644 articles/unique-email-addresses.md diff --git a/articles/can-place-flowers.md b/articles/can-place-flowers.md new file mode 100644 index 000000000..7ccaeba96 --- /dev/null +++ b/articles/can-place-flowers.md @@ -0,0 +1,178 @@ +## 1. Iteration - I + +::tabs-start + +```python +class Solution: + def canPlaceFlowers(self, flowerbed: List[int], n: int) -> bool: + f = [0] + flowerbed + [0] + + for i in range(1, len(f) - 1): + if f[i - 1] == 0 and f[i] == 0 and f[i + 1] == 0: + f[i] = 1 + n -= 1 + + return n <= 0 +``` + +```java +public class Solution { + public boolean canPlaceFlowers(int[] flowerbed, int n) { + int[] f = new int[flowerbed.length + 2]; + for (int i = 0; i < flowerbed.length; i++) { + f[i + 1] = flowerbed[i]; + } + + for (int i = 1; i < f.length - 1; i++) { + if (f[i - 1] == 0 && f[i] == 0 && f[i + 1] == 0) { + f[i] = 1; + n--; + } + } + return n <= 0; + } +} +``` + +```cpp +class Solution { +public: + bool canPlaceFlowers(vector& flowerbed, int n) { + vector f(flowerbed.size() + 2, 0); + for (int i = 0; i < flowerbed.size(); i++) { + f[i + 1] = flowerbed[i]; + } + + for (int i = 1; i < f.size() - 1; i++) { + if (f[i - 1] == 0 && f[i] == 0 && f[i + 1] == 0) { + f[i] = 1; + n--; + } + } + return n <= 0; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} flowerbed + * @param {number} n + * @return {boolean} + */ + canPlaceFlowers(flowerbed, n) { + const f = [0, ...flowerbed, 0]; + + for (let i = 1; i < f.length - 1; i++) { + if (f[i - 1] === 0 && f[i] === 0 && f[i + 1] === 0) { + f[i] = 1; + n--; + } + } + return n <= 0; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Iteration - II + +::tabs-start + +```python +class Solution: + def canPlaceFlowers(self, flowerbed: List[int], n: int) -> bool: + empty = 0 if flowerbed[0] else 1 + + for f in flowerbed: + if f: + n -= int((empty - 1) / 2) + empty = 0 + else: + empty += 1 + + n -= empty // 2 + return n <= 0 +``` + +```java +public class Solution { + public boolean canPlaceFlowers(int[] flowerbed, int n) { + int empty = flowerbed[0] == 0 ? 1 : 0; + + for (int f : flowerbed) { + if (f == 1) { + n -= (empty - 1) / 2; + empty = 0; + } else { + empty++; + } + } + + n -= empty / 2; + return n <= 0; + } +} +``` + +```cpp +class Solution { +public: + bool canPlaceFlowers(vector& flowerbed, int n) { + int empty = flowerbed[0] == 0 ? 1 : 0; + + for (int f : flowerbed) { + if (f == 1) { + n -= (empty - 1) / 2; + empty = 0; + } else { + empty++; + } + } + + n -= empty / 2; + return n <= 0; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} flowerbed + * @param {number} n + * @return {boolean} + */ + canPlaceFlowers(flowerbed, n) { + let empty = flowerbed[0] === 0 ? 1 : 0; + + for (let f of flowerbed) { + if (f === 1) { + n -= Math.floor(Math.max(0, empty - 1) / 2); + empty = 0; + } else { + empty++; + } + } + + n -= Math.floor(empty / 2); + return n <= 0; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/concatenation-of-array.md b/articles/concatenation-of-array.md new file mode 100644 index 000000000..24e788da2 --- /dev/null +++ b/articles/concatenation-of-array.md @@ -0,0 +1,135 @@ +## 1. Iteration (Two Pass) + +::tabs-start + +```python +class Solution: + def getConcatenation(self, nums: List[int]) -> List[int]: + ans = [] + for i in range(2): + for num in nums: + ans.append(num) + return ans +``` + +```java +public class Solution { + public int[] getConcatenation(int[] nums) { + int[] ans=new int[2 * nums.length]; + int idx = 0; + for (int i = 0; i < 2; i++) { + for (int num : nums) { + ans[idx++] = num; + } + } + return ans; + } +} +``` + +```cpp +class Solution { +public: + vector getConcatenation(vector& nums) { + vector ans; + for (int i = 0; i < 2; ++i) { + for (int num : nums) { + ans.push_back(num); + } + } + return ans; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number[]} + */ + getConcatenation(nums) { + let ans = []; + for (let i = 0; i < 2; i++) { + for (let num of nums) { + ans.push(num); + } + } + return ans; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ + +--- + +## 2. Iteration (One Pass) + +::tabs-start + +```python +class Solution: + def getConcatenation(self, nums: List[int]) -> List[int]: + n = len(nums) + ans = [0] * (2 * n) + for i, num in enumerate(nums): + ans[i] = ans[i + n] = num + return ans +``` + +```java +public class Solution { + public int[] getConcatenation(int[] nums) { + int n = nums.length; + int[] ans = new int[2 * n]; + for (int i = 0; i < n; i++) { + ans[i] = ans[i + n] = nums[i]; + } + return ans; + } +} +``` + +```cpp +class Solution { +public: + vector getConcatenation(vector& nums) { + int n = nums.size(); + vector ans(2 * n); + for (int i = 0; i < n; ++i) { + ans[i] = ans[i + n] = nums[i]; + } + return ans; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number[]} + */ + getConcatenation(nums) { + let n = nums.length; + let ans = new Array(2 * n); + for (let i = 0; i < n; i++) { + ans[i] = ans[i + n] = nums[i]; + } + return ans; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/find-pivot-index.md b/articles/find-pivot-index.md new file mode 100644 index 000000000..7e8568d02 --- /dev/null +++ b/articles/find-pivot-index.md @@ -0,0 +1,284 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def pivotIndex(self, nums: List[int]) -> int: + n = len(nums) + for i in range(n): + leftSum = rightSum = 0 + for l in range(i): + leftSum += nums[l] + for r in range(i + 1, n): + rightSum += nums[r] + if leftSum == rightSum: + return i + return -1 +``` + +```java +public class Solution { + public int pivotIndex(int[] nums) { + int n = nums.length; + for (int i = 0; i < n; i++) { + int leftSum = 0, rightSum = 0; + for (int l = 0; l < i; l++) { + leftSum += nums[l]; + } + for (int r = i + 1; r < n; r++) { + rightSum += nums[r]; + } + if (leftSum == rightSum) { + return i; + } + } + return -1; + } +} +``` + +```cpp +class Solution { +public: + int pivotIndex(vector& nums) { + int n = nums.size(); + for (int i = 0; i < n; i++) { + int leftSum = 0, rightSum = 0; + for (int l = 0; l < i; l++) { + leftSum += nums[l]; + } + for (int r = i + 1; r < n; r++) { + rightSum += nums[r]; + } + if (leftSum == rightSum) { + return i; + } + } + return -1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + pivotIndex(nums) { + const n = nums.length; + for (let i = 0; i < n; i++) { + let leftSum = 0, rightSum = 0; + for (let l = 0; l < i; l++) { + leftSum += nums[l]; + } + for (let r = i + 1; r < n; r++) { + rightSum += nums[r]; + } + if (leftSum === rightSum) { + return i; + } + } + return -1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(1)$ + +--- + +## 2. Prefix Sum + +::tabs-start + +```python +class Solution: + def pivotIndex(self, nums: List[int]) -> int: + n = len(nums) + prefixSum = [0] * (n + 1) + for i in range(n): + prefixSum[i + 1] = prefixSum[i] + nums[i] + + for i in range(n): + leftSum = prefixSum[i] + rightSum = prefixSum[n] - prefixSum[i + 1] + if leftSum == rightSum: + return i + return -1 +``` + +```java +public class Solution { + public int pivotIndex(int[] nums) { + int n = nums.length; + int[] prefixSum = new int[n + 1]; + for (int i = 0; i < n; i++) { + prefixSum[i + 1] = prefixSum[i] + nums[i]; + } + + for (int i = 0; i < n; i++) { + int leftSum = prefixSum[i]; + int rightSum = prefixSum[n] - prefixSum[i + 1]; + if (leftSum == rightSum) { + return i; + } + } + return -1; + } +} +``` + +```cpp +class Solution { +public: + int pivotIndex(vector& nums) { + int n = nums.size(); + vector prefixSum(n + 1, 0); + for (int i = 0; i < n; i++) { + prefixSum[i + 1] = prefixSum[i] + nums[i]; + } + + for (int i = 0; i < n; i++) { + int leftSum = prefixSum[i]; + int rightSum = prefixSum[n] - prefixSum[i + 1]; + if (leftSum == rightSum) { + return i; + } + } + return -1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + pivotIndex(nums) { + const n = nums.length; + const prefixSum = new Array(n + 1).fill(0); + for (let i = 0; i < n; i++) { + prefixSum[i + 1] = prefixSum[i] + nums[i]; + } + + for (let i = 0; i < n; i++) { + const leftSum = prefixSum[i]; + const rightSum = prefixSum[n] - prefixSum[i + 1]; + if (leftSum === rightSum) { + return i; + } + } + return -1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Prefix Sum (Optimal) + +::tabs-start + +```python +class Solution: + def pivotIndex(self, nums: List[int]) -> int: + total = sum(nums) + leftSum = 0 + for i in range(len(nums)): + rightSum = total - nums[i] - leftSum + if leftSum == rightSum: + return i + leftSum += nums[i] + return -1 +``` + +```java +public class Solution { + public int pivotIndex(int[] nums) { + int total = 0; + for (int num : nums) { + total += num; + } + + int leftSum = 0; + for (int i = 0; i < nums.length; i++) { + int rightSum = total - leftSum - nums[i]; + if (leftSum == rightSum) { + return i; + } + leftSum += nums[i]; + } + return -1; + } +} +``` + +```cpp +class Solution { +public: + int pivotIndex(vector& nums) { + int total = 0; + for (int num : nums) { + total += num; + } + + int leftSum = 0; + for (int i = 0; i < nums.size(); i++) { + int rightSum = total - leftSum - nums[i]; + if (leftSum == rightSum) { + return i; + } + leftSum += nums[i]; + } + return -1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + pivotIndex(nums) { + let total = 0; + for (let num of nums) { + total += num; + } + + let leftSum = 0; + for (let i = 0; i < nums.length; i++) { + let rightSum = total - leftSum - nums[i]; + if (leftSum === rightSum) { + return i; + } + leftSum += nums[i]; + } + return -1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/is-anagram.md b/articles/is-anagram.md index 45f2dd7d4..ae3a44534 100644 --- a/articles/is-anagram.md +++ b/articles/is-anagram.md @@ -271,7 +271,7 @@ class Solution { ### Time & Space Complexity * Time complexity: $O(n + m)$ -* Space complexity: $O(1)$ since we have at most 26 different characters. +* Space complexity: $O(1)$ since we have at most $26$ different characters. > Where $n$ is the length of string $s$ and $m$ is the length of string $t$. @@ -439,6 +439,6 @@ class Solution { ### Time & Space Complexity * Time complexity: $O(n + m)$ -* Space complexity: $O(1)$ since we have at most 26 different characters. +* Space complexity: $O(1)$ since we have at most $26$ different characters. > Where $n$ is the length of string $s$ and $m$ is the length of string $t$. diff --git a/articles/is-subsequence.md b/articles/is-subsequence.md new file mode 100644 index 000000000..fde7628a6 --- /dev/null +++ b/articles/is-subsequence.md @@ -0,0 +1,513 @@ +## 1. Recursion + +::tabs-start + +```python +class Solution: + def isSubsequence(self, s: str, t: str) -> bool: + def rec(i, j): + if i == len(s): + return True + if j == len(t): + return False + + if s[i] == t[j]: + return rec(i + 1, j + 1) + return rec(i, j + 1) + return rec(0, 0) +``` + +```java +public class Solution { + public boolean isSubsequence(String s, String t) { + return rec(s, t, 0, 0); + } + + private boolean rec(String s, String t, int i, int j) { + if (i == s.length()) return true; + if (j == t.length()) return false; + if (s.charAt(i) == t.charAt(j)) { + return rec(s, t, i + 1, j + 1); + } + return rec(s, t, i, j + 1); + } +} +``` + +```cpp +class Solution { +public: + bool isSubsequence(string s, string t) { + return rec(s, t, 0, 0); + } + +private: + bool rec(string& s, string& t, int i, int j) { + if (i == s.size()) return true; + if (j == t.size()) return false; + if (s[i] == t[j]) { + return rec(s, t, i + 1, j + 1); + } + return rec(s, t, i, j + 1); + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @param {string} t + * @return {boolean} + */ + isSubsequence(s, t) { + const rec = (i, j) => { + if (i === s.length) return true; + if (j === t.length) return false; + if (s[i] === t[j]) return rec(i + 1, j + 1); + return rec(i, j + 1); + }; + return rec(0, 0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * m)$ +* Space complexity: $O(n)$ + +> Where $n$ is the length of the string $s$ and $m$ is the length of the string $t$. + +--- + +## 2. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def isSubsequence(self, s: str, t: str) -> bool: + n, m = len(s), len(t) + memo = [[-1] * m for _ in range(n)] + + def rec(i, j): + if i == n: + return True + if j == m: + return False + if memo[i][j] != -1: + return memo[i][j] == 1 + if s[i] == t[j]: + memo[i][j] = 1 if rec(i + 1, j + 1) else 0 + else: + memo[i][j] = 1 if rec(i, j + 1) else 0 + return memo[i][j] == 1 + + return rec(0, 0) +``` + +```java +public class Solution { + public boolean isSubsequence(String s, String t) { + int n = s.length(), m = t.length(); + int[][] memo = new int[n][m]; + for (int[] row : memo) { + Arrays.fill(row, -1); + } + return rec(s, t, 0, 0, memo); + } + + private boolean rec(String s, String t, int i, int j, int[][] memo) { + if (i == s.length()) return true; + if (j == t.length()) return false; + if (memo[i][j] != -1) return memo[i][j] == 1; + if (s.charAt(i) == t.charAt(j)) { + memo[i][j] = rec(s, t, i + 1, j + 1, memo) ? 1 : 0; + } else { + memo[i][j] = rec(s, t, i, j + 1, memo) ? 1 : 0; + } + return memo[i][j] == 1; + } +} +``` + +```cpp +class Solution { +public: + bool isSubsequence(string s, string t) { + int n = s.size(), m = t.size(); + vector> memo(n, vector(m, -1)); + return rec(s, t, 0, 0, memo); + } + +private: + bool rec(string& s, string& t, int i, int j, vector>& memo) { + if (i == s.size()) return true; + if (j == t.size()) return false; + if (memo[i][j] != -1) return memo[i][j] == 1; + if (s[i] == t[j]) { + memo[i][j] = rec(s, t, i + 1, j + 1, memo) ? 1 : 0; + } else { + memo[i][j] = rec(s, t, i, j + 1, memo) ? 1 : 0; + } + return memo[i][j] == 1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @param {string} t + * @return {boolean} + */ + isSubsequence(s, t) { + const n = s.length, m = t.length; + const memo = Array.from({ length: n }, () => Array(m).fill(-1)); + + const rec = (i, j) => { + if (i === n) return true; + if (j === m) return false; + if (memo[i][j] !== -1) return memo[i][j] === 1; + if (s[i] === t[j]) { + memo[i][j] = rec(i + 1, j + 1) ? 1 : 0; + } else { + memo[i][j] = rec(i, j + 1) ? 1 : 0; + } + return memo[i][j] === 1; + }; + + return rec(0, 0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * m)$ +* Space complexity: $O(n * m)$ + +> Where $n$ is the length of the string $s$ and $m$ is the length of the string $t$. + +--- + +## 3. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def isSubsequence(self, s: str, t: str) -> bool: + n, m = len(s), len(t) + dp = [[False] * (m + 1) for _ in range(n + 1)] + + for j in range(m + 1): + dp[n][j] = True + + for i in range(n - 1, -1, -1): + for j in range(m - 1, -1, -1): + if s[i] == t[j]: + dp[i][j] = dp[i + 1][j + 1] + else: + dp[i][j] = dp[i][j + 1] + + return dp[0][0] +``` + +```java +public class Solution { + public boolean isSubsequence(String s, String t) { + int n = s.length(), m = t.length(); + boolean[][] dp = new boolean[n + 1][m + 1]; + + for (int j = 0; j <= m; j++) { + dp[n][j] = true; + } + + for (int i = n - 1; i >= 0; i--) { + for (int j = m - 1; j >= 0; j--) { + if (s.charAt(i) == t.charAt(j)) { + dp[i][j] = dp[i + 1][j + 1]; + } else { + dp[i][j] = dp[i][j + 1]; + } + } + } + + return dp[0][0]; + } +} +``` + +```cpp +class Solution { +public: + bool isSubsequence(string s, string t) { + int n = s.size(), m = t.size(); + vector> dp(n + 1, vector(m + 1, false)); + + for (int j = 0; j <= m; ++j) { + dp[n][j] = true; + } + + for (int i = n - 1; i >= 0; --i) { + for (int j = m - 1; j >= 0; --j) { + if (s[i] == t[j]) { + dp[i][j] = dp[i + 1][j + 1]; + } else { + dp[i][j] = dp[i][j + 1]; + } + } + } + + return dp[0][0]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @param {string} t + * @return {boolean} + */ + isSubsequence(s, t) { + const n = s.length, m = t.length; + const dp = Array.from({ length: n + 1 }, () => Array(m + 1).fill(false)); + + for (let j = 0; j <= m; j++) { + dp[n][j] = true; + } + + for (let i = n - 1; i >= 0; i--) { + for (let j = m - 1; j >= 0; j--) { + if (s[i] === t[j]) { + dp[i][j] = dp[i + 1][j + 1]; + } else { + dp[i][j] = dp[i][j + 1]; + } + } + } + + return dp[0][0]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * m)$ +* Space complexity: $O(n * m)$ + +> Where $n$ is the length of the string $s$ and $m$ is the length of the string $t$. + +--- + +## 4. Two Pointers + +::tabs-start + +```python +class Solution: + def isSubsequence(self, s: str, t: str) -> bool: + i = j = 0 + while i < len(s) and j < len(t): + if s[i] == t[j]: + i += 1 + j += 1 + return i == len(s) +``` + +```java +public class Solution { + public boolean isSubsequence(String s, String t) { + int i = 0, j = 0; + while (i < s.length() && j < t.length()) { + if (s.charAt(i) == t.charAt(j)) { + i++; + } + j++; + } + return i == s.length(); + } +} +``` + +```cpp +class Solution { +public: + bool isSubsequence(string s, string t) { + int i = 0, j = 0; + while (i < s.length() && j < t.length()) { + if (s[i] == t[j]) { + i++; + } + j++; + } + return i == s.length(); + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @param {string} t + * @return {boolean} + */ + isSubsequence(s, t) { + let i = 0, j = 0; + while (i < s.length && j < t.length) { + if (s.charAt(i) == t.charAt(j)) { + i++; + } + j++; + } + return i == s.length; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n + m)$ +* Space complexity: $O(1)$ + +> Where $n$ is the length of the string $s$ and $m$ is the length of the string $t$. + +## 5. Follow-Up Solution (Index Jumping) + +::tabs-start + +```python +class Solution: + def isSubsequence(self, s: str, t: str) -> bool: + n, m = len(s), len(t) + if m == 0: + return n == 0 + + store = [[m + 1] * 26 for _ in range(m)] + store[m - 1][ord(t[m - 1]) - ord('a')] = m - 1 + + for i in range(m - 2, -1, -1): + store[i] = store[i + 1][:] + store[i][ord(t[i]) - ord('a')] = i + + i, j = 0, 0 + while i < n and j < m: + j = store[j][ord(s[i]) - ord('a')] + 1 + if j > m: + return False + i += 1 + + return i == n +``` + +```java +public class Solution { + public boolean isSubsequence(String s, String t) { + int n = s.length(), m = t.length(); + if (m == 0) return n == 0; + + int[][] store = new int[m][26]; + for (int i = 0; i < m; i++) { + Arrays.fill(store[i], m + 1); + } + + store[m - 1][t.charAt(m - 1) - 'a'] = m - 1; + + for (int i = m - 2; i >= 0; i--) { + store[i] = store[i + 1].clone(); + store[i][t.charAt(i) - 'a'] = i; + } + + int i = 0, j = 0; + while (i < n && j < m) { + j = store[j][s.charAt(i) - 'a'] + 1; + if (j > m) return false; + i++; + } + + return i == n; + } +} +``` + +```cpp +class Solution { +public: + bool isSubsequence(string s, string t) { + int n = s.length(), m = t.length(); + if (m == 0) return n == 0; + + vector> store(m, vector(26, m + 1)); + store[m - 1][t[m - 1] - 'a'] = m - 1; + + for (int i = m - 2; i >= 0; i--) { + store[i] = store[i + 1]; + store[i][t[i] - 'a'] = i; + } + + int i = 0, j = 0; + while (i < n && j < m) { + j = store[j][s[i] - 'a'] + 1; + if (j > m) return false; + i++; + } + + return i == n; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @param {string} t + * @return {boolean} + */ + isSubsequence(s, t) { + const n = s.length, m = t.length; + if (m === 0) return n === 0; + + const store = Array.from({ length: m }, () => Array(26).fill(m + 1)); + store[m - 1][t.charCodeAt(m - 1) - 'a'.charCodeAt(0)] = m - 1; + + for (let i = m - 2; i >= 0; i--) { + store[i] = [...store[i + 1]]; + store[i][t.charCodeAt(i) - 'a'.charCodeAt(0)] = i; + } + + let i = 0, j = 0; + while (i < n && j < m) { + j = store[j][s.charCodeAt(i) - 'a'.charCodeAt(0)] + 1; + if (j > m) return false; + i++; + } + + return i === n; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n + m)$ +* Space complexity: $O(m)$ + +> Where $n$ is the length of the string $s$ and $m$ is the length of the string $t$. \ No newline at end of file diff --git a/articles/isomorphic-strings.md b/articles/isomorphic-strings.md new file mode 100644 index 000000000..afbaa6c10 --- /dev/null +++ b/articles/isomorphic-strings.md @@ -0,0 +1,201 @@ +## 1. Hash Map (Two Pass) + +::tabs-start + +```python +class Solution: + def helper(self, s: str, t: str) -> bool: + mp = {} + for i in range(len(s)): + if (s[i] in mp) and (mp[s[i]] != t[i]): + return False + mp[s[i]] = t[i] + return True + + def isIsomorphic(self, s: str, t: str) -> bool: + return self.helper(s, t) and self.helper(t, s) +``` + +```java +public class Solution { + private boolean helper(String s, String t) { + HashMap map = new HashMap<>(); + for (int i = 0; i < s.length(); i++) { + char sc = s.charAt(i); + char tc = t.charAt(i); + if (map.containsKey(sc) && map.get(sc) != tc) { + return false; + } + map.put(sc, tc); + } + return true; + } + + public boolean isIsomorphic(String s, String t) { + return helper(s, t) && helper(t, s); + } +} +``` + +```cpp +class Solution { +private: + bool helper(const string& s, const string& t) { + unordered_map map; + for (int i = 0; i < s.length(); i++) { + if (map.count(s[i]) && map[s[i]] != t[i]) { + return false; + } + map[s[i]] = t[i]; + } + return true; + } + +public: + bool isIsomorphic(string s, string t) { + return helper(s, t) && helper(t, s); + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @param {string} t + * @return {boolean} + */ + helper(s, t) { + const map = new Map(); + for (let i = 0; i < s.length; i++) { + if (map.has(s[i]) && map.get(s[i]) !== t[i]) { + return false; + } + map.set(s[i], t[i]); + } + return true; + } + + /** + * @param {string} s + * @param {string} t + * @return {boolean} + */ + isIsomorphic(s, t) { + return this.helper(s, t) && this.helper(t, s); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(m)$ + +> Where $n$ is the length of the input string and $m$ is the number of unique characters in the strings. + +--- + +## 2. Hash Map (One Pass) + +::tabs-start + +```python +class Solution: + def isIsomorphic(self, s: str, t: str) -> bool: + mapST, mapTS = {}, {} + + for i in range(len(s)): + c1, c2 = s[i], t[i] + if ((c1 in mapST and mapST[c1] != c2) or + (c2 in mapTS and mapTS[c2] != c1)): + return False + mapST[c1] = c2 + mapTS[c2] = c1 + + return True +``` + +```java +public class Solution { + public boolean isIsomorphic(String s, String t) { + HashMap mapST = new HashMap<>(); + HashMap mapTS = new HashMap<>(); + + for (int i = 0; i < s.length(); i++) { + char c1 = s.charAt(i), c2 = t.charAt(i); + + if ((mapST.containsKey(c1) && mapST.get(c1) != c2) || + (mapTS.containsKey(c2) && mapTS.get(c2) != c1)) { + return false; + } + + mapST.put(c1, c2); + mapTS.put(c2, c1); + } + + return true; + } +} +``` + +```cpp +class Solution { +public: + bool isIsomorphic(string s, string t) { + unordered_map mapST, mapTS; + + for (int i = 0; i < s.size(); i++) { + char c1 = s[i], c2 = t[i]; + + if ((mapST.count(c1) && mapST[c1] != c2) || + (mapTS.count(c2) && mapTS[c2] != c1)) { + return false; + } + + mapST[c1] = c2; + mapTS[c2] = c1; + } + + return true; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @param {string} t + * @return {boolean} + */ + isIsomorphic(s, t) { + const mapTS = new Map(); + + for (let i = 0; i < s.length; i++) { + const c1 = s[i], c2 = t[i]; + + if ((mapST.has(c1) && mapST.get(c1) !== c2) || + (mapTS.has(c2) && mapTS.get(c2) !== c1)) { + return false; + } + + mapST.set(c1, c2); + mapTS.set(c2, c1); + } + + return true; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(m)$ + +> Where $n$ is the length of the input string and $m$ is the number of unique characters in the strings. \ No newline at end of file diff --git a/articles/length-of-last-word.md b/articles/length-of-last-word.md new file mode 100644 index 000000000..baa94e557 --- /dev/null +++ b/articles/length-of-last-word.md @@ -0,0 +1,230 @@ +## 1. Iteration - I + +::tabs-start + +```python +class Solution: + def lengthOfLastWord(self, s: str) -> int: + length = i = 0 + while i < len(s): + if s[i] == ' ': + while i < len(s) and s[i] == ' ': + i += 1 + if i == len(s): + return length + length = 0 + else: + length += 1 + i += 1 + return length +``` + +```java +public class Solution { + public int lengthOfLastWord(String s) { + int length = 0, i = 0; + while (i < s.length()) { + if (s.charAt(i) == ' ') { + while (i < s.length() && s.charAt(i) == ' ') { + i++; + } + if (i == s.length()) { + return length; + } + length = 0; + } else { + length++; + i++; + } + } + return length; + } +} +``` + +```cpp +class Solution { +public: + int lengthOfLastWord(string s) { + int length = 0, i = 0; + while (i < s.length()) { + if (s[i] == ' ') { + while (i < s.length() && s[i] == ' ') { + i++; + } + if (i == s.length()) { + return length; + } + length = 0; + } else { + length++; + i++; + } + } + return length; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {number} + */ + lengthOfLastWord(s) { + let length = 0, i = 0; + while (i < s.length) { + if (s[i] === ' ') { + while (i < s.length && s[i] === ' ') { + i++; + } + if (i === s.length) { + return length; + } + length = 0; + } else { + length++; + i++; + } + } + return length; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ + +--- + +## 2. Iteration - II + +::tabs-start + +```python +class Solution: + def lengthOfLastWord(self, s: str) -> int: + i, length = len(s) - 1, 0 + while s[i] == ' ': + i -= 1 + while i >= 0 and s[i] != ' ': + i -= 1 + length += 1 + return length +``` + +```java +public class Solution { + public int lengthOfLastWord(String s) { + int n = s.length(); + int i = n - 1, length = 0; + while (s.charAt(i) == ' ') { + i--; + } + while (i >= 0 && s.charAt(i) != ' ') { + i--; + length++; + } + return length; + } +} +``` + +```cpp +class Solution { +public: + int lengthOfLastWord(string s) { + int n = s.length(); + int i = n - 1, length = 0; + while (s[i] == ' ') i--; + while (i >= 0 && s[i] != ' ') { + i--; + length++; + } + return length; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {number} + */ + lengthOfLastWord(s) { + let n = s.length; + let i = n - 1, length = 0; + while (s.charAt(i) === ' ') { + i--; + } + while (i >= 0 && s.charAt(i) !== ' ') { + i--; + length++; + } + return length; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ + +--- + +## 3. Built-In Function + +::tabs-start + +```python +class Solution: + def lengthOfLastWord(self, s: str) -> int: + return len(s.split().pop()) +``` + +```java +public class Solution { + public int lengthOfLastWord(String s) { + s = s.trim(); + return s.length() - s.lastIndexOf(" ") - 1; + } +} +``` + +```cpp +class Solution { +public: + int lengthOfLastWord(string s) { + s.erase(s.find_last_not_of(' ') + 1); + return s.substr(s.find_last_of(' ') + 1).length(); + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {number} + */ + lengthOfLastWord(s) { + return s.trim().split(' ').pop().length + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/longest-common-prefix.md b/articles/longest-common-prefix.md new file mode 100644 index 000000000..c7f5658fb --- /dev/null +++ b/articles/longest-common-prefix.md @@ -0,0 +1,503 @@ +## 1. Iteration - I + +::tabs-start + +```python +class Solution: + def longestCommonPrefix(self, strs: List[str]) -> str: + prefix = strs[0] + for i in range(1, len(strs)): + j = 0 + while j < min(len(prefix), len(strs[i])): + if prefix[j] != strs[i][j]: + break + j += 1 + prefix = prefix[:j] + return prefix +``` + +```java +public class Solution { + public String longestCommonPrefix(String[] strs) { + String prefix = strs[0]; + for (int i = 1; i < strs.length; i++) { + int j = 0; + while (j < Math.min(prefix.length(), strs[i].length())) { + if (prefix.charAt(j) != strs[i].charAt(j)) { + break; + } + j++; + } + prefix = prefix.substring(0, j); + } + return prefix; + } +} +``` + +```cpp +class Solution { +public: + string longestCommonPrefix(vector& strs) { + string prefix = strs[0]; + for (int i = 1; i < strs.size(); i++) { + int j = 0; + while (j < min(prefix.length(), strs[i].length())) { + if (prefix[j] != strs[i][j]) { + break; + } + j++; + } + prefix = prefix.substr(0, j); + } + return prefix; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} strs + * @return {string} + */ + longestCommonPrefix(strs) { + let prefix = strs[0]; + for (let i = 1; i < strs.length; i++) { + let j = 0; + while (j < Math.min(prefix.length, strs[i].length)) { + if (prefix[j] !== strs[i][j]) { + break; + } + j++; + } + prefix = prefix.slice(0, j); + } + return prefix; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * m)$ +* Space complexity: $O(n)$ + +> Where $n$ is the length of the shortest string and $m$ is the number of strings. + +--- + +## 2. Iteration - II + +::tabs-start + +```python +class Solution: + def longestCommonPrefix(self, strs: List[str]) -> str: + for i in range(len(strs[0])): + for s in strs: + if i == len(s) or s[i] != strs[0][i]: + return s[:i] + return strs[0] +``` + +```java +public class Solution { + public String longestCommonPrefix(String[] strs) { + for (int i = 0; i < strs[0].length(); i++) { + for (String s : strs) { + if (i == s.length() || s.charAt(i) != strs[0].charAt(i)) { + return s.substring(0, i); + } + } + } + return strs[0]; + } +} +``` + +```cpp +class Solution { +public: + string longestCommonPrefix(vector& strs) { + for (int i = 0; i < strs[0].length(); i++) { + for (const string& s : strs) { + if (i == s.length() || s[i] != strs[0][i]) { + return s.substr(0, i); + } + } + } + return strs[0]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} strs + * @return {string} + */ + longestCommonPrefix(strs) { + for (let i = 0; i < strs[0].length; i++) { + for (let s of strs) { + if (i === s.length || s[i] !== strs[0][i]) { + return s.slice(0, i); + } + } + } + return strs[0]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * m)$ +* Space complexity: $O(1)$ since we did not use extra space. + +> Where $n$ is the length of the shortest string and $m$ is the number of strings. + +--- + +## 3. Sorting + +::tabs-start + +```python +class Solution: + def longestCommonPrefix(self, strs: List[str]) -> str: + if len(strs) == 1: + return strs[0] + + strs = sorted(strs) + for i in range(min(len(strs[0]), len(strs[-1]))): + if strs[0][i] != strs[-1][i]: + return strs[0][:i] + return strs[0] +``` + +```java +public class Solution { + public String longestCommonPrefix(String[] strs) { + if (strs.length == 1) { + return strs[0]; + } + + Arrays.sort(strs); + int N = Math.min(strs[0].length(), strs[strs.length - 1].length()); + for (int i = 0; i < N; i++) { + if (strs[0].charAt(i) != strs[strs.length - 1].charAt(i)) { + return strs[0].substring(0, i); + } + } + return strs[0]; + } +} +``` + +```cpp +class Solution { +public: + string longestCommonPrefix(vector& strs) { + if (strs.size() == 1) { + return strs[0]; + } + + sort(strs.begin(), strs.end()); + for (int i = 0; i < min(strs[0].length(), strs.back().length()); i++) { + if (strs[0][i] != strs.back()[i]) { + return strs[0].substr(0, i); + } + } + return strs[0]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} strs + * @return {string} + */ + longestCommonPrefix(strs) { + if (strs.length === 1) { + return strs[0]; + } + + strs.sort(); + let N = Math.min(strs[0].length, strs[strs.length - 1].length); + for (let i = 0; i < N; i++) { + if (strs[0][i] !== strs[strs.length - 1][i]) { + return strs[0].slice(0, i); + } + } + return strs[0]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * m \log m)$ +* Space complexity: $O(1)$ or $O(m)$ depending on the sorting algorithm. + +> Where $n$ is the length of the longest string and $m$ is the number of strings. + +--- + +## 4. Trie + +::tabs-start + +```python +class TrieNode: + def __init__(self): + self.children = {} + +class Trie: + def __init__(self): + self.root = TrieNode() + + def insert(self, word: str) -> None: + node = self.root + for char in word: + if char not in node.children: + node.children[char] = TrieNode() + node = node.children[char] + + def lcp(self, word: str, prefixLen: int) -> int: + node = self.root + for i in range(min(len(word), prefixLen)): + if word[i] not in node.children: + return i + node = node.children[word[i]] + return min(len(word), prefixLen) + +class Solution: + def longestCommonPrefix(self, strs: list[str]) -> str: + if len(strs) == 1: + return strs[0] + + mini = 0 + for i in range(1, len(strs)): + if len(strs[mini]) > len(strs[i]): + mini = i + + trie = Trie() + trie.insert(strs[mini]) + prefixLen = len(strs[mini]) + for i in range(len(strs)): + prefixLen = trie.lcp(strs[i], prefixLen) + return strs[0][:prefixLen] +``` + +```java +class TrieNode { + Map children = new HashMap<>(); +} + +class Trie { + TrieNode root = new TrieNode(); + + void insert(String word) { + TrieNode node = root; + for (char c : word.toCharArray()) { + node.children.putIfAbsent(c, new TrieNode()); + node = node.children.get(c); + } + } + + int lcp(String word, int prefixLen) { + TrieNode node = root; + int i = 0; + while (i < Math.min(word.length(), prefixLen)) { + if (!node.children.containsKey(word.charAt(i))) { + return i; + } + node = node.children.get(word.charAt(i)); + i++; + } + return Math.min(word.length(), prefixLen); + } +} + +public class Solution { + public String longestCommonPrefix(String[] strs) { + if (strs.length == 1) { + return strs[0]; + } + + int mini = 0; + for (int i = 1; i < strs.length; i++) { + if (strs[mini].length() > strs[i].length()) { + mini = i; + } + } + + Trie trie = new Trie(); + trie.insert(strs[mini]); + int prefixLen = strs[mini].length(); + + for (int i = 0; i < strs.length; i++) { + prefixLen = trie.lcp(strs[i], prefixLen); + } + + return strs[0].substring(0, prefixLen); + } +} +``` + +```cpp +class TrieNode { +public: + unordered_map children; +}; + +class Trie { +public: + TrieNode* root; + Trie() { + root = new TrieNode(); + } + + void insert(const string& word) { + TrieNode* node = root; + for (char c : word) { + if (node->children.find(c) == node->children.end()) { + node->children[c] = new TrieNode(); + } + node = node->children[c]; + } + } + + int lcp(const string& word, int prefixLen) { + TrieNode* node = root; + int i = 0; + while (i < min((int)word.length(), prefixLen)) { + if (node->children.find(word[i]) == node->children.end()) { + return i; + } + node = node->children[word[i]]; + i++; + } + return min((int)word.length(), prefixLen); + } +}; + +class Solution { +public: + string longestCommonPrefix(vector& strs) { + if (strs.size() == 1) { + return strs[0]; + } + int mini = 0; + for (int i = 1; i < strs.size(); i++) { + if (strs[mini].size() > strs[i].size()) { + mini = i; + } + } + + Trie trie; + trie.insert(strs[mini]); + int prefixLen = strs[mini].length(); + + for (int i = 0; i < strs.size(); i++) { + prefixLen = trie.lcp(strs[i], prefixLen); + } + + return strs[0].substr(0, prefixLen); + } +}; +``` + +```javascript +class TrieNode { + constructor() { + this.children = {}; + } +} + +class Trie { + constructor() { + this.root = new TrieNode(); + } + + /** + * @param {string} word + * @return {void} + */ + insert(word) { + let node = this.root; + for (let char of word) { + if (!node.children[char]) { + node.children[char] = new TrieNode(); + } + node = node.children[char]; + } + } + + /** + * @param {string} word + * @param {number} prefixLen + * @return {number} + */ + lcp(word, prefixLen) { + let node = this.root; + let i = 0; + while (i < Math.min(word.length, prefixLen)) { + if (!node.children[word[i]]) { + return i; + } + node = node.children[word[i]]; + i++; + } + return Math.min(word.length, prefixLen); + } +} + +class Solution { + /** + * @param {string[]} strs + * @return {string} + */ + longestCommonPrefix(strs) { + if (strs.length === 1) { + return strs[0]; + } + + let mini = 0; + for (let i = 1; i < strs.length; i++) { + if (strs[mini].length > strs[i].length) { + mini = i; + } + } + + const trie = new Trie(); + trie.insert(strs[mini]); + let prefixLen = strs[mini].length; + + for (let i = 0; i < strs.length; i++) { + prefixLen = trie.lcp(strs[i], prefixLen); + } + + return strs[0].substring(0, prefixLen); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * m)$ +* Space complexity: $O(n)$ + +> Where $n$ is the length of the shortest string and $m$ is the number of strings. \ No newline at end of file diff --git a/articles/majority-element.md b/articles/majority-element.md new file mode 100644 index 000000000..9ac32df2c --- /dev/null +++ b/articles/majority-element.md @@ -0,0 +1,494 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def majorityElement(self, nums: List[int]) -> int: + n = len(nums) + for num in nums: + count = sum(1 for i in nums if i == num) + if count > n // 2: + return num +``` + +```java +public class Solution { + public int majorityElement(int[] nums) { + int n = nums.length; + for (int num : nums) { + int count = 0; + for (int i : nums) { + if (i == num) { + count++; + } + } + if (count > n / 2) { + return num; + } + } + return -1; + } +} +``` + +```cpp +class Solution { +public: + int majorityElement(vector& nums) { + int n = nums.size(); + for (int num : nums) { + int count = 0; + for (int i : nums) { + if (i == num) { + count++; + } + } + if (count > n / 2) { + return num; + } + } + return -1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + majorityElement(nums) { + let n = nums.length; + for (let num of nums) { + let count = nums.reduce((acc, val) => acc + (val === num ? 1 : 0), 0); + if (count > Math.floor(n / 2)) { + return num; + } + } + return -1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(1)$ + +--- + +## 2. Hash Map + +::tabs-start + +```python +class Solution: + def majorityElement(self, nums: List[int]) -> int: + count = defaultdict(int) + res = maxCount = 0 + + for num in nums: + count[num] += 1 + if maxCount < count[num]: + res = num + maxCount = count[num] + return res +``` + +```java +public class Solution { + public int majorityElement(int[] nums) { + HashMap count = new HashMap<>(); + int res = 0, maxCount = 0; + + for (int num : nums) { + count.put(num, count.getOrDefault(num, 0) + 1); + if (count.get(num) > maxCount) { + res = num; + maxCount = count.get(num); + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int majorityElement(vector& nums) { + unordered_map count; + int res = 0, maxCount = 0; + + for (int num : nums) { + count[num]++; + if (count[num] > maxCount) { + res = num; + maxCount = count[num]; + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + majorityElement(nums) { + const count = new Map(); + let res = 0, maxCount = 0; + + for (let num of nums) { + count.set(num, (count.get(num) || 0) + 1); + if (count.get(num) > maxCount) { + res = num; + maxCount = count.get(num); + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Sorting + +::tabs-start + +```python +class Solution: + def majorityElement(self, nums: List[int]) -> int: + nums.sort() + return nums[len(nums) // 2] +``` + +```java +public class Solution { + public int majorityElement(int[] nums) { + Arrays.sort(nums); + return nums[nums.length / 2]; + } +} +``` + +```cpp +class Solution { +public: + int majorityElement(vector& nums) { + sort(nums.begin(), nums.end()); + return nums[nums.size() / 2]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + majorityElement(nums) { + nums.sort(); + return nums[Math.floor(nums.length / 2)]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(1)$ or $O(n)$ depending on the sorting algorithm. + +--- + +## 4. Bit Manipulation + +::tabs-start + +```python +class Solution: + def majorityElement(self, nums: List[int]) -> int: + n = len(nums) + bit = [0] * 32 + for num in nums: + for i in range(32): + bit[i] += ((num >> i) & 1) + + res = 0 + for i in range(32): + if bit[i] > (n // 2): + if i == 31: + res -= (1 << i) + else: + res |= (1 << i) + return res +``` + +```java +public class Solution { + public int majorityElement(int[] nums) { + int n = nums.length; + int[] bit = new int[32]; + for (int num : nums) { + for (int i = 0; i < 32; i++) { + bit[i] += (num >> i) & 1; + } + } + + int res = 0; + for (int i = 0; i < 32; i++) { + if (bit[i] > n / 2) { + res |= (1 << i); + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int majorityElement(vector& nums) { + int n = nums.size(); + vector bit(32, 0); + for (int num : nums) { + for (int i = 0; i < 32; i++) { + bit[i] += (num >> i) & 1; + } + } + + int res = 0; + for (int i = 0; i < 32; i++) { + if (bit[i] > n / 2) { + res |= (1 << i); + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + majorityElement(nums) { + const n = nums.length; + const bit = Array(32).fill(0); + for (let num of nums) { + for (let i = 0; i < 32; i++) { + bit[i] += (num >> i) & 1; + } + } + + let res = 0; + for (let i = 0; i < 32; i++) { + if (bit[i] > Math.floor(n / 2)) { + res |= (1 << i); + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * 32)$ +* Space complexity: $O(32)$ + +> $32$ represents the number of bits as the given numbers are integers. + +--- + +## 5. Boyer-Moore Voting Algorithm + +::tabs-start + +```python +class Solution: + def majorityElement(self, nums): + res = count = 0 + + for num in nums: + if count == 0: + res = num + count += (1 if num == res else -1) + return res +``` + +```java +public class Solution { + public int majorityElement(int[] nums) { + int res = 0, count = 0; + + for (int num : nums) { + if (count == 0) { + res = num; + } + count += (num == res) ? 1 : -1; + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int majorityElement(vector& nums) { + int res = 0, count = 0; + + for (int num : nums) { + if (count == 0) { + res = num; + } + count += (num == res) ? 1 : -1; + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + majorityElement(nums) { + let res = 0, count = 0; + + for (let num of nums) { + if (count === 0) { + res = num; + } + count += (num === res) ? 1 : -1; + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ + +--- + +## 6. Randomization + +::tabs-start + +```python +class Solution: + def majorityElement(self, nums): + n = len(nums) + while True: + candidate = random.choice(nums) + if nums.count(candidate) > n // 2: + return candidate +``` + +```java +public class Solution { + public int majorityElement(int[] nums) { + Random rand = new Random(); + int n = nums.length; + + while (true) { + int candidate = nums[rand.nextInt(n)]; + int count = 0; + for (int num : nums) { + if (num == candidate) { + count++; + } + } + if (count > n / 2) { + return candidate; + } + } + } +} +``` + +```cpp +class Solution { +public: + int majorityElement(vector& nums) { + int n = nums.size(); + + while (true) { + int candidate = nums[rand() % n]; + int count = 0; + for (int num : nums) { + if (num == candidate) { + count++; + } + } + if (count > n / 2) { + return candidate; + } + } + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + majorityElement(nums) { + const n = nums.length; + while (true) { + const candidate = nums[Math.floor(Math.random() * n)]; + let count = 0; + for (const num of nums) { + if (num === candidate) { + count++; + } + } + if (count > Math.floor(n / 2)) { + return candidate; + } + } + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ + +> The probability of randomly choosing the majority element is greater than $50\%$, so the expected number of iterations in the outer while loop is constant. \ No newline at end of file diff --git a/articles/maximum-number-of-balloons.md b/articles/maximum-number-of-balloons.md new file mode 100644 index 000000000..597cbf295 --- /dev/null +++ b/articles/maximum-number-of-balloons.md @@ -0,0 +1,185 @@ +## 1. Hash Map - I + +::tabs-start + +```python +class Solution: + def maxNumberOfBalloons(self, text: str) -> int: + countText = Counter(text) + balloon = Counter("balloon") + + res = len(text) + for c in balloon: + res = min(res, countText[c] // balloon[c]) + return res +``` + +```java +public class Solution { + public int maxNumberOfBalloons(String text) { + Map countText = new HashMap<>(); + for (char c : text.toCharArray()) { + countText.put(c, countText.getOrDefault(c, 0) + 1); + } + + Map balloon = new HashMap<>(); + for (char c : "balloon".toCharArray()) { + balloon.put(c, balloon.getOrDefault(c, 0) + 1); + } + + int res = text.length(); + for (char c : balloon.keySet()) { + res = Math.min(res, countText.getOrDefault(c, 0) / balloon.get(c)); + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int maxNumberOfBalloons(string text) { + unordered_map countText; + for (char c : text) { + countText[c]++; + } + + unordered_map balloon = {{'b', 1}, {'a', 1}, + {'l', 2}, {'o', 2}, {'n', 1}}; + + int res = text.length(); + for (auto& entry : balloon) { + res = min(res, countText[entry.first] / entry.second); + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} text + * @return {number} + */ + maxNumberOfBalloons(text) { + const countText = {}; + for (const c of text) { + countText[c] = (countText[c] || 0) + 1; + } + + const balloon = { b: 1, a: 1, l: 2, o: 2, n: 1 }; + + let res = text.length; + for (const c in balloon) { + res = Math.min(res, Math.floor((countText[c] || 0) / balloon[c])); + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ since we have at most $26$ different characters. + +--- + +## 2. Hash Map - II + +::tabs-start + +```python +class Solution: + def maxNumberOfBalloons(self, text: str) -> int: + mp = defaultdict(int) + for c in text: + if c in "balon": + mp[c] += 1 + + if len(mp) < 5: + return 0 + + mp['l'] //= 2 + mp['o'] //= 2 + return min(mp.values()) +``` + +```java +public class Solution { + public int maxNumberOfBalloons(String text) { + Map mp = new HashMap<>(); + for (char c : text.toCharArray()) { + if ("balon".indexOf(c) != -1) { + mp.put(c, mp.getOrDefault(c, 0) + 1); + } + } + + if (mp.size() < 5) { + return 0; + } + + mp.put('l', mp.get('l') / 2); + mp.put('o', mp.get('o') / 2); + return Collections.min(mp.values()); + } +} +``` + +```cpp +class Solution { +public: + int maxNumberOfBalloons(string text) { + unordered_map mp; + for (char c : text) { + if (string("balon").find(c) != string::npos) { + mp[c]++; + } + } + + if (mp.size() < 5) { + return 0; + } + + mp['l'] /= 2; + mp['o'] /= 2; + return min({mp['b'], mp['a'], mp['l'], mp['o'], mp['n']}); + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} text + * @return {number} + */ + maxNumberOfBalloons(text) { + const mp = new Map(); + for (let c of text) { + if ("balon".includes(c)) { + mp.set(c, (mp.get(c) || 0) + 1); + } + } + + if (mp.size < 5) { + return 0; + } + + mp.set('l', Math.floor(mp.get('l') / 2)); + mp.set('o', Math.floor(mp.get('o') / 2)); + return Math.min(...Array.from(mp.values())); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ since $balloon$ has $5$ different characters. \ No newline at end of file diff --git a/articles/next-greater-element-i.md b/articles/next-greater-element-i.md new file mode 100644 index 000000000..b81ca977a --- /dev/null +++ b/articles/next-greater-element-i.md @@ -0,0 +1,334 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def nextGreaterElement(self, nums1: List[int], nums2: List[int]) -> List[int]: + n = len(nums2) + res = [] + for num in nums1: + nextGreater = -1 + for i in range(n - 1, -1, -1): + if nums2[i] > num: + nextGreater = nums2[i] + elif nums2[i] == num: + break + res.append(nextGreater) + return res +``` + +```java +public class Solution { + public int[] nextGreaterElement(int[] nums1, int[] nums2) { + int n = nums2.length; + int[] res = new int[nums1.length]; + for (int i = 0; i < nums1.length; i++) { + int nextGreater = -1; + for (int j = n - 1; j >= 0; j--) { + if (nums2[j] > nums1[i]) { + nextGreater = nums2[j]; + } else if (nums2[j] == nums1[i]) { + break; + } + } + res[i] = nextGreater; + } + return res; + } +} +``` + +```cpp +class Solution { +public: + vector nextGreaterElement(vector& nums1, vector& nums2) { + int n = nums2.size(); + vector res; + for (int num : nums1) { + int nextGreater = -1; + for (int i = n - 1; i >= 0; i--) { + if (nums2[i] > num) { + nextGreater = nums2[i]; + } else if (nums2[i] == num) { + break; + } + } + res.push_back(nextGreater); + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums1 + * @param {number[]} nums2 + * @return {number[]} + */ + nextGreaterElement(nums1, nums2) { + const n = nums2.length; + const res = []; + for (const num of nums1) { + let nextGreater = -1; + for (let i = n - 1; i >= 0; i--) { + if (nums2[i] > num) { + nextGreater = nums2[i]; + } else if (nums2[i] === num) { + break; + } + } + res.push(nextGreater); + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n)$ +* Space complexity: $O(1)$ + +> Where $m$ is the size of the array $nums1$ and $n$ is the size of the array $nums2$. + +--- + +## 2. Hash Map + +::tabs-start + +```python +class Solution: + def nextGreaterElement(self, nums1: List[int], nums2: List[int]) -> List[int]: + nums1Idx = {num : i for i, num in enumerate(nums1)} + res = [-1] * len(nums1) + + for i in range(len(nums2)): + if nums2[i] not in nums1Idx: + continue + for j in range(i + 1, len(nums2)): + if nums2[j] > nums2[i]: + idx = nums1Idx[nums2[i]] + res[idx] = nums2[j] + break + return res +``` + +```java +public class Solution { + public int[] nextGreaterElement(int[] nums1, int[] nums2) { + HashMap nums1Idx = new HashMap<>(); + for (int i = 0; i < nums1.length; i++) { + nums1Idx.put(nums1[i], i); + } + + int[] res = new int[nums1.length]; + Arrays.fill(res, -1); + + for (int i = 0; i < nums2.length; i++) { + if (!nums1Idx.containsKey(nums2[i])) { + continue; + } + for (int j = i + 1; j < nums2.length; j++) { + if (nums2[j] > nums2[i]) { + int idx = nums1Idx.get(nums2[i]); + res[idx] = nums2[j]; + break; + } + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + vector nextGreaterElement(vector& nums1, vector& nums2) { + unordered_map nums1Idx; + for (int i = 0; i < nums1.size(); i++) { + nums1Idx[nums1[i]] = i; + } + + vector res(nums1.size(), -1); + + for (int i = 0; i < nums2.size(); i++) { + if (nums1Idx.find(nums2[i]) == nums1Idx.end()) { + continue; + } + for (int j = i + 1; j < nums2.size(); j++) { + if (nums2[j] > nums2[i]) { + int idx = nums1Idx[nums2[i]]; + res[idx] = nums2[j]; + break; + } + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums1 + * @param {number[]} nums2 + * @return {number[]} + */ + nextGreaterElement(nums1, nums2) { + const nums1Idx = new Map(); + nums1.forEach((num, i) => nums1Idx.set(num, i)); + + const res = new Array(nums1.length).fill(-1); + + for (let i = 0; i < nums2.length; i++) { + if (!nums1Idx.has(nums2[i])) { + continue; + } + for (let j = i + 1; j < nums2.length; j++) { + if (nums2[j] > nums2[i]) { + const idx = nums1Idx.get(nums2[i]); + res[idx] = nums2[j]; + break; + } + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n)$ +* Space complexity: $O(m)$ + +> Where $m$ is the size of the array $nums1$ and $n$ is the size of the array $nums2$. + +--- + +## 3. Stack + +::tabs-start + +```python +class Solution: + def nextGreaterElement(self, nums1: List[int], nums2: List[int]) -> List[int]: + nums1Idx = {num : i for i, num in enumerate(nums1)} + res = [-1] * len(nums1) + + stack = [] + for i in range(len(nums2)): + cur = nums2[i] + while stack and cur > stack[-1]: + val = stack.pop() + idx = nums1Idx[val] + res[idx] = cur + if cur in nums1Idx: + stack.append(cur) + return res +``` + +```java +public class Solution { + public int[] nextGreaterElement(int[] nums1, int[] nums2) { + HashMap nums1Idx = new HashMap<>(); + for (int i = 0; i < nums1.length; i++) { + nums1Idx.put(nums1[i], i); + } + + int[] res = new int[nums1.length]; + for (int i = 0; i < res.length; i++) { + res[i] = -1; + } + + Stack stack = new Stack<>(); + for (int num : nums2) { + while (!stack.isEmpty() && num > stack.peek()) { + int val = stack.pop(); + int idx = nums1Idx.get(val); + res[idx] = num; + } + if (nums1Idx.containsKey(num)) { + stack.push(num); + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + vector nextGreaterElement(vector& nums1, vector& nums2) { + unordered_map nums1Idx; + for (int i = 0; i < nums1.size(); i++) { + nums1Idx[nums1[i]] = i; + } + + vector res(nums1.size(), -1); + stack stack; + + for (int num : nums2) { + while (!stack.empty() && num > stack.top()) { + int val = stack.top(); + stack.pop(); + int idx = nums1Idx[val]; + res[idx] = num; + } + if (nums1Idx.find(num) != nums1Idx.end()) { + stack.push(num); + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums1 + * @param {number[]} nums2 + * @return {number[]} + */ + nextGreaterElement(nums1, nums2) { + const nums1Idx = new Map(); + nums1.forEach((num, i) => nums1Idx.set(num, i)); + + const res = new Array(nums1.length).fill(-1); + const stack = []; + + for (let num of nums2) { + while (stack.length && num > stack[stack.length - 1]) { + const val = stack.pop(); + const idx = nums1Idx.get(val); + res[idx] = num; + } + if (nums1Idx.has(num)) { + stack.push(num); + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m + n)$ +* Space complexity: $O(m)$ + +> Where $m$ is the size of the array $nums1$ and $n$ is the size of the array $nums2$. \ No newline at end of file diff --git a/articles/pascals-triangle.md b/articles/pascals-triangle.md new file mode 100644 index 000000000..a3434d283 --- /dev/null +++ b/articles/pascals-triangle.md @@ -0,0 +1,260 @@ +## 1. Combinatorics + +::tabs-start + +```python +class Solution: + def generate(self, numRows: int) -> List[List[int]]: + res = [] + for n in range(numRows): + row = [1] + val = 1 + for k in range(1, n + 1): + val = val * (n - k + 1) // k + row.append(val) + res.append(row) + return res +``` + +```java +public class Solution { + public List> generate(int numRows) { + List> res = new ArrayList<>(); + for (int n = 0; n < numRows; n++) { + List row = new ArrayList<>(); + row.add(1); + int val = 1; + for (int k = 1; k <= n; k++) { + val = val * (n - k + 1) / k; + row.add(val); + } + res.add(row); + } + return res; + } +} +``` + +```cpp +class Solution { +public: + vector> generate(int numRows) { + vector> res; + for (int n = 0; n < numRows; n++) { + vector row; + row.push_back(1); + int val = 1; + for (int k = 1; k <= n; k++) { + val = val * (n - k + 1) / k; + row.push_back(val); + } + res.push_back(row); + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} numRows + * @return {number[][]} + */ + generate(numRows) { + let res = []; + for (let n = 0; n < numRows; n++) { + let row = [1]; + let val = 1; + for (let k = 1; k <= n; k++) { + val = (val * (n - k + 1)) / k; + row.push(val); + } + res.push(row); + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(1)$ + +--- + +## 2. Dynamic Programming - I + +::tabs-start + +```python +class Solution: + def generate(self, numRows: int) -> List[List[int]]: + res = [[1]] + + for i in range(numRows - 1): + temp = [0] + res[-1] + [0] + row = [] + for j in range(len(res[-1]) + 1): + row.append(temp[j] + temp[j + 1]) + res.append(row) + return res +``` + +```java +public class Solution { + public List> generate(int numRows) { + List> res = new ArrayList<>(); + res.add(new ArrayList<>()); + res.get(0).add(1); + + for (int i = 1; i < numRows; i++) { + List temp = new ArrayList<>(res.get(i - 1)); + temp.add(0, 0); + temp.add(0); + List row = new ArrayList<>(); + + for (int j = 0; j < res.get(i - 1).size() + 1; j++) { + row.add(temp.get(j) + temp.get(j + 1)); + } + + res.add(row); + } + return res; + } +} +``` + +```cpp +class Solution { +public: + vector> generate(int numRows) { + vector> res = {{1}}; + + for (int i = 0; i < numRows - 1; i++) { + vector temp = {0}; + temp.insert(temp.end(), res.back().begin(), res.back().end()); + temp.push_back(0); + vector row; + for (size_t j = 0; j < res.back().size() + 1; j++) { + row.push_back(temp[j] + temp[j + 1]); + } + res.push_back(row); + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} numRows + * @return {number[][]} + */ + generate(numRows) { + let res = [[1]]; + + for (let i = 0; i < numRows - 1; i++) { + let temp = [0, ...res[res.length - 1], 0]; + let row = []; + for (let j = 0; j < res[res.length - 1].length + 1; j++) { + row.push(temp[j] + temp[j + 1]); + } + res.push(row); + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n)$ + +--- + +## 3. Dynamic Programming - II + +::tabs-start + +```python +class Solution: + def generate(self, numRows: int) -> List[List[int]]: + res = [[1] * (i + 1) for i in range(numRows)] + for i in range(2, numRows): + for j in range(1, i): + res[i][j] = res[i - 1][j - 1] + res[i - 1][j] + return res +``` + +```java +public class Solution { + public List> generate(int numRows) { + List> res = new ArrayList<>(); + + for (int i = 0; i < numRows; i++) { + List row = new ArrayList<>(); + for (int j = 0; j <= i; j++) { + if (j == 0 || j == i) { + row.add(1); + } else { + row.add(res.get(i - 1).get(j - 1) + res.get(i - 1).get(j)); + } + } + res.add(row); + } + return res; + } +} +``` + +```cpp +class Solution { +public: + vector> generate(int numRows) { + vector> res(numRows); + + for (int i = 0; i < numRows; i++) { + res[i].resize(i + 1); + res[i][0] = res[i][i] = 1; + for (int j = 1; j < i; j++){ + res[i][j] = res[i - 1][j - 1] + res[i - 1][j]; + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} numRows + * @return {number[][]} + */ + generate(numRows) { + let res = Array.from({ length: numRows }, (_, i) => Array(i + 1).fill(1)); + + for (let i = 2; i < numRows; i++) { + for (let j = 1; j < i; j++) { + res[i][j] = res[i - 1][j - 1] + res[i - 1][j]; + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/remove-element.md b/articles/remove-element.md new file mode 100644 index 000000000..1c3d722e4 --- /dev/null +++ b/articles/remove-element.md @@ -0,0 +1,233 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def removeElement(self, nums: list[int], val: int) -> int: + tmp = [] + for num in nums: + if num == val: + continue + tmp.append(num) + for i in range(len(tmp)): + nums[i] = tmp[i] + return len(tmp) +``` + +```java +public class Solution { + public int removeElement(int[] nums, int val) { + List tmp = new ArrayList<>(); + for (int num : nums) { + if (num != val) { + tmp.add(num); + } + } + for (int i = 0; i < tmp.size(); i++) { + nums[i] = tmp.get(i); + } + return tmp.size(); + } +} +``` + +```cpp +class Solution { +public: + int removeElement(vector& nums, int val) { + vector tmp; + for (int num : nums) { + if (num != val) { + tmp.push_back(num); + } + } + for (int i = 0; i < tmp.size(); i++) { + nums[i] = tmp[i]; + } + return tmp.size(); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} val + * @return {number} + */ + removeElement(nums, val) { + const tmp = []; + for (const num of nums) { + if (num !== val) { + tmp.push(num); + } + } + for (let i = 0; i < tmp.length; i++) { + nums[i] = tmp[i]; + } + return tmp.length; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Two Pointers - I + +::tabs-start + +```python +class Solution: + def removeElement(self, nums: list[int], val: int) -> int: + k = 0 + for i in range(len(nums)): + if nums[i] != val: + nums[k] = nums[i] + k += 1 + return k +``` + +```java +public class Solution { + public int removeElement(int[] nums, int val) { + int k = 0; + for (int i = 0; i < nums.length; i++) { + if (nums[i] != val) { + nums[k++] = nums[i]; + } + } + return k; + } +} +``` + +```cpp +class Solution { +public: + int removeElement(vector& nums, int val) { + int k = 0; + for (int i = 0; i < nums.size(); i++) { + if (nums[i] != val) { + nums[k++] = nums[i]; + } + } + return k; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} val + * @return {number} + */ + removeElement(nums, val) { + let k = 0; + for (let i = 0; i < nums.length; i++) { + if (nums[i] !== val) { + nums[k++] = nums[i]; + } + } + return k; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ + +--- + +## 3. Two Pointers - II + +::tabs-start + +```python +class Solution: + def removeElement(self, nums: list[int], val: int) -> int: + i = 0 + n = len(nums) + while i < n: + if nums[i] == val: + n -= 1 + nums[i] = nums[n] + else: + i += 1 + return n +``` + +```java +public class Solution { + public int removeElement(int[] nums, int val) { + int i = 0, n = nums.length; + while (i < n) { + if (nums[i] == val) { + nums[i] = nums[--n]; + } else { + i++; + } + } + return n; + } +} +``` + +```cpp +class Solution { +public: + int removeElement(vector& nums, int val) { + int i = 0, n = nums.size(); + while (i < n) { + if (nums[i] == val) { + nums[i] = nums[--n]; + } else { + i++; + } + } + return n; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} val + * @return {number} + */ + removeElement(nums, val) { + let i = 0, n = nums.length; + while (i < n) { + if (nums[i] == val) { + nums[i] = nums[--n]; + } else { + i++; + } + } + return n; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/replace-elements-with-greatest-element-on-right-side.md b/articles/replace-elements-with-greatest-element-on-right-side.md new file mode 100644 index 000000000..1825436a7 --- /dev/null +++ b/articles/replace-elements-with-greatest-element-on-right-side.md @@ -0,0 +1,154 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def replaceElements(self, arr: List[int]) -> List[int]: + n = len(arr) + ans = [0] * n + for i in range(n): + rightMax = -1 + for j in range(i + 1, n): + rightMax = max(rightMax, arr[j]) + ans[i] = rightMax + return ans +``` + +```java +public class Solution { + public int[] replaceElements(int[] arr) { + int n = arr.length; + int[] ans = new int[n]; + for (int i = 0; i < n; i++) { + int rightMax = -1; + for (int j = i + 1; j < n; j++) { + rightMax = Math.max(rightMax, arr[j]); + } + ans[i] = rightMax; + } + return ans; + } +} +``` + +```cpp +class Solution { +public: + vector replaceElements(vector& arr) { + int n = arr.size(); + vector ans(n); + for (int i = 0; i < n; ++i) { + int rightMax = -1; + for (int j = i + 1; j < n; ++j) { + rightMax = max(rightMax, arr[j]); + } + ans[i] = rightMax; + } + return ans; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} arr + * @return {number[]} + */ + replaceElements(arr) { + let n = arr.length; + let ans = new Array(n); + for (let i = 0; i < n; i++) { + let rightMax = -1; + for (let j = i + 1; j < n; j++) { + rightMax = Math.max(rightMax, arr[j]); + } + ans[i] = rightMax; + } + return ans; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(1)$ + +--- + +## 2. Suffix Max + +::tabs-start + +```python +class Solution: + def replaceElements(self, arr: List[int]) -> List[int]: + n = len(arr) + ans = [0] * n + rightMax = -1 + for i in range(n - 1, -1, -1): + ans[i] = rightMax + rightMax = max(arr[i], rightMax) + return ans +``` + +```java +public class Solution { + public int[] replaceElements(int[] arr) { + int n = arr.length; + int[] ans = new int[n]; + int rightMax = -1; + for (int i = n - 1; i >= 0; i--) { + ans[i] = rightMax; + rightMax = Math.max(rightMax, arr[i]); + } + return ans; + } +} +``` + +```cpp +class Solution { +public: + vector replaceElements(vector& arr) { + int n = arr.size(); + vector ans(n); + int rightMax = -1; + for (int i = n - 1; i >= 0; --i) { + ans[i] = rightMax; + rightMax = max(rightMax, arr[i]); + } + return ans; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} arr + * @return {number[]} + */ + replaceElements(arr) { + let n = arr.length; + let ans = new Array(n); + let rightMax = -1; + for (let i = n - 1; i >= 0; i--) { + ans[i] = rightMax; + rightMax = Math.max(rightMax, arr[i]); + } + return ans; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/unique-email-addresses.md b/articles/unique-email-addresses.md new file mode 100644 index 000000000..41aead4ae --- /dev/null +++ b/articles/unique-email-addresses.md @@ -0,0 +1,197 @@ +## 1. Built-In Functions + +::tabs-start + +```python +class Solution: + def numUniqueEmails(self, emails: List[str]) -> int: + unique = set() + + for e in emails: + local, domain = e.split('@') + local = local.split("+")[0] + local = local.replace(".", "") + unique.add((local, domain)) + return len(unique) +``` + +```java +public class Solution { + public int numUniqueEmails(String[] emails) { + Set unique = new HashSet<>(); + + for (String e : emails) { + String[] parts = e.split("@"); + String local = parts[0]; + String domain = parts[1]; + + local = local.split("\\+")[0]; + local = local.replace(".", ""); + unique.add(local + "@" + domain); + } + return unique.size(); + } +} +``` + +```cpp +class Solution { +public: + int numUniqueEmails(vector& emails) { + unordered_set unique; + + for (string e : emails) { + string local = e.substr(0, e.find('@')); + local = local.substr(0, local.find('+')); + erase(local, '.'); + unique.insert(local + e.substr(e.find('@'))); + } + return unique.size(); + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} emails + * @return {number} + */ + numUniqueEmails(emails) { + const unique = new Set(); + + for (let e of emails) { + let [local, domain] = e.split('@'); + local = local.split('+')[0]; + local = local.replace(/\./g, ''); + unique.add(`${local}@${domain}`); + } + return unique.size; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * m)$ +* Space complexity: $O(n)$ + +> Where $n$ is the number of strings in the array, and $m$ is the average length of these strings. + +--- + +## 2. Iteration + +::tabs-start + +```python +class Solution: + def numUniqueEmails(self, emails: List[str]) -> int: + unique = set() + + for e in emails: + i, local = 0, "" + while e[i] not in ["@", "+"]: + if e[i] != ".": + local += e[i] + i += 1 + + while e[i] != "@": + i += 1 + domain = e[i + 1:] + unique.add((local, domain)) + return len(unique) +``` + +```java +public class Solution { + public int numUniqueEmails(String[] emails) { + Set unique = new HashSet<>(); + + for (String e : emails) { + int i = 0; + StringBuilder local = new StringBuilder(); + while (i < e.length() && e.charAt(i) != '@' && e.charAt(i) != '+') { + if (e.charAt(i) != '.') { + local.append(e.charAt(i)); + } + i++; + } + + while (i < e.length() && e.charAt(i) != '@') { + i++; + } + String domain = e.substring(i + 1); + unique.add(local.toString() + "@" + domain); + } + return unique.size(); + } +} +``` + +```cpp +class Solution { +public: + int numUniqueEmails(vector& emails) { + unordered_set unique; + + for (string e : emails) { + int i = 0; + string local = ""; + while (i < e.length() && e[i] != '@' && e[i] != '+') { + if (e[i] != '.') { + local += e[i]; + } + i++; + } + + while (i < e.length() && e[i] != '@') { + i++; + } + string domain = e.substr(i + 1); + unique.insert(local + "@" + domain); + } + return unique.size(); + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} emails + * @return {number} + */ + numUniqueEmails(emails) { + const unique = new Set(); + + for (let e of emails) { + let i = 0, local = ""; + while (i < e.length && e[i] !== '@' && e[i] !== '+') { + if (e[i] !== '.') { + local += e[i]; + } + i++; + } + + while (i < e.length && e[i] !== '@') { + i++; + } + const domain = e.slice(i + 1); + unique.add(`${local}@${domain}`); + } + return unique.size; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * m)$ +* Space complexity: $O(n)$ + +> Where $n$ is the number of strings in the array, and $m$ is the average length of these strings. \ No newline at end of file From fca0598663fbf47f4052046cd39af1f4e06e9341 Mon Sep 17 00:00:00 2001 From: Guillaume Desktop Date: Sat, 30 Nov 2024 13:00:01 +0100 Subject: [PATCH 05/45] create csharp/0069-sqrt.cs --- csharp/0069-sqrt.cs | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 csharp/0069-sqrt.cs diff --git a/csharp/0069-sqrt.cs b/csharp/0069-sqrt.cs new file mode 100644 index 000000000..103574432 --- /dev/null +++ b/csharp/0069-sqrt.cs @@ -0,0 +1,21 @@ +public class Solution +{ + public int MySqrt(int x) + { + int left = 0, right = x; + while (left <= right) + { + int mid = (left + right) >> 1; + long pow = (long)mid * mid; + if (pow <= x) + { + left = mid + 1; + } + else + { + right = mid - 1; + } + } + return left - 1; + } +} \ No newline at end of file From f138da79ff0597a7e06d0c27a1fef8f681a9d809 Mon Sep 17 00:00:00 2001 From: Guillaume Desktop Date: Sat, 30 Nov 2024 13:03:25 +0100 Subject: [PATCH 06/45] create csharp/0203-remove-linked-list-elements.cs --- csharp/0203-remove-linked-list-elements.cs | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 csharp/0203-remove-linked-list-elements.cs diff --git a/csharp/0203-remove-linked-list-elements.cs b/csharp/0203-remove-linked-list-elements.cs new file mode 100644 index 000000000..a07d5b6f1 --- /dev/null +++ b/csharp/0203-remove-linked-list-elements.cs @@ -0,0 +1,21 @@ +public class Solution +{ + public ListNode RemoveElements(ListNode head, int val) + { + ListNode dummy = new ListNode(0, head); + ListNode current = dummy; + + while (current.next is not null) + { + if (current.next.val == val) + { + current.next = current.next.next; + } + else + { + current = current.next; + } + } + return dummy.next; + } +} \ No newline at end of file From ad912fbae839721e995671444e6b74f4e6a77342 Mon Sep 17 00:00:00 2001 From: Guillaume Desktop Date: Sat, 30 Nov 2024 15:09:13 +0100 Subject: [PATCH 07/45] Added additional solution to csharp/0003-longest-substring-without-repeating-characters.cs --- ...-substring-without-repeating-characters.cs | 52 +++++++++++++++++-- 1 file changed, 47 insertions(+), 5 deletions(-) diff --git a/csharp/0003-longest-substring-without-repeating-characters.cs b/csharp/0003-longest-substring-without-repeating-characters.cs index cf6e82d7d..15f410758 100644 --- a/csharp/0003-longest-substring-without-repeating-characters.cs +++ b/csharp/0003-longest-substring-without-repeating-characters.cs @@ -1,14 +1,22 @@ -public class Solution { - public int LengthOfLongestSubstring(string s) { +// hashset +public class Solution +{ + public int LengthOfLongestSubstring(string s) + { int leftPointer = 0, rightPointer = 0, maxLength = 0; HashSet chars = new HashSet(); - while (rightPointer < s.Length) { + while (rightPointer < s.Length) + { char currChar = s[rightPointer]; - if (chars.Contains(currChar)) { // Move left pointer until all duplicate chars removed + if (chars.Contains(currChar)) + { + // Move left pointer until all duplicate chars removed chars.Remove(s[leftPointer]); leftPointer++; - } else { + } + else + { chars.Add(currChar); maxLength = Math.Max(maxLength, rightPointer - leftPointer + 1); rightPointer++; @@ -16,4 +24,38 @@ public int LengthOfLongestSubstring(string s) { } return maxLength; } +} + +//bitmask +public class Solution +{ + private Int128 ONE = 1; + public int LengthOfLongestSubstring(string s) + { + int Convert(char ch) => ch - ' '; + Int128 mask = 0; + int l = 0, r = 0, output = 0; + while (r < s.Length) + { + Int128 temp = mask ^ (ONE << Convert(s[r])); + if (temp < mask) + { + while (s[l] != s[r]) + { + mask ^= ONE << Convert(s[l]); + l++; + } + mask ^= ONE << Convert(s[l]); + l++; + } + else + { + mask = temp; + output = Math.Max(output, r - l + 1); + r++; + } + } + + return output; + } } \ No newline at end of file From 9898f4bbb596ee8d42a877820ac0293f24ea937e Mon Sep 17 00:00:00 2001 From: Yaseen Khan Date: Sat, 30 Nov 2024 23:45:15 -0800 Subject: [PATCH 08/45] changed filename to match other submitted solutions for this problem --- csharp/{0069-sqrt.cs => 0069-sqrtx.cs} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename csharp/{0069-sqrt.cs => 0069-sqrtx.cs} (99%) diff --git a/csharp/0069-sqrt.cs b/csharp/0069-sqrtx.cs similarity index 99% rename from csharp/0069-sqrt.cs rename to csharp/0069-sqrtx.cs index 103574432..a994a0f06 100644 --- a/csharp/0069-sqrt.cs +++ b/csharp/0069-sqrtx.cs @@ -18,4 +18,4 @@ public int MySqrt(int x) } return left - 1; } -} \ No newline at end of file +} From 70c2bab48651f009f8690b0db532c275cee053b1 Mon Sep 17 00:00:00 2001 From: Bot-A0 <71089234+Ahmad-A0@users.noreply.github.com> Date: Sun, 1 Dec 2024 08:07:57 +0000 Subject: [PATCH 09/45] =?UTF-8?q?=F0=9F=93=9C=20Update=20README=20table=20?= =?UTF-8?q?(=F0=9F=9B=A0=EF=B8=8F=20from=20Github=20Actions)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index fbe35a7f3..621435471 100644 --- a/README.md +++ b/README.md @@ -166,7 +166,7 @@ If you would like to have collaborator permissions on the repo to merge your own [0441 - Arranging Coins](https://leetcode.com/problems/arranging-coins/) |
|
|
[✔️](cpp%2F0441-arranging-coins.cpp)
|
|
|
|
|
[✔️](java%2F0441-arranging-coins.java)
|
[✔️](javascript%2F0441-arranging-coins.js)
|
[✔️](kotlin%2F0441-arranging-coins.kt)
|
[✔️](python%2F0441-arranging-coins.py)
|
|
[✔️](rust%2F0441-arranging-coins.rs)
|
|
|
[0977 - Squares of a Sorted Array](https://leetcode.com/problems/squares-of-a-sorted-array/) |
|
|
[✔️](cpp%2F0977-squares-of-a-sorted-array.cpp)
|
|
|
[✔️](go%2F0977-squares-of-a-sorted-array.go)
|
|
[✔️](java%2F0977-squares-of-a-sorted-array.java)
|
[✔️](javascript%2F0977-squares-of-a-sorted-array.js)
|
[✔️](kotlin%2F0977-squares-of-a-sorted-array.kt)
|
[✔️](python%2F0977-squares-of-a-sorted-array.py)
|
|
[✔️](rust%2F0977-squares-of-a-sorted-array.rs)
|
|
[✔️](swift%2F0977-squares-of-a-sorted-array.swift)
|
[✔️](typescript%2F0977-squares-of-a-sorted-array.ts)
[0367 - Valid Perfect Square](https://leetcode.com/problems/valid-perfect-square/) |
|
[✔️](c%2F0367-valid-perfect-square.c)
|
[✔️](cpp%2F0367-valid-perfect-square.cpp)
|
|
|
[✔️](go%2F0367-valid-perfect-square.go)
|
|
[✔️](java%2F0367-valid-perfect-square.java)
|
[✔️](javascript%2F0367-valid-perfect-square.js)
|
[✔️](kotlin%2F0367-valid-perfect-square.kt)
|
[✔️](python%2F0367-valid-perfect-square.py)
|
|
|
|
[✔️](swift%2F0367-valid-perfect-square.swift)
|
-[0069 - Sqrt(x) ](https://leetcode.com/problems/sqrtx/) |
|
[✔️](c%2F0069-sqrtx.c)
|
[✔️](cpp%2F0069-sqrtx.cpp)
|
|
|
|
|
[✔️](java%2F0069-sqrtx.java)
|
[✔️](javascript%2F0069-sqrtx.js)
|
[✔️](kotlin%2F0069-sqrtx.kt)
|
[✔️](python%2F0069-sqrtx.py)
|
|
|
|
|
+[0069 - Sqrt(x) ](https://leetcode.com/problems/sqrtx/) |
|
[✔️](c%2F0069-sqrtx.c)
|
[✔️](cpp%2F0069-sqrtx.cpp)
|
[✔️](csharp%2F0069-sqrtx.cs)
|
|
|
|
[✔️](java%2F0069-sqrtx.java)
|
[✔️](javascript%2F0069-sqrtx.js)
|
[✔️](kotlin%2F0069-sqrtx.kt)
|
[✔️](python%2F0069-sqrtx.py)
|
|
|
|
|
[0540 - Single Element in a Sorted Array](https://leetcode.com/problems/single-element-in-a-sorted-array/) |
|
[✔️](c%2F0540-single-element-in-a-sorted-array.c)
|
[✔️](cpp%2F0540-single-element-in-a-sorted-array.cpp)
|
|
|
|
|
[✔️](java%2F0540-single-element-in-a-sorted-array.java)
|
[✔️](javascript%2F0540-single-element-in-a-sorted-array.js)
|
[✔️](kotlin%2F0540-single-element-in-a-sorted-array.kt)
|
[✔️](python%2F0540-single-element-in-a-sorted-array.py)
|
|
|
|
|
[✔️](typescript%2F0540-single-element-in-a-sorted-array.ts)
[1011 - Capacity to Ship Packages](https://leetcode.com/problems/capacity-to-ship-packages-within-d-days/) |
|
|
[✔️](cpp%2F1011-capacity-to-ship-packages-within-d-days.cpp)
|
|
|
|
|
[✔️](java%2F1011-capacity-to-ship-packages-within-d-days.java)
|
|
[✔️](kotlin%2F1011-capacity-to-ship-packages-within-d-days.kt)
|
[✔️](python%2F1011-capacity-to-ship-packages-within-d-days.py)
|
|
|
|
|
[0162 - Find Peak Element](https://leetcode.com/problems/find-peak-element/) |
|
[✔️](c%2F0162-find-peak-element.c)
|
[✔️](cpp%2F0162-find-peak-element.cpp)
|
|
|
|
|
[✔️](java%2F0162-find-peak-element.java)
|
[✔️](javascript%2F0162-find-peak-element.js)
|
[✔️](kotlin%2F0162-find-peak-element.kt)
|
[✔️](python%2F0162-find-peak-element.py)
|
|
|
|
|
@@ -192,7 +192,7 @@ If you would like to have collaborator permissions on the repo to merge your own [0206 - Reverse Linked List](https://leetcode.com/problems/reverse-linked-list/) |
|
[✔️](c%2F0206-reverse-linked-list.c)
|
[✔️](cpp%2F0206-reverse-linked-list.cpp)
|
[✔️](csharp%2F0206-reverse-linked-list.cs)
|
[✔️](dart%2F0206-reverse-linked-list.dart)
|
[✔️](go%2F0206-reverse-linked-list.go)
|
|
[✔️](java%2F0206-reverse-linked-list.java)
|
[✔️](javascript%2F0206-reverse-linked-list.js)
|
[✔️](kotlin%2F0206-reverse-linked-list.kt)
|
[✔️](python%2F0206-reverse-linked-list.py)
|
[✔️](ruby%2F0206-reverse-linked-list.rb)
|
[✔️](rust%2F0206-reverse-linked-list.rs)
|
[✔️](scala%2F0206-reverse-linked-list.scala)
|
[✔️](swift%2F0206-reverse-linked-list.swift)
|
[✔️](typescript%2F0206-reverse-linked-list.ts)
[0021 - Merge Two Sorted Lists](https://leetcode.com/problems/merge-two-sorted-lists/) |
|
[✔️](c%2F0021-merge-two-sorted-lists.c)
|
[✔️](cpp%2F0021-merge-two-sorted-lists.cpp)
|
[✔️](csharp%2F0021-merge-two-sorted-lists.cs)
|
[✔️](dart%2F0021-merge-two-sorted-lists.dart)
|
[✔️](go%2F0021-merge-two-sorted-lists.go)
|
|
[✔️](java%2F0021-merge-two-sorted-lists.java)
|
[✔️](javascript%2F0021-merge-two-sorted-lists.js)
|
[✔️](kotlin%2F0021-merge-two-sorted-lists.kt)
|
[✔️](python%2F0021-merge-two-sorted-lists.py)
|
[✔️](ruby%2F0021-merge-two-sorted-lists.rb)
|
[✔️](rust%2F0021-merge-two-sorted-lists.rs)
|
[✔️](scala%2F0021-merge-two-sorted-lists.scala)
|
[✔️](swift%2F0021-merge-two-sorted-lists.swift)
|
[✔️](typescript%2F0021-merge-two-sorted-lists.ts)
[0234 - Palindrome Linked List](https://leetcode.com/problems/palindrome-linked-list/) |
|
[✔️](c%2F0234-palindrome-linked-list.c)
|
[✔️](cpp%2F0234-palindrome-linked-list.cpp)
|
|
|
[✔️](go%2F0234-palindrome-linked-list.go)
|
|
[✔️](java%2F0234-palindrome-linked-list.java)
|
[✔️](javascript%2F0234-palindrome-linked-list.js)
|
[✔️](kotlin%2F0234-palindrome-linked-list.kt)
|
[✔️](python%2F0234-palindrome-linked-list.py)
|
|
|
|
|
-[0203 - Remove Linked List Elements](https://leetcode.com/problems/remove-linked-list-elements/) |
|
|
[✔️](cpp%2F0203-remove-linked-list-elements.cpp)
|
|
|
[✔️](go%2F0203-remove-linked-list-elements.go)
|
|
[✔️](java%2F0203-remove-linked-list-elements.java)
|
[✔️](javascript%2F0203-remove-linked-list-elements.js)
|
[✔️](kotlin%2F0203-remove-linked-list-elements.kt)
|
[✔️](python%2F0203-remove-linked-list-elements.py)
|
|
|
|
|
[✔️](typescript%2F0203-remove-linked-list-elements.ts)
+[0203 - Remove Linked List Elements](https://leetcode.com/problems/remove-linked-list-elements/) |
|
|
[✔️](cpp%2F0203-remove-linked-list-elements.cpp)
|
[✔️](csharp%2F0203-remove-linked-list-elements.cs)
|
|
[✔️](go%2F0203-remove-linked-list-elements.go)
|
|
[✔️](java%2F0203-remove-linked-list-elements.java)
|
[✔️](javascript%2F0203-remove-linked-list-elements.js)
|
[✔️](kotlin%2F0203-remove-linked-list-elements.kt)
|
[✔️](python%2F0203-remove-linked-list-elements.py)
|
|
|
|
|
[✔️](typescript%2F0203-remove-linked-list-elements.ts)
[0083 - Remove Duplicates From Sorted List](https://leetcode.com/problems/remove-duplicates-from-sorted-list/) |
|
[✔️](c%2F0083-remove-duplicates-from-sorted-list.c)
|
[✔️](cpp%2F0083-remove-duplicates-from-sorted-list.cpp)
|
|
|
[✔️](go%2F0083-remove-duplicates-from-sorted-list.go)
|
|
[✔️](java%2F0083-remove-duplicates-from-sorted-list.java)
|
[✔️](javascript%2F0083-remove-duplicates-from-sorted-list.js)
|
[✔️](kotlin%2F0083-remove-duplicates-from-sorted-list.kt)
|
[✔️](python%2F0083-remove-duplicates-from-sorted-list.py)
|
|
|
|
[✔️](swift%2F0083-remove-duplicates-from-sorted-list.swift)
|
[0876 - Middle of the Linked List](https://leetcode.com/problems/middle-of-the-linked-list/) |
|
[✔️](c%2F0876-middle-of-the-linked-list.c)
|
[✔️](cpp%2F0876-middle-of-the-linked-list.cpp)
|
[✔️](csharp%2F0876-middle-of-the-linked-list.cs)
|
|
[✔️](go%2F0876-middle-of-the-linked-list.go)
|
|
[✔️](java%2F0876-middle-of-the-linked-list.java)
|
[✔️](javascript%2F0876-middle-of-the-linked-list.js)
|
[✔️](kotlin%2F0876-middle-of-the-linked-list.kt)
|
[✔️](python%2F0876-middle-of-the-linked-list.py)
|
|
[✔️](rust%2F0876-middle-of-the-linked-list.rs)
|
|
[✔️](swift%2F0876-middle-of-the-linked-list.swift)
|
[✔️](typescript%2F0876-middle-of-the-linked-list.ts)
[0160 - Intersection of Two Linked Lists](https://leetcode.com/problems/intersection-of-two-linked-lists/) |
|
[✔️](c%2F0160-intersection-of-two-linked-lists.c)
|
[✔️](cpp%2F0160-intersection-of-two-linked-lists.cpp)
|
|
|
[✔️](go%2F0160-intersection-of-two-linked-lists.go)
|
|
[✔️](java%2F0160-intersection-of-two-linked-lists.java)
|
[✔️](javascript%2F0160-intersection-of-two-linked-lists.js)
|
[✔️](kotlin%2F0160-intersection-of-two-linked-lists.kt)
|
[✔️](python%2F0160-intersection-of-two-linked-lists.py)
|
|
|
|
|
From a0098a6a0fe56a9168f75367937d01f667358bdb Mon Sep 17 00:00:00 2001 From: Sri Hari <94112314+Srihari2222@users.noreply.github.com> Date: Sat, 7 Dec 2024 02:06:25 +0530 Subject: [PATCH 10/45] Batch-3/Neetcode-All/Added (#3759) --- hints/count-connected-components.md | 31 +++++++++++++++++++++++ hints/course-schedule-ii.md | 31 +++++++++++++++++++++++ hints/course-schedule.md | 2 +- hints/min-cost-to-connect-points.md | 31 +++++++++++++++++++++++ hints/network-delay-time.md | 31 +++++++++++++++++++++++ hints/reconstruct-flight-path.md | 39 +++++++++++++++++++++++++++++ hints/redundant-connection.md | 31 +++++++++++++++++++++++ hints/swim-in-rising-water.md | 31 +++++++++++++++++++++++ hints/valid-tree.md | 31 +++++++++++++++++++++++ hints/word-ladder.md | 31 +++++++++++++++++++++++ 10 files changed, 288 insertions(+), 1 deletion(-) create mode 100644 hints/count-connected-components.md create mode 100644 hints/course-schedule-ii.md create mode 100644 hints/min-cost-to-connect-points.md create mode 100644 hints/network-delay-time.md create mode 100644 hints/reconstruct-flight-path.md create mode 100644 hints/redundant-connection.md create mode 100644 hints/swim-in-rising-water.md create mode 100644 hints/valid-tree.md create mode 100644 hints/word-ladder.md diff --git a/hints/count-connected-components.md b/hints/count-connected-components.md new file mode 100644 index 000000000..4998f22ad --- /dev/null +++ b/hints/count-connected-components.md @@ -0,0 +1,31 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution as good or better than O(V + E) time and O(V + E) space, where V is the number vertices and E is the number of edges in the graph. +

+
+ +
+
+ Hint 1 +

+ Assume there are no edges initially, so we have n components, as there are that many nodes given. Now, we should add the given edges between the nodes. Can you think of an algorithm to add the edges between the nodes? Also, after adding an edge, how do you determine the number of components left? +

+
+ +
+
+ Hint 2 +

+ We can use the Union-Find (DSU) algorithm to add the given edges. For simplicity, we use Union-Find by size, where we merge the smaller component into the larger one. The Union-Find algorithm inserts the edge only between two nodes from different components. It does not add the edge if the nodes are from the same component. How do you find the number of components after adding the edges? For example, consider that nodes 0 and 1 are not connected, so there are initially two components. After adding an edge between these nodes, they become part of the same component, leaving us with one component. +

+
+ +
+
+ Hint 3 +

+ We create an object of the DSU and initialize the result variable res = n, which indicates that there are n components initially. We then iterate through the given edges. For each edge, we attempt to connect the nodes using the union function of the DSU. If the union is successful, we decrement res; otherwise, we continue. Finally, we return res as the number of components. +

+
\ No newline at end of file diff --git a/hints/course-schedule-ii.md b/hints/course-schedule-ii.md new file mode 100644 index 000000000..e187980f9 --- /dev/null +++ b/hints/course-schedule-ii.md @@ -0,0 +1,31 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution with O(V + E) time and O(V + E) space, where V is the number of courses (nodes) and E is the number of prerequisites (edges). +

+
+ +
+
+ Hint 1 +

+ Consider the problem as a graph where courses represent the nodes, and prerequisite[i] = [a, b] represents a directed edge from a to b. We need to determine whether the graph contains a cycle. Why? Because if there is a cycle, it is impossible to complete the courses involved in the cycle. Can you think of an algorithm to detect a cycle in a graph and also find the valid ordering if a cycle doesn't exist? +

+
+ +
+
+ Hint 2 +

+ We can use DFS to detect a cycle in a graph. However, we also need to find the valid ordering of the courses, which can also be achieved using DFS. Alternatively, we can use the Topological Sort algorithm to find the valid ordering in this directed graph, where the graph must be acyclic to complete all the courses, and the prerequisite of a course acts as the parent node of that course. How would you implement this? +

+
+ +
+
+ Hint 3 +

+ We compute the indegrees of all the nodes. Then, we perform a BFS starting from the nodes that have no parents (indegree[node] == 0). At each level, we traverse these nodes, decrement the indegree of their child nodes, and append those child nodes to the queue if their indegree becomes 0. We only append nodes whose indegree is 0 or becomes 0 during the BFS to our result array. If the length of the result array is not equal to the number of courses, we return an empty array. +

+
\ No newline at end of file diff --git a/hints/course-schedule.md b/hints/course-schedule.md index 1660429b5..1392ac134 100644 --- a/hints/course-schedule.md +++ b/hints/course-schedule.md @@ -2,7 +2,7 @@
Recommended Time & Space Complexity

- You should aim for a solution with O(V + E) time and O(V + E) space, where V is the number of courses (nodes) and E is the number of prerequisites (edges). + You should aim for a solution with O(V + E) time and O(V + E) space, where V is the number of courses (nodes) and E is the number of prerequisites (edges).

diff --git a/hints/min-cost-to-connect-points.md b/hints/min-cost-to-connect-points.md new file mode 100644 index 000000000..cc585977d --- /dev/null +++ b/hints/min-cost-to-connect-points.md @@ -0,0 +1,31 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution as good or better than O((n^2)logn) time and O(n^2) space, where n is the number of points. +

+
+ +
+
+ Hint 1 +

+ Think of this problem as a graph, where the given points represent nodes. We need to connect these nodes into a single component by creating edges. Can you think of an advanced graph algorithm that can be used to connect all points into one component? +

+
+ +
+
+ Hint 2 +

+ We use Kruskal's algorithm along with Union-Find (DSU) to connect nodes into components. The final component forms the minimum spanning tree (MST), where the edges between nodes are weighted by the Manhattan distance, and the total weight of the tree is minimized. How would you implement this? +

+
+ +
+
+ Hint 3 +

+ We create the possible edges by iterating through every pair of points and calculating the weights as the Manhattan distance between them. Next, we sort the edges in ascending order based on their weights, as we aim to minimize the cost. Then, we traverse through these edges, connecting the nodes and adding the weight of the edge to the total cost if the edge is successfully added. The final result will be the minimum cost. +

+
\ No newline at end of file diff --git a/hints/network-delay-time.md b/hints/network-delay-time.md new file mode 100644 index 000000000..901cefb98 --- /dev/null +++ b/hints/network-delay-time.md @@ -0,0 +1,31 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution as good or better than O(ElogV) time and O(V + E) space, where E is the number of edges and V is the number of vertices (nodes). +

+
+ +
+
+ Hint 1 +

+ As we are given the source node and we need to find the minimum time to reach all nodes, this represents the shortest path from the source node to all nodes. Can you think of a standard algorithm to find the shortest path from a source to a destination? Maybe a heap-based algorithm is helpful. +

+
+ +
+
+ Hint 2 +

+ We can use Dijkstra's algorithm to find the shortest path from a source to destination. We end up finding the shortest paths from the source to the nodes that we encounter in finding the destination. So, to find shortest path for all nodes from the source, we need to perform Dijkstra's algorithm until the heap in this algorithm becomes empty. How would you implement this? +

+
+ +
+
+ Hint 3 +

+ We use a Min-Heap as we need to find the minimum time. We create an adjacency list for the given times (weighted edges). We also initialize an array dist[] of size n (number of nodes) which represents the distance from the source to all nodes, initialized with infinity. We put dist[source] = 0. Then we continue the algorithm. After the heap becomes empty, if we don't visit any node, we return -1; otherwise, we return the time. +

+
\ No newline at end of file diff --git a/hints/reconstruct-flight-path.md b/hints/reconstruct-flight-path.md new file mode 100644 index 000000000..43f336c17 --- /dev/null +++ b/hints/reconstruct-flight-path.md @@ -0,0 +1,39 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution with O(ElogE) time and O(E) space, where E is the number of tickets (edges). +

+
+ +
+
+ Hint 1 +

+ Consider this problem as a graph where airports are the nodes and tickets are the edges. Since we need to utilize all the tickets exactly once, can you think of an algorithm that visits every edge exactly once? Additionally, how do you ensure the smallest lexical order is maintained? +

+
+ +
+
+ Hint 2 +

+ We build an adjacency list from the given tickets, which represent directed edges. We perform a DFS to construct the result, but we first sort the neighbors' list of each node to ensure the smallest lexical order. Why? Sorting guarantees that during DFS, we visit the node with the smallest lexical order first. +

+
+ +
+
+ Hint 3 +

+ DFS would be a naive solution, as it takes O(E * V) time, where E is the number of tickets (edges) and V is the number of airports (nodes). In this approach, we traverse from the given source airport JFK, perform DFS by removing the neighbor, traversing it, and then reinserting it back. Can you think of a better way? Perhaps an advanced algorithm that incorporates DFS might be helpful? +

+
+ +
+
+ Hint 4 +

+ We can use Hierholzer's algorithm, a modified DFS approach. Instead of appending the node to the result list immediately, we first visit all its neighbors. This results in a post-order traversal. After completing all the DFS calls, we reverse the path to obtain the final path, which is also called Euler's path. +

+
\ No newline at end of file diff --git a/hints/redundant-connection.md b/hints/redundant-connection.md new file mode 100644 index 000000000..cdd3217fe --- /dev/null +++ b/hints/redundant-connection.md @@ -0,0 +1,31 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution as good or better than O(V + E) time and O(V + E) space, where V is the number vertices and E is the number of edges in the graph. +

+
+ +
+
+ Hint 1 +

+ There will be only one edge that creates the cycle in the given problem. Why? Because the graph is initially acyclic, and a cycle is formed only after adding one extra edge that was not present in the graph initially. Can you think of an algorithm that helps determine whether the current connecting edge forms a cycle? Perhaps a component-oriented algorithm? +

+
+ +
+
+ Hint 2 +

+ We can use the Union-Find (DSU) algorithm to create the graph from the given edges. While connecting the edges, if we fail to connect any edge, it means this is the redundant edge, and we return it. How would you implement this? +

+
+ +
+
+ Hint 3 +

+ We create an instance of the DSU object and traverse through the given edges. For each edge, we attempt to connect the nodes using the union function. If the union function returns false, indicating that the current edge forms a cycle, we immediately return that edge. +

+
\ No newline at end of file diff --git a/hints/swim-in-rising-water.md b/hints/swim-in-rising-water.md new file mode 100644 index 000000000..5e098b6f0 --- /dev/null +++ b/hints/swim-in-rising-water.md @@ -0,0 +1,31 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution with O((n^2)logn) time and O(n^2) space, where n is the number of rows or columns of the square matrix. +

+
+ +
+
+ Hint 1 +

+ Think of this problem as a graph where each cell represents a node. You can move from one cell to its adjacent cell if the time is greater than or equal to the adjacent cell's elevation. Note that swimming does not cost time, but you may need to wait at a cell for the time to reach the required elevation. What do you notice about the path from (0, 0) to (n - 1, n - 1)? Perhaps a greedy approach would be useful here. +

+
+ +
+
+ Hint 2 +

+ We can observe that the maximum elevation value along the path determines the time taken for that path. Therefore, we need to find the path where the maximum elevation is minimized. Can you think of an algorithm to find such a path from the source (0, 0) to the destination (n - 1, n - 1)? Perhaps a shortest path algorithm could be useful here. +

+
+ +
+
+ Hint 3 +

+ We can use Dijkstra's algorithm. We initialize a Min-heap and a matrix with infinity. We run the algorithm starting from the source (0, 0), and we track the maximum elevation encountered along the paths. This maximum elevation is used as the key for comparison in Dijkstra's algorithm. If we encounter the destination (n - 1, n - 1), we return the maximum elevation of the path that reached the destination. +

+
\ No newline at end of file diff --git a/hints/valid-tree.md b/hints/valid-tree.md new file mode 100644 index 000000000..7e7d37358 --- /dev/null +++ b/hints/valid-tree.md @@ -0,0 +1,31 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution as good or better than O(V + E) time and O(V + E) space, where V is the number vertices and E is the number of edges in the graph. +

+
+ +
+
+ Hint 1 +

+ According to the definition of a tree, a tree is an undirected graph with no cycles, all the nodes are connected as one component, and any two nodes have exactly one path. Can you think of a recursive algorithm to detect a cycle in the given graph and ensure it is a tree? +

+
+ +
+
+ Hint 2 +

+ We can use the Depth First Search (DFS) algorithm to detect a cycle in the graph. Since a tree has only one component, we can start the DFS from any node, say node 0. During the traversal, we recursively visit its neighbors (children). If we encounter any already visited node that is not the parent of the current node, we return false as it indicates a cycle. How would you implement this? +

+
+ +
+
+ Hint 3 +

+ We start DFS from node 0, assuming -1 as its parent. We initialize a hash set visit to track the visited nodes in the graph. During the DFS, we first check if the current node is already in visit. If it is, we return false, detecting a cycle. Otherwise, we mark the node as visited and perform DFS on its neighbors, skipping the parent node to avoid revisiting it. After all DFS calls, if we have visited all nodes, we return true, as the graph is connected. Otherwise, we return false because a tree must contain all nodes. +

+
\ No newline at end of file diff --git a/hints/word-ladder.md b/hints/word-ladder.md new file mode 100644 index 000000000..efdd32a2c --- /dev/null +++ b/hints/word-ladder.md @@ -0,0 +1,31 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution with O((m ^ 2) * n) time and O((m ^ 2) * n) space, where n is the number of words and m is the length of the word. +

+
+ +
+
+ Hint 1 +

+ Consider the given problem in terms of a graph, treating strings as nodes. Think of a way to build edges where two strings have an edge if they differ by a single character. A naive approach would be to consider each pair of strings and check whether an edge can be formed. Can you think of an efficient way? For example, consider a string hot and think about the strings that can be formed from it by changing a single letter. +

+
+ +
+
+ Hint 2 +

+ To efficiently build edges, consider transforming each word into intermediate states by replacing one character with a wildcard, like *. For example, the word hot can be transformed into *ot, h*t, and ho*. These intermediate states act as "parents" that connect words differing by one character. For instance, *ot can connect to words like cot. For each word in the list, generate all possible patterns by replacing each character with * and store the word as a child of these patterns. We can run a BFS starting from the beginWord, visiting other words while avoiding revisiting by using a hash set. +

+
+ +
+
+ Hint 3 +

+ When visiting a node during BFS, if the word matches the endWord, we immediately return true. Otherwise, we generate the pattern words that can be formed from the current word and attempt to visit the words connected to these pattern words. We add only unvisited words to the queue. If we exhaust all possibilities without finding the endWord, we return false. +

+
\ No newline at end of file From 232acfa353f770d8658edc38eba93dd12bf86978 Mon Sep 17 00:00:00 2001 From: Sri Hari <94112314+Srihari2222@users.noreply.github.com> Date: Sat, 7 Dec 2024 02:08:52 +0530 Subject: [PATCH 11/45] SriHari: Batch-3/Neetcode-All/Added articles (#3760) * small changes * Batch-3/Neetcode-All/Added-articles * Batch-3/Neetcode-All/Added-articles * Batch-3/Neetcode-All/Added-articles * Batch-3/Neetcode-All/Added-articles * Batch-3/Neetcode-All/Added-articles * Batch-3/Neetcode-All/Added-articles * Batch-3/Neetcode-All/Added-articles --- .../best-time-to-buy-and-sell-stock-ii.md | 458 +++++++ articles/brick-wall.md | 245 ++++ articles/continuous-subarray-sum.md | 177 +++ articles/design-hashmap.md | 365 ++++++ articles/design-hashset.md | 1028 +++++++++++++++ articles/destination-city.md | 253 ++++ articles/find-all-anagrams-in-a-string.md | 581 +++++++++ ...ind-all-numbers-disappeared-in-an-array.md | 358 ++++++ ...dex-of-the-first-occurrence-in-a-string.md | 633 ++++++++++ ...-words-that-can-be-formed-by-characters.md | 391 ++++++ .../first-unique-character-in-a-string.md | 365 ++++++ articles/grid-game.md | 354 ++++++ articles/intersection-of-two-arrays.md | 566 +++++++++ .../largest-3-same-digit-number-in-string.md | 255 ++++ articles/largest-number.md | 188 +++ ...-substring-between-two-equal-characters.md | 380 ++++++ articles/longest-common-prefix.md | 4 +- ...um-product-difference-between-two-pairs.md | 255 ++++ ...-length-of-two-palindromic-subsequences.md | 854 +++++++++++++ .../maximum-score-after-splitting-a-string.md | 495 ++++++++ ...anges-to-make-alternating-binary-string.md | 207 +++ ...er-of-swaps-to-make-the-string-balanced.md | 219 ++++ articles/monotonic-array.md | 305 +++++ articles/number-of-good-pairs.md | 221 ++++ ...-of-pairs-of-interchangeable-rectangles.md | 364 ++++++ .../number-of-students-unable-to-eat-lunch.md | 348 ++++++ articles/pascals-triangle-ii.md | 375 ++++++ articles/pascals-triangle.md | 6 +- articles/path-crossing.md | 242 ++++ articles/products-of-array-discluding-self.md | 7 +- articles/push-dominoes.md | 749 +++++++++++ articles/range-sum-query-immutable.md | 452 +++++++ articles/reconstruct-flight-path.md | 6 +- ...te-characters-to-make-all-strings-equal.md | 228 ++++ articles/redundant-connection.md | 2 +- articles/set-mismatch.md | 721 +++++++++++ articles/sort-an-array.md | 1105 +++++++++++++++++ articles/sort-colors.md | 437 +++++++ ...with-x-elements-greater-than-or-equal-x.md | 506 ++++++++ articles/subarray-sum-equals-k.md | 169 +++ articles/time-needed-to-buy-tickets.md | 329 +++++ ...nique-length-3-palindromic-subsequences.md | 863 +++++++++++++ articles/word-pattern.md | 533 ++++++++ 43 files changed, 16586 insertions(+), 13 deletions(-) create mode 100644 articles/best-time-to-buy-and-sell-stock-ii.md create mode 100644 articles/brick-wall.md create mode 100644 articles/continuous-subarray-sum.md create mode 100644 articles/design-hashmap.md create mode 100644 articles/design-hashset.md create mode 100644 articles/destination-city.md create mode 100644 articles/find-all-anagrams-in-a-string.md create mode 100644 articles/find-all-numbers-disappeared-in-an-array.md create mode 100644 articles/find-the-index-of-the-first-occurrence-in-a-string.md create mode 100644 articles/find-words-that-can-be-formed-by-characters.md create mode 100644 articles/first-unique-character-in-a-string.md create mode 100644 articles/grid-game.md create mode 100644 articles/intersection-of-two-arrays.md create mode 100644 articles/largest-3-same-digit-number-in-string.md create mode 100644 articles/largest-number.md create mode 100644 articles/largest-substring-between-two-equal-characters.md create mode 100644 articles/maximum-product-difference-between-two-pairs.md create mode 100644 articles/maximum-product-of-the-length-of-two-palindromic-subsequences.md create mode 100644 articles/maximum-score-after-splitting-a-string.md create mode 100644 articles/minimum-changes-to-make-alternating-binary-string.md create mode 100644 articles/minimum-number-of-swaps-to-make-the-string-balanced.md create mode 100644 articles/monotonic-array.md create mode 100644 articles/number-of-good-pairs.md create mode 100644 articles/number-of-pairs-of-interchangeable-rectangles.md create mode 100644 articles/number-of-students-unable-to-eat-lunch.md create mode 100644 articles/pascals-triangle-ii.md create mode 100644 articles/path-crossing.md create mode 100644 articles/push-dominoes.md create mode 100644 articles/range-sum-query-immutable.md create mode 100644 articles/redistribute-characters-to-make-all-strings-equal.md create mode 100644 articles/set-mismatch.md create mode 100644 articles/sort-an-array.md create mode 100644 articles/sort-colors.md create mode 100644 articles/special-array-with-x-elements-greater-than-or-equal-x.md create mode 100644 articles/subarray-sum-equals-k.md create mode 100644 articles/time-needed-to-buy-tickets.md create mode 100644 articles/unique-length-3-palindromic-subsequences.md create mode 100644 articles/word-pattern.md diff --git a/articles/best-time-to-buy-and-sell-stock-ii.md b/articles/best-time-to-buy-and-sell-stock-ii.md new file mode 100644 index 000000000..e6abb861a --- /dev/null +++ b/articles/best-time-to-buy-and-sell-stock-ii.md @@ -0,0 +1,458 @@ +## 1. Recursion + +::tabs-start + +```python +class Solution: + def maxProfit(self, prices: List[int]) -> int: + def rec(i, bought): + if i == len(prices): + return 0 + res = rec(i + 1, bought) + if bought: + res = max(res, prices[i] + rec(i + 1, False)) + else: + res = max(res, -prices[i] + rec(i + 1, True)) + return res + return rec(0, False) +``` + +```java +public class Solution { + public int maxProfit(int[] prices) { + return rec(prices, 0, false); + } + + private int rec(int[] prices, int i, boolean bought) { + if (i == prices.length) { + return 0; + } + int res = rec(prices, i + 1, bought); + if (bought) { + res = Math.max(res, prices[i] + rec(prices, i + 1, false)); + } else { + res = Math.max(res, -prices[i] + rec(prices, i + 1, true)); + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int maxProfit(vector& prices) { + return rec(prices, 0, false); + } + +private: + int rec(vector& prices, int i, bool bought) { + if (i == prices.size()) { + return 0; + } + int res = rec(prices, i + 1, bought); + if (bought) { + res = max(res, prices[i] + rec(prices, i + 1, false)); + } else { + res = max(res, -prices[i] + rec(prices, i + 1, true)); + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} prices + * @return {number} + */ + maxProfit(prices) { + const rec = (i, bought) => { + if (i === prices.length) { + return 0; + } + let res = rec(i + 1, bought); + if (bought) { + res = Math.max(res, prices[i] + rec(i + 1, false)); + } else { + res = Math.max(res, -prices[i] + rec(i + 1, true)); + } + return res; + }; + return rec(0, false); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(2 ^ n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def maxProfit(self, prices: List[int]) -> int: + dp = {} + + def rec(i, bought): + if i == len(prices): + return 0 + if (i, bought) in dp: + return dp[(i, bought)] + res = rec(i + 1, bought) + if bought: + res = max(res, prices[i] + rec(i + 1, False)) + else: + res = max(res, -prices[i] + rec(i + 1, True)) + dp[(i, bought)] = res + return res + + return rec(0, False) +``` + +```java +public class Solution { + public int maxProfit(int[] prices) { + int n = prices.length; + int[][] dp = new int[n][2]; + for (int i = 0; i < n; i++) { + dp[i][0] = -1; + dp[i][1] = -1; + } + return rec(prices, 0, 0, dp); + } + + private int rec(int[] prices, int i, int bought, int[][] dp) { + if (i == prices.length) { + return 0; + } + if (dp[i][bought] != -1) { + return dp[i][bought]; + } + int res = rec(prices, i + 1, bought, dp); + if (bought == 1) { + res = Math.max(res, prices[i] + rec(prices, i + 1, 0, dp)); + } else { + res = Math.max(res, -prices[i] + rec(prices, i + 1, 1, dp)); + } + dp[i][bought] = res; + return res; + } +} +``` + +```cpp +class Solution { +public: + int maxProfit(vector& prices) { + int n = prices.size(); + vector> dp(n, vector(2, -1)); + return rec(prices, 0, 0, dp); + } + +private: + int rec(vector& prices, int i, int bought, vector>& dp) { + if (i == prices.size()) { + return 0; + } + if (dp[i][bought] != -1) { + return dp[i][bought]; + } + int res = rec(prices, i + 1, bought, dp); + if (bought == 1) { + res = max(res, prices[i] + rec(prices, i + 1, 0, dp)); + } else { + res = max(res, -prices[i] + rec(prices, i + 1, 1, dp)); + } + dp[i][bought] = res; + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} prices + * @return {number} + */ + maxProfit(prices) { + const n = prices.length; + const dp = Array.from({ length: n }, () => Array(2).fill(-1)); + + const rec = (i, bought) => { + if (i === n) { + return 0; + } + if (dp[i][bought] !== -1) { + return dp[i][bought]; + } + let res = rec(i + 1, bought); + if (bought) { + res = Math.max(res, prices[i] + rec(i + 1, 0)); + } else { + res = Math.max(res, -prices[i] + rec(i + 1, 1)); + } + dp[i][bought] = res; + return res; + }; + + return rec(0, 0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def maxProfit(self, prices: List[int]) -> int: + n = len(prices) + dp = [[0] * 2 for _ in range(n + 1)] + + for i in range(n - 1, -1, -1): + dp[i][0] = max(dp[i + 1][0], -prices[i] + dp[i + 1][1]) + dp[i][1] = max(dp[i + 1][1], prices[i] + dp[i + 1][0]) + + return dp[0][0] +``` + +```java +public class Solution { + public int maxProfit(int[] prices) { + int n = prices.length; + int[][] dp = new int[n + 1][2]; + + for (int i = n - 1; i >= 0; i--) { + dp[i][0] = Math.max(dp[i + 1][0], -prices[i] + dp[i + 1][1]); + dp[i][1] = Math.max(dp[i + 1][1], prices[i] + dp[i + 1][0]); + } + + return dp[0][0]; + } +} +``` + +```cpp +class Solution { +public: + int maxProfit(vector& prices) { + int n = prices.size(); + vector> dp(n + 1, vector(2, 0)); + + for (int i = n - 1; i >= 0; i--) { + dp[i][0] = max(dp[i + 1][0], -prices[i] + dp[i + 1][1]); + dp[i][1] = max(dp[i + 1][1], prices[i] + dp[i + 1][0]); + } + + return dp[0][0]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} prices + * @return {number} + */ + maxProfit(prices) { + const n = prices.length; + const dp = Array.from({ length: n + 1 }, () => Array(2).fill(0)); + + for (let i = n - 1; i >= 0; i--) { + dp[i][0] = Math.max(dp[i + 1][0], -prices[i] + dp[i + 1][1]); + dp[i][1] = Math.max(dp[i + 1][1], prices[i] + dp[i + 1][0]); + } + + return dp[0][0]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 4. Dynamic Programming (Space Optimized) + +::tabs-start + +```python +class Solution: + def maxProfit(self, prices: List[int]) -> int: + n = len(prices) + next_buy = next_sell = 0 + cur_buy = cur_sell = 0 + + for i in range(n - 1, -1, -1): + cur_buy = max(next_buy, -prices[i] + next_sell) + cur_sell = max(next_sell, prices[i] + next_buy) + next_buy = cur_buy + next_sell = cur_sell + + return cur_buy +``` + +```java +public class Solution { + public int maxProfit(int[] prices) { + int nextBuy = 0, nextSell = 0; + int curBuy = 0, curSell = 0; + + for (int i = prices.length - 1; i >= 0; i--) { + curBuy = Math.max(nextBuy, -prices[i] + nextSell); + curSell = Math.max(nextSell, prices[i] + nextBuy); + nextBuy = curBuy; + nextSell = curSell; + } + + return curBuy; + } +} +``` + +```cpp +class Solution { +public: + int maxProfit(vector& prices) { + int nextBuy = 0, nextSell = 0; + int curBuy = 0, curSell = 0; + + for (int i = prices.size() - 1; i >= 0; i--) { + curBuy = max(nextBuy, -prices[i] + nextSell); + curSell = max(nextSell, prices[i] + nextBuy); + nextBuy = curBuy; + nextSell = curSell; + } + + return curBuy; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} prices + * @return {number} + */ + maxProfit(prices) { + let nextBuy = 0, nextSell = 0; + let curBuy = 0, curSell = 0; + + for (let i = prices.length - 1; i >= 0; i--) { + curBuy = Math.max(nextBuy, -prices[i] + nextSell); + curSell = Math.max(nextSell, prices[i] + nextBuy); + nextBuy = curBuy; + nextSell = curSell; + } + + return curBuy; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ + +--- + +## 5. Greedy + +::tabs-start + +```python +class Solution: + def maxProfit(self, prices: List[int]) -> int: + profit = 0 + + for i in range(1, len(prices)): + if prices[i] > prices[i - 1]: + profit += (prices[i] - prices[i - 1]) + + return profit +``` + +```java +public class Solution { + public int maxProfit(int[] prices) { + int profit = 0; + for (int i = 1; i < prices.length; i++) { + if (prices[i] > prices[i - 1]) { + profit += (prices[i] - prices[i - 1]); + } + } + return profit; + } +} +``` + +```cpp +class Solution { +public: + int maxProfit(vector& prices) { + int profit = 0; + for (int i = 1; i < prices.size(); i++) { + if (prices[i] > prices[i - 1]) { + profit += (prices[i] - prices[i - 1]); + } + } + return profit; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} prices + * @return {number} + */ + maxProfit(prices) { + let profit = 0; + for (let i = 1; i < prices.length; i++) { + if (prices[i] > prices[i - 1]) { + profit += (prices[i] - prices[i - 1]); + } + } + return profit; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/brick-wall.md b/articles/brick-wall.md new file mode 100644 index 000000000..991f98ce3 --- /dev/null +++ b/articles/brick-wall.md @@ -0,0 +1,245 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def leastBricks(self, wall: List[List[int]]) -> int: + n = len(wall) + m = 0 + for brick in wall[0]: + m += brick + + gaps = [[] for _ in range(n)] + for i in range(n): + gap = 0 + for brick in wall[i]: + gap += brick + gaps[i].append(gap) + + res = n + for line in range(1, m): + cuts = 0 + for i in range(n): + if line not in gaps[i]: + cuts += 1 + + res = min(res, cuts) + return res +``` + +```java +public class Solution { + public int leastBricks(List> wall) { + int n = wall.size(); + int m = 0; + for (int brick : wall.get(0)) { + m += brick; + } + + List> gaps = new ArrayList<>(); + for (int i = 0; i < n; i++) { + gaps.add(new ArrayList<>()); + int gap = 0; + for (int brick : wall.get(i)) { + gap += brick; + gaps.get(i).add(gap); + } + } + + int res = n; + for (int line = 1; line < m; line++) { + int cuts = 0; + for (int i = 0; i < n; i++) { + if (!gaps.get(i).contains(line)) { + cuts++; + } + } + res = Math.min(res, cuts); + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int leastBricks(vector>& wall) { + int n = wall.size(); + int m = 0; + for (int brick : wall[0]) { + m += brick; + } + + vector> gaps(n); + for (int i = 0; i < n; i++) { + int gap = 0; + for (int brick : wall[i]) { + gap += brick; + gaps[i].push_back(gap); + } + } + + int res = n; + for (int line = 1; line < m; line++) { + int cuts = 0; + for (int i = 0; i < n; i++) { + if (find(gaps[i].begin(), gaps[i].end(), line) == gaps[i].end()) { + cuts++; + } + } + res = min(res, cuts); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} wall + * @return {number} + */ + leastBricks(wall) { + const n = wall.length; + let m = 0; + for (const brick of wall[0]) { + m += brick; + } + + const gaps = Array.from({ length: n }, () => []); + for (let i = 0; i < n; i++) { + let gap = 0; + for (const brick of wall[i]) { + gap += brick; + gaps[i].push(gap); + } + } + + let res = n; + for (let line = 1; line < m; line++) { + let cuts = 0; + for (let i = 0; i < n; i++) { + if (!gaps[i].includes(line)) { + cuts++; + } + } + res = Math.min(res, cuts); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n * g)$ +* Space complexity: $O(n * g)$ + +> Where $m$ is the sum of widths of the bricks in the first row, $n$ is the number of rows and $g$ is the average number of gaps in each row. + +--- + +## 2. Hash Map + +::tabs-start + +```python +class Solution: + def leastBricks(self, wall: List[List[int]]) -> int: + countGap = {0: 0} + + for r in wall: + total = 0 + for i in range(len(r) - 1): + total += r[i] + countGap[total] = 1 + countGap.get(total, 0) + + return len(wall) - max(countGap.values()) +``` + +```java +public class Solution { + public int leastBricks(List> wall) { + HashMap countGap = new HashMap<>(); + countGap.put(0, 0); + + for (List row : wall) { + int total = 0; + for (int i = 0; i < row.size() - 1; i++) { + total += row.get(i); + countGap.put(total, countGap.getOrDefault(total, 0) + 1); + } + } + + int maxGaps = 0; + for (int count : countGap.values()) { + maxGaps = Math.max(maxGaps, count); + } + + return wall.size() - maxGaps; + } +} +``` + +```cpp +class Solution { +public: + int leastBricks(vector>& wall) { + unordered_map countGap; + countGap[0] = 0; + + for (const auto& row : wall) { + int total = 0; + for (size_t i = 0; i < row.size() - 1; ++i) { + total += row[i]; + countGap[total]++; + } + } + + int maxGaps = 0; + for (const auto& [key, value] : countGap) { + maxGaps = max(maxGaps, value); + } + + return wall.size() - maxGaps; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} wall + * @return {number} + */ + leastBricks(wall) { + const countGap = new Map(); + countGap.set(0, 0); + for (const row of wall) { + let total = 0; + for (let i = 0; i < row.length - 1; i++) { + total += row[i]; + countGap.set(total, (countGap.get(total) || 0) + 1); + } + } + return wall.length - Math.max(...countGap.values()); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(N)$ +* Space complexity: $O(g)$ + +> Where $N$ is the total number of bricks in the wall and $g$ is the total number of gaps in all the rows. \ No newline at end of file diff --git a/articles/continuous-subarray-sum.md b/articles/continuous-subarray-sum.md new file mode 100644 index 000000000..a6509f498 --- /dev/null +++ b/articles/continuous-subarray-sum.md @@ -0,0 +1,177 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def checkSubarraySum(self, nums: List[int], k: int) -> bool: + for i in range(len(nums) - 1): + sum = nums[i] + for j in range(i + 1, len(nums)): + sum += nums[j] + if sum % k == 0: + return True + return False +``` + +```java +public class Solution { + public boolean checkSubarraySum(int[] nums, int k) { + for (int i = 0; i < nums.length - 1; i++) { + int sum = nums[i]; + for (int j = i + 1; j < nums.length; j++) { + sum += nums[j]; + if (sum % k == 0) return true; + } + } + return false; + } +} +``` + +```cpp +class Solution { +public: + bool checkSubarraySum(vector& nums, int k) { + for (int i = 0; i < nums.size() - 1; i++) { + int sum = nums[i]; + for (int j = i + 1; j < nums.size(); j++) { + sum += nums[j]; + if (sum % k == 0) return true; + } + } + return false; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {boolean} + */ + checkSubarraySum(nums, k) { + for (let i = 0; i < nums.length - 1; i++) { + let sum = nums[i]; + for (let j = i + 1; j < nums.length; j++) { + sum += nums[j]; + if (sum % k == 0) return true; + } + } + return false; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(1)$ + +--- + +## 2. Prefix SUm + Hash Map + +::tabs-start + +```python +class Solution: + def checkSubarraySum(self, nums: List[int], k: int) -> bool: + remainder = {0: -1} # remainder -> end index + total = 0 + + for i, num in enumerate(nums): + total += num + r = total % k + if r not in remainder: + remainder[r] = i + elif i - remainder[r] > 1: + return True + + return False +``` + +```java +public class Solution { + public boolean checkSubarraySum(int[] nums, int k) { + HashMap remainder = new HashMap<>(); + remainder.put(0, -1); + int total = 0; + + for (int i = 0; i < nums.length; i++) { + total += nums[i]; + int r = total % k; + if (!remainder.containsKey(r)) { + remainder.put(r, i); + } else if (i - remainder.get(r) > 1) { + return true; + } + } + + return false; + } +} +``` + +```cpp +class Solution { +public: + bool checkSubarraySum(vector& nums, int k) { + unordered_map remainder; + remainder[0] = -1; + int total = 0; + + for (int i = 0; i < nums.size(); i++) { + total += nums[i]; + int r = total % k; + if (remainder.find(r) == remainder.end()) { + remainder[r] = i; + } else if (i - remainder[r] > 1) { + return true; + } + } + + return false; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {boolean} + */ + checkSubarraySum(nums, k) { + const remainder = new Map(); + remainder.set(0, -1); + let total = 0; + + for (let i = 0; i < nums.length; i++) { + total += nums[i]; + let r = total % k; + if (!remainder.has(r)) { + remainder.set(r, i); + } else if (i - remainder.get(r) > 1) { + return true; + } + } + + return false; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(k)$ + +> Where $n$ is the size of the array $nums$ and $k$ is the number that a subarray sum needs to be multiple of. \ No newline at end of file diff --git a/articles/design-hashmap.md b/articles/design-hashmap.md new file mode 100644 index 000000000..4a64bdee1 --- /dev/null +++ b/articles/design-hashmap.md @@ -0,0 +1,365 @@ +## 1. Array + +::tabs-start + +```python +class MyHashMap: + + def __init__(self): + self.map = [-1] * 1000001 + + def put(self, key: int, value: int) -> None: + self.map[key] = value + + def get(self, key: int) -> int: + return self.map[key] + + def remove(self, key: int) -> None: + self.map[key] = -1 +``` + +```java +public class MyHashMap { + private int[] map; + + public MyHashMap() { + map = new int[1000001]; + Arrays.fill(map, -1); + } + + public void put(int key, int value) { + map[key] = value; + } + + public int get(int key) { + return map[key]; + } + + public void remove(int key) { + map[key] = -1; + } +} +``` + +```cpp +class MyHashMap { +private: + vector map; + +public: + MyHashMap() : map(1000001, -1) {} + + void put(int key, int value) { + map[key] = value; + } + + int get(int key) { + return map[key]; + } + + void remove(int key) { + map[key] = -1; + } +}; +``` + +```javascript +class MyHashMap { + constructor() { + this.map = new Array(1000001).fill(-1); + } + + /** + * @param {number} key + * @param {number} value + * @return {void} + */ + put(key, value) { + this.map[key] = value; + } + + /** + * @param {number} key + * @return {number} + */ + get(key) { + return this.map[key]; + } + + /** + * @param {number} key + * @return {void} + */ + remove(key) { + this.map[key] = -1; + } +} + +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(1)$ for each function call. +* Space complexity: $O(1000000)$ since the key is in the range $[0, 1000000]$. + +--- + +## 2. Linked List + +::tabs-start + +```python +class ListNode: + def __init__(self, key = -1, val = -1, next = None): + self.key = key + self.val = val + self.next = next + +class MyHashMap: + + def __init__(self): + self.map = [ListNode() for _ in range(1000)] + + def hash(self, key: int) -> int: + return key % len(self.map) + + def put(self, key: int, value: int) -> None: + cur = self.map[self.hash(key)] + while cur.next: + if cur.next.key == key: + cur.next.val = value + return + cur = cur.next + cur.next = ListNode(key, value) + + def get(self, key: int) -> int: + cur = self.map[self.hash(key)].next + while cur: + if cur.key == key: + return cur.val + cur = cur.next + return -1 + + def remove(self, key: int) -> None: + cur = self.map[self.hash(key)] + while cur.next: + if cur.next.key == key: + cur.next = cur.next.next + return + cur = cur.next +``` + +```java +class ListNode { + int key, val; + ListNode next; + + public ListNode(int key, int val, ListNode next) { + this.key = key; + this.val = val; + this.next = next; + } + + public ListNode() { + this(-1, -1, null); + } +} + +public class MyHashMap { + private ListNode[] map; + + public MyHashMap() { + map = new ListNode[1000]; + for (int i = 0; i < 1000; i++) { + map[i] = new ListNode(); + } + } + + private int hash(int key) { + return key % map.length; + } + + public void put(int key, int value) { + ListNode cur = map[hash(key)]; + while (cur.next != null) { + if (cur.next.key == key) { + cur.next.val = value; + return; + } + cur = cur.next; + } + cur.next = new ListNode(key, value, null); + } + + public int get(int key) { + ListNode cur = map[hash(key)].next; + while (cur != null) { + if (cur.key == key) { + return cur.val; + } + cur = cur.next; + } + return -1; + } + + public void remove(int key) { + ListNode cur = map[hash(key)]; + while (cur.next != null) { + if (cur.next.key == key) { + cur.next = cur.next.next; + return; + } + cur = cur.next; + } + } +} +``` + +```cpp +class MyHashMap { +private: + struct ListNode { + int key, val; + ListNode* next; + + ListNode(int key = -1, int val = -1, ListNode* next = nullptr) + : key(key), val(val), next(next) {} + }; + + vector map; + int hash(int key) { + return key % map.size(); + } + +public: + MyHashMap() { + map.resize(1000); + for (auto& bucket : map) { + bucket = new ListNode(0); + } + } + + void put(int key, int value) { + ListNode* cur = map[hash(key)]; + while (cur->next) { + if (cur->next->key == key) { + cur->next->val = value; + return; + } + cur = cur->next; + } + cur->next = new ListNode(key, value); + } + + int get(int key) { + ListNode* cur = map[hash(key)]->next; + while (cur) { + if (cur->key == key) { + return cur->val; + } + cur = cur->next; + } + return -1; + } + + void remove(int key) { + ListNode* cur = map[hash(key)]; + while (cur->next) { + if (cur->next->key == key) { + ListNode* tmp = cur->next; + cur->next = cur->next->next; + delete tmp; + return; + } + cur = cur->next; + } + } +}; +``` + +```javascript +class ListNode { + /** + * @param {number} key + * @param {number} val + * @param {ListNode} next + */ + constructor(key = -1, val = -1, next = null) { + this.key = key; + this.val = val; + this.next = next; + } +} + +class MyHashMap { + constructor() { + this.map = Array.from({ length: 1000 }, () => new ListNode()); + } + + /** + * @param {number} key + * @return {number} + */ + hash(key) { + return key % this.map.length; + } + + /** + * @param {number} key + * @param {number} value + * @return {void} + */ + put(key, value) { + let cur = this.map[this.hash(key)]; + while (cur.next) { + if (cur.next.key === key) { + cur.next.val = value; + return; + } + cur = cur.next; + } + cur.next = new ListNode(key, value); + } + + /** + * @param {number} key + * @return {number} + */ + get(key) { + let cur = this.map[this.hash(key)].next; + while (cur) { + if (cur.key === key) { + return cur.val; + } + cur = cur.next; + } + return -1; + } + + /** + * @param {number} key + * @return {void} + */ + remove(key) { + let cur = this.map[this.hash(key)]; + while (cur.next) { + if (cur.next.key === key) { + cur.next = cur.next.next; + return; + } + cur = cur.next; + } + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(\frac{n}{k})$ for each function call. +* Space complexity: $O(k + m)$ + +> Where $n$ is the number of keys, $k$ is the size of the map ($1000$) and $m$ is the number of unique keys. \ No newline at end of file diff --git a/articles/design-hashset.md b/articles/design-hashset.md new file mode 100644 index 000000000..9ee3f0884 --- /dev/null +++ b/articles/design-hashset.md @@ -0,0 +1,1028 @@ +## 1. Brute Force + +::tabs-start + +```python +class MyHashSet: + + def __init__(self): + self.data = [] + + def add(self, key: int) -> None: + if key not in self.data: + self.data.append(key) + + def remove(self, key: int) -> None: + if key in self.data: + self.data.remove(key) + + def contains(self, key: int) -> bool: + return key in self.data +``` + +```java +public class MyHashSet { + private List data; + + public MyHashSet() { + data = new ArrayList<>(); + } + + public void add(int key) { + if (!data.contains(key)) { + data.add(key); + } + } + + public void remove(int key) { + data.remove(Integer.valueOf(key)); + } + + public boolean contains(int key) { + return data.contains(key); + } +} +``` + +```cpp +class MyHashSet { +private: + vector data; +public: + MyHashSet() {} + + void add(int key) { + if (find(data.begin(), data.end(), key) == data.end()) { + data.push_back(key); + } + } + + void remove(int key) { + auto it = find(data.begin(), data.end(), key); + if (it != data.end()) { + data.erase(it); + } + } + + bool contains(int key) { + return find(data.begin(), data.end(), key) != data.end(); + } +}; +``` + +```javascript +class MyHashSet { + constructor() { + this.data = []; + } + + /** + * @param {number} key + * @return {void} + */ + add(key) { + if (!this.data.includes(key)) { + this.data.push(key); + } + } + + /** + * @param {number} key + * @return {void} + */ + remove(key) { + const index = this.data.indexOf(key); + if (index !== -1) { + this.data.splice(index, 1); + } + } + + /** + * @param {number} key + * @return {boolean} + */ + contains(key) { + return this.data.includes(key); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ for each function call. +* Space complexity: $O(n)$ + +--- + +## 2. Boolean Array + +::tabs-start + +```python +class MyHashSet: + + def __init__(self): + self.data = [False] * 1000001 + + def add(self, key: int) -> None: + self.data[key] = True + + def remove(self, key: int) -> None: + self.data[key] = False + + def contains(self, key: int) -> bool: + return self.data[key] +``` + +```java +public class MyHashSet { + private boolean[] data; + + public MyHashSet() { + data = new boolean[1000001]; + } + + public void add(int key) { + data[key] = true; + } + + public void remove(int key) { + data[key] = false; + } + + public boolean contains(int key) { + return data[key]; + } +} +``` + +```cpp +class MyHashSet { +private: + vector data; +public: + MyHashSet() : data(1000001, false) {} + + void add(int key) { + data[key] = true; + } + + void remove(int key) { + data[key] = false; + } + + bool contains(int key) { + return data[key]; + } +}; +``` + +```javascript +class MyHashSet { + constructor() { + this.data = new Array(1000001).fill(false); + } + + /** + * @param {number} key + * @return {void} + */ + add(key) { + this.data[key] = true; + } + + /** + * @param {number} key + * @return {void} + */ + remove(key) { + this.data[key] = false; + } + + /** + * @param {number} key + * @return {boolean} + */ + contains(key) { + return this.data[key]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(1)$ for each function call. +* Space complexity: $O(1000000)$ since the key is in the range $[0, 1000000]$. + +--- + +## 3. Linked List + +::tabs-start + +```python +class ListNode: + def __init__(self, key: int): + self.key = key + self.next = None + +class MyHashSet: + + def __init__(self): + self.set = [ListNode(0) for _ in range(10**4)] + + def add(self, key: int) -> None: + cur = self.set[key % len(self.set)] + while cur.next: + if cur.next.key == key: + return + cur = cur.next + cur.next = ListNode(key) + + def remove(self, key: int) -> None: + cur = self.set[key % len(self.set)] + while cur.next: + if cur.next.key == key: + cur.next = cur.next.next + return + cur = cur.next + + def contains(self, key: int) -> bool: + cur = self.set[key % len(self.set)] + while cur.next: + if cur.next.key == key: + return True + cur = cur.next + return False +``` + +```java +public class MyHashSet { + + private static class ListNode { + int key; + ListNode next; + + ListNode(int key) { + this.key = key; + } + } + + private final ListNode[] set; + + public MyHashSet() { + set = new ListNode[10000]; + for (int i = 0; i < set.length; i++) { + set[i] = new ListNode(0); + } + } + + public void add(int key) { + ListNode cur = set[key % set.length]; + while (cur.next != null) { + if (cur.next.key == key) { + return; + } + cur = cur.next; + } + cur.next = new ListNode(key); + } + + public void remove(int key) { + ListNode cur = set[key % set.length]; + while (cur.next != null) { + if (cur.next.key == key) { + cur.next = cur.next.next; + return; + } + cur = cur.next; + } + } + + public boolean contains(int key) { + ListNode cur = set[key % set.length]; + while (cur.next != null) { + if (cur.next.key == key) { + return true; + } + cur = cur.next; + } + return false; + } +} +``` + +```cpp +class MyHashSet { +private: + struct ListNode { + int key; + ListNode* next; + ListNode(int k) : key(k), next(nullptr) {} + }; + + vector set; + + int hash(int key) { + return key % set.size(); + } + +public: + MyHashSet() { + set.resize(10000); + for (auto& bucket : set) { + bucket = new ListNode(0); + } + } + + void add(int key) { + ListNode* cur = set[hash(key)]; + while (cur->next) { + if (cur->next->key == key) { + return; + } + cur = cur->next; + } + cur->next = new ListNode(key); + } + + void remove(int key) { + ListNode* cur = set[hash(key)]; + while (cur->next) { + if (cur->next->key == key) { + ListNode* temp = cur->next; + cur->next = temp->next; + delete temp; + return; + } + cur = cur->next; + } + } + + bool contains(int key) { + ListNode* cur = set[hash(key)]; + while (cur->next) { + if (cur->next->key == key) { + return true; + } + cur = cur->next; + } + return false; + } +}; +``` + +```javascript +class ListNode { + /** + * @param {number} key + */ + constructor(key) { + this.key = key; + this.next = null; + } +} + +class MyHashSet { + constructor() { + this.set = Array.from({ length: 10000 }, () => new ListNode(0)); + } + + /** + * @param {number} key + * @return {number} + */ + hash(key) { + return key % this.set.length; + } + + /** + * @param {number} key + * @return {void} + */ + add(key) { + let cur = this.set[this.hash(key)]; + while (cur.next) { + if (cur.next.key === key) { + return; + } + cur = cur.next; + } + cur.next = new ListNode(key); + } + + /** + * @param {number} key + * @return {void} + */ + remove(key) { + let cur = this.set[this.hash(key)]; + while (cur.next) { + if (cur.next.key === key) { + cur.next = cur.next.next; + return; + } + cur = cur.next; + } + } + + /** + * @param {number} key + * @return {boolean} + */ + contains(key) { + let cur = this.set[this.hash(key)]; + while (cur.next) { + if (cur.next.key === key) { + return true; + } + cur = cur.next; + } + return false; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(\frac{n}{k})$ for each function call. +* Space complexity: $O(k + m)$ + +> Where $n$ is the number of keys, $k$ is the size of the set ($10000$) and $m$ is the number of unique keys. + +--- + +## 4. Binary Search Tree + +::tabs-start + +```python +class TreeNode: + def __init__(self, key): + self.key = key + self.left = None + self.right = None + +class BST: + def __init__(self): + self.root = None + + def insert(self, root, key): + if not root: + return TreeNode(key) + if key < root.key: + root.left = self.insert(root.left, key) + elif key > root.key: + root.right = self.insert(root.right, key) + return root + + def delete(self, root, key): + if not root: + return None + if key < root.key: + root.left = self.delete(root.left, key) + elif key > root.key: + root.right = self.delete(root.right, key) + else: + if not root.left: + return root.right + if not root.right: + return root.left + temp = self.minValueNode(root.right) + root.key = temp.key + root.right = self.delete(root.right, temp.key) + return root + + def minValueNode(self, root): + while root.left: + root = root.left + return root + + def search(self, root, key): + if not root: + return False + if key == root.key: + return True + elif key < root.key: + return self.search(root.left, key) + else: + return self.search(root.right, key) + + def add(self, key): + self.root = self.insert(self.root, key) + + def remove(self, key): + self.root = self.delete(self.root, key) + + def contains(self, key): + return self.search(self.root, key) + +class MyHashSet: + def __init__(self): + self.size = 10000 + self.buckets = [BST() for _ in range(self.size)] + + def _hash(self, key): + return key % self.size + + def add(self, key: int) -> None: + idx = self._hash(key) + if not self.contains(key): + self.buckets[idx].add(key) + + def remove(self, key: int) -> None: + idx = self._hash(key) + self.buckets[idx].remove(key) + + def contains(self, key: int) -> bool: + idx = self._hash(key) + return self.buckets[idx].contains(key) +``` + +```java +class TreeNode { + int key; + TreeNode left, right; + + TreeNode(int key) { + this.key = key; + } +} + +class BST { + private TreeNode root; + + private TreeNode insert(TreeNode node, int key) { + if (node == null) return new TreeNode(key); + if (key < node.key) node.left = insert(node.left, key); + else if (key > node.key) node.right = insert(node.right, key); + return node; + } + + private TreeNode delete(TreeNode node, int key) { + if (node == null) return null; + if (key < node.key) node.left = delete(node.left, key); + else if (key > node.key) node.right = delete(node.right, key); + else { + if (node.left == null) return node.right; + if (node.right == null) return node.left; + TreeNode temp = minValueNode(node.right); + node.key = temp.key; + node.right = delete(node.right, temp.key); + } + return node; + } + + private TreeNode minValueNode(TreeNode node) { + while (node.left != null) node = node.left; + return node; + } + + private boolean search(TreeNode node, int key) { + if (node == null) return false; + if (key == node.key) return true; + return key < node.key ? search(node.left, key) : search(node.right, key); + } + + public void add(int key) { + root = insert(root, key); + } + + public void remove(int key) { + root = delete(root, key); + } + + public boolean contains(int key) { + return search(root, key); + } +} + +public class MyHashSet { + private final int size = 10000; + private BST[] buckets; + + public MyHashSet() { + buckets = new BST[size]; + for (int i = 0; i < size; i++) { + buckets[i] = new BST(); + } + } + + private int hash(int key) { + return key % size; + } + + public void add(int key) { + int idx = hash(key); + if (!buckets[idx].contains(key)) { + buckets[idx].add(key); + } + } + + public void remove(int key) { + int idx = hash(key); + buckets[idx].remove(key); + } + + public boolean contains(int key) { + int idx = hash(key); + return buckets[idx].contains(key); + } +} +``` + +```cpp +class BST { +private: + struct TreeNode { + int key; + TreeNode* left; + TreeNode* right; + TreeNode(int k) : key(k), left(nullptr), right(nullptr) {} + }; + + TreeNode* insert(TreeNode* root, int key) { + if (!root) return new TreeNode(key); + if (key < root->key) + root->left = insert(root->left, key); + else if (key > root->key) + root->right = insert(root->right, key); + return root; + } + + TreeNode* deleteNode(TreeNode* root, int key) { + if (!root) return nullptr; + if (key < root->key) + root->left = deleteNode(root->left, key); + else if (key > root->key) + root->right = deleteNode(root->right, key); + else { + if (!root->left) { + TreeNode* temp = root->right; + delete root; + return temp; + } + if (!root->right) { + TreeNode* temp = root->left; + delete root; + return temp; + } + TreeNode* temp = minValueNode(root->right); + root->key = temp->key; + root->right = deleteNode(root->right, temp->key); + } + return root; + } + + TreeNode* minValueNode(TreeNode* root) { + while (root->left) root = root->left; + return root; + } + + bool search(TreeNode* root, int key) { + if (!root) return false; + if (key == root->key) return true; + return key < root->key ? search(root->left, key) : search(root->right, key); + } + + TreeNode* root; + +public: + BST() : root(nullptr) {} + + void add(int key) { + root = insert(root, key); + } + + void remove(int key) { + root = deleteNode(root, key); + } + + bool contains(int key) { + return search(root, key); + } +}; + +class MyHashSet { +private: + const int size = 10000; + vector buckets; + + int hash(int key) { + return key % size; + } + +public: + MyHashSet() : buckets(size) {} + + void add(int key) { + int idx = hash(key); + if (!contains(key)) { + buckets[idx].add(key); + } + } + + void remove(int key) { + int idx = hash(key); + buckets[idx].remove(key); + } + + bool contains(int key) { + int idx = hash(key); + return buckets[idx].contains(key); + } +}; +``` + +```javascript +class TreeNode { + /** + * @param {number} key + */ + constructor(key) { + this.key = key; + this.left = null; + this.right = null; + } +} + +class BST { + constructor() { + this.root = null; + } + + /** + * @param {number} key + * @return {void} + */ + add(key) { + this.root = this._insert(this.root, key); + } + + /** + * @param {TreeNode} node + * @param {number} key + * @return {TreeNode} + */ + _insert(node, key) { + if (!node) return new TreeNode(key); + if (key < node.key) node.left = this._insert(node.left, key); + else if (key > node.key) node.right = this._insert(node.right, key); + return node; + } + + /** + * @param {number} key + * @return {void} + */ + remove(key) { + this.root = this._deleteNode(this.root, key); + } + + /** + * @param {TreeNode} node + * @param {number} key + * @return {TreeNode} + */ + _deleteNode(node, key) { + if (!node) return null; + if (key < node.key) node.left = this._deleteNode(node.left, key); + else if (key > node.key) node.right = this._deleteNode(node.right, key); + else { + if (!node.left) return node.right; + if (!node.right) return node.left; + let minNode = this._minValueNode(node.right); + node.key = minNode.key; + node.right = this._deleteNode(node.right, minNode.key); + } + return node; + } + + /** + * @param {TreeNode} node + * @return {TreeNode} + */ + _minValueNode(node) { + while (node.left) node = node.left; + return node; + } + + /** + * @param {number} key + * @return {boolean} + */ + contains(key) { + return this._search(this.root, key); + } + + /** + * @param {TreeNode} node + * @param {number} key + * @return {boolean} + */ + _search(node, key) { + if (!node) return false; + if (key === node.key) return true; + if (key < node.key) return this._search(node.left, key); + return this._search(node.right, key); + } +} + +class MyHashSet { + constructor() { + this.size = 10000; + this.buckets = Array.from({ length: this.size }, () => new BST()); + } + + _hash(key) { + return key % this.size; + } + + /** + * @param {number} key + * @return {void} + */ + add(key) { + const idx = this._hash(key); + if (!this.buckets[idx].contains(key)) { + this.buckets[idx].add(key); + } + } + + /** + * @param {number} key + * @return {void} + */ + remove(key) { + const idx = this._hash(key); + this.buckets[idx].remove(key); + } + + /** + * @param {number} key + * @return {boolean} + */ + contains(key) { + const idx = this._hash(key); + return this.buckets[idx].contains(key); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(\log (\frac{n}{k}))$ in average case, $O(\frac{n}{k})$ in worst case for each function call. +* Space complexity: $O(k + m)$ + +> Where $n$ is the number of keys, $k$ is the size of the set ($10000$) and $m$ is the number of unique keys. + +--- + +## 5. Bit Manipulation + +::tabs-start + +```python +class MyHashSet: + + def __init__(self): + # key is in the range [1, 1000000] + # 31251 * 32 = 1000032 + self.set = [0] * 31251 + + def add(self, key: int) -> None: + self.set[key // 32] |= self.getMask(key) + + def remove(self, key: int) -> None: + if self.contains(key): + self.set[key // 32] ^= self.getMask(key) + + def contains(self, key: int) -> bool: + return self.set[key // 32] & self.getMask(key) != 0 + + def getMask(self, key: int) -> int: + return 1 << (key % 32) +``` + +```java +public class MyHashSet { + private int[] set; + + public MyHashSet() { + // key is in the range [1, 1000000] + // 31251 * 32 = 1000032 + set = new int[31251]; + } + + public void add(int key) { + set[key / 32] |= getMask(key); + } + + public void remove(int key) { + if (contains(key)) { + set[key / 32] ^= getMask(key); + } + } + + public boolean contains(int key) { + return (set[key / 32] & getMask(key)) != 0; + } + + private int getMask(int key) { + return 1 << (key % 32); + } +} +``` + +```cpp +class MyHashSet { +private: + int set[31251]; + + int getMask(int key) { + return 1 << (key % 32); + } + +public: + MyHashSet() { + // key is in the range [1, 1000000] + // 31251 * 32 = 1000032 + memset(set, 0, sizeof(set)); + } + + void add(int key) { + set[key / 32] |= getMask(key); + } + + void remove(int key) { + if (contains(key)) { + set[key / 32] ^= getMask(key); + } + } + + bool contains(int key) { + return (set[key / 32] & getMask(key)) != 0; + } +}; +``` + +```javascript +class MyHashSet { + constructor() { + // key is in the range [1, 1000000] + // 31251 * 32 = 1000032 + this.set = new Array(31251).fill(0); + } + + /** + * @param {number} key + * @return {void} + */ + add(key) { + this.set[Math.floor(key / 32)] |= this.getMask(key); + } + + /** + * @param {number} key + * @return {void} + */ + remove(key) { + if (this.contains(key)) { + this.set[Math.floor(key / 32)] ^= this.getMask(key); + } + } + + /** + * @param {number} key + * @return {boolean} + */ + contains(key) { + return (this.set[Math.floor(key / 32)] & this.getMask(key)) !== 0; + } + + /** + * @param {number} key + * @return {number} + */ + getMask(key) { + return 1 << (key % 32); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(1)$ for each function call. +* Space complexity: $O(k)$ + +> Where $k$ is the size of the set $(31251)$. \ No newline at end of file diff --git a/articles/destination-city.md b/articles/destination-city.md new file mode 100644 index 000000000..0dc585b2b --- /dev/null +++ b/articles/destination-city.md @@ -0,0 +1,253 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def destCity(self, paths: List[List[str]]) -> str: + for i in range(len(paths)): + flag = True + for j in range(len(paths)): + if paths[i][1] == paths[j][0]: + flag = False + break + if flag: + return paths[i][1] + return "" +``` + +```java +public class Solution { + public String destCity(List> paths) { + for (int i = 0; i < paths.size(); i++) { + boolean flag = true; + for (int j = 0; j < paths.size(); j++) { + if (paths.get(i).get(1).equals(paths.get(j).get(0))) { + flag = false; + break; + } + } + if (flag) { + return paths.get(i).get(1); + } + } + return ""; + } +} +``` + +```cpp +class Solution { +public: + string destCity(vector>& paths) { + for (int i = 0; i < paths.size(); i++) { + bool flag = true; + for (int j = 0; j < paths.size(); j++) { + if (paths[i][1] == paths[j][0]) { + flag = false; + break; + } + } + if (flag) { + return paths[i][1]; + } + } + return ""; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[][]} paths + * @return {string} + */ + destCity(paths) { + for (let i = 0; i < paths.length; i++) { + let flag = true; + for (let j = 0; j < paths.length; j++) { + if (paths[i][1] === paths[j][0]) { + flag = false; + break; + } + } + if (flag) { + return paths[i][1]; + } + } + return ""; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(1)$ + +--- + +## 2. Hash Set + +::tabs-start + +```python +class Solution: + def destCity(self, paths: List[List[str]]) -> str: + s = set() + for p in paths: + s.add(p[0]) + + for p in paths: + if p[1] not in s: + return p[1] +``` + +```java +public class Solution { + public String destCity(List> paths) { + Set s = new HashSet<>(); + for (List p : paths) { + s.add(p.get(0)); + } + + for (List p : paths) { + if (!s.contains(p.get(1))) { + return p.get(1); + } + } + return ""; + } +} +``` + +```cpp +class Solution { +public: + string destCity(vector>& paths) { + unordered_set s; + for (auto& p : paths) { + s.insert(p[0]); + } + + for (auto& p : paths) { + if (s.find(p[1]) == s.end()) { + return p[1]; + } + } + return ""; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[][]} paths + * @return {string} + */ + destCity(paths) { + const s = new Set(); + for (const p of paths) { + s.add(p[0]); + } + + for (const p of paths) { + if (!s.has(p[1])) { + return p[1]; + } + } + return ""; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Hash Map + +::tabs-start + +```python +class Solution: + def destCity(self, paths: List[List[str]]) -> str: + mp = {p[0]: p[1] for p in paths} + + start = paths[0][0] + while start in mp: + start = mp[start] + return start +``` + +```java +public class Solution { + public String destCity(List> paths) { + Map mp = new HashMap<>(); + for (List p : paths) { + mp.put(p.get(0), p.get(1)); + } + + String start = paths.get(0).get(0); + while (mp.containsKey(start)) { + start = mp.get(start); + } + return start; + } +} +``` + +```cpp +class Solution { +public: + string destCity(vector>& paths) { + unordered_map mp; + for (auto& p : paths) { + mp[p[0]] = p[1]; + } + + string start = paths[0][0]; + while (mp.find(start) != mp.end()) { + start = mp[start]; + } + return start; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[][]} paths + * @return {string} + */ + destCity(paths) { + const mp = new Map(); + for (const p of paths) { + mp.set(p[0], p[1]); + } + + let start = paths[0][0]; + while (mp.has(start)) { + start = mp.get(start); + } + return start; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/find-all-anagrams-in-a-string.md b/articles/find-all-anagrams-in-a-string.md new file mode 100644 index 000000000..4201b94ed --- /dev/null +++ b/articles/find-all-anagrams-in-a-string.md @@ -0,0 +1,581 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def findAnagrams(self, s: str, p: str) -> List[int]: + n, m = len(s), len(p) + p = sorted(p) + res = [] + for i in range(n - m + 1): + sub = sorted(s[i : i + m]) + if sub == p: + res.append(i) + return res +``` + +```java +public class Solution { + public List findAnagrams(String s, String p) { + int n = s.length(), m = p.length(); + List res = new ArrayList<>(); + char[] pArr = p.toCharArray(); + Arrays.sort(pArr); + String sortedP = new String(pArr); + + for (int i = 0; i <= n - m; i++) { + char[] subArr = s.substring(i, i + m).toCharArray(); + Arrays.sort(subArr); + if (new String(subArr).equals(sortedP)) { + res.add(i); + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + vector findAnagrams(string s, string p) { + int n = s.size(), m = p.size(); + vector res; + sort(p.begin(), p.end()); + + for (int i = 0; i <= n - m; i++) { + string sub = s.substr(i, m); + sort(sub.begin(), sub.end()); + if (sub == p) { + res.push_back(i); + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @param {string} p + * @return {number[]} + */ + findAnagrams(s, p) { + const n = s.length, m = p.length; + const res = []; + const sortedP = p.split('').sort().join(''); + + for (let i = 0; i <= n - m; i++) { + const sub = s.substring(i, i + m).split('').sort().join(''); + if (sub === sortedP) { + res.push(i); + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * m \log m)$ +* Space complexity: $O(m)$ + +> Where $n$ is the length of the string $s$ and $m$ is the length of the string $p$. + +--- + +## 2. Prefix Count + Sliding Window + +::tabs-start + +```python +class Solution: + def findAnagrams(self, s: str, p: str) -> List[int]: + n, m = len(s), len(p) + if m > n: + return [] + pCount = [0] * 26 + for c in p: + pCount[ord(c) - ord('a')] += 1 + + prefix = [[0] * 26 for _ in range(n + 1)] + for i in range(1, n + 1): + for j in range(26): + prefix[i][j] = prefix[i - 1][j] + prefix[i][ord(s[i - 1]) - ord('a')] += 1 + + i, j = 0, m - 1 + res = [] + while j < n: + isValid = True + for c in range(26): + if prefix[j + 1][c] - prefix[i][c] != pCount[c]: + isValid = False + break + if isValid: + res.append(i) + i += 1 + j += 1 + + return res +``` + +```java +public class Solution { + public List findAnagrams(String s, String p) { + int n = s.length(), m = p.length(); + if (m > n) return new ArrayList<>(); + + int[] pCount = new int[26]; + for (char c : p.toCharArray()) { + pCount[c - 'a']++; + } + + int[][] prefix = new int[n + 1][26]; + for (int i = 1; i <= n; i++) { + System.arraycopy(prefix[i - 1], 0, prefix[i], 0, 26); + prefix[i][s.charAt(i - 1) - 'a']++; + } + + List res = new ArrayList<>(); + int i = 0, j = m - 1; + while (j < n) { + boolean isValid = true; + for (int c = 0; c < 26; c++) { + if (prefix[j + 1][c] - prefix[i][c] != pCount[c]) { + isValid = false; + break; + } + } + if (isValid) res.add(i); + i++; + j++; + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector findAnagrams(string s, string p) { + int n = s.size(), m = p.size(); + if (m > n) return {}; + + vector pCount(26, 0); + for (char c : p) pCount[c - 'a']++; + + vector> prefix(n + 1, vector(26, 0)); + for (int i = 1; i <= n; i++) { + prefix[i] = prefix[i - 1]; + prefix[i][s[i - 1] - 'a']++; + } + + vector res; + int i = 0, j = m - 1; + while (j < n) { + bool isValid = true; + for (int c = 0; c < 26; c++) { + if (prefix[j + 1][c] - prefix[i][c] != pCount[c]) { + isValid = false; + break; + } + } + if (isValid) res.push_back(i); + i++; + j++; + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @param {string} p + * @return {number[]} + */ + findAnagrams(s, p) { + const n = s.length, m = p.length; + if (m > n) return []; + + const pCount = Array(26).fill(0); + for (const c of p) { + pCount[c.charCodeAt(0) - 97]++; + } + + const prefix = Array.from({ length: n + 1 }, () => Array(26).fill(0)); + for (let i = 1; i <= n; i++) { + for (let j = 0; j < 26; j++) { + prefix[i][j] = prefix[i - 1][j]; + } + prefix[i][s.charCodeAt(i - 1) - 97]++; + } + + const res = []; + let i = 0, j = m - 1; + while (j < n) { + let isValid = true; + for (let c = 0; c < 26; c++) { + if (prefix[j + 1][c] - prefix[i][c] !== pCount[c]) { + isValid = false; + break; + } + } + if (isValid) res.push(i); + i++; + j++; + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n + m)$ +* Space complexity: $O(n)$ + +> Where $n$ is the length of the string $s$ and $m$ is the length of the string $p$. + +--- + +## 3. Sliding Window + +::tabs-start + +```python +class Solution: + def findAnagrams(self, s: str, p: str) -> List[int]: + if len(p) > len(s): return [] + pCount, sCount = {}, {} + for i in range(len(p)): + pCount[p[i]] = 1 + pCount.get(p[i], 0) + sCount[s[i]] = 1+ sCount.get(s[i], 0) + + res = [0] if sCount == pCount else [] + l = 0 + for r in range(len(p), len(s)): + sCount[s[r]] = 1+ sCount.get(s[r], 0) + sCount[s[l]] -= 1 + + if sCount[s[l]] == 0: + sCount.pop(s[l]) + l += 1 + if sCount == pCount: + res.append(l) + return res +``` + +```java +public class Solution { + public List findAnagrams(String s, String p) { + if (p.length() > s.length()) return new ArrayList<>(); + + int[] pCount = new int[26]; + int[] sCount = new int[26]; + + for (char c : p.toCharArray()) { + pCount[c - 'a']++; + } + for (int i = 0; i < p.length(); i++) { + sCount[s.charAt(i) - 'a']++; + } + + List res = new ArrayList<>(); + if (Arrays.equals(pCount, sCount)) res.add(0); + + int l = 0; + for (int r = p.length(); r < s.length(); r++) { + sCount[s.charAt(r) - 'a']++; + sCount[s.charAt(l) - 'a']--; + l++; + if (Arrays.equals(pCount, sCount)) { + res.add(l); + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector findAnagrams(string s, string p) { + if (p.size() > s.size()) return {}; + + vector pCount(26, 0), sCount(26, 0); + for (char c : p) { + pCount[c - 'a']++; + } + for (int i = 0; i < p.size(); i++) { + sCount[s[i] - 'a']++; + } + + vector res; + if (pCount == sCount) res.push_back(0); + + int l = 0; + for (int r = p.size(); r < s.size(); r++) { + sCount[s[r] - 'a']++; + sCount[s[l] - 'a']--; + l++; + if (pCount == sCount) { + res.push_back(l); + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @param {string} p + * @return {number[]} + */ + findAnagrams(s, p) { + if (p.length > s.length) return []; + + const pCount = new Array(26).fill(0); + const sCount = new Array(26).fill(0); + + for (const char of p) { + pCount[char.charCodeAt(0) - 97]++; + } + for (let i = 0; i < p.length; i++) { + sCount[s.charCodeAt(i) - 97]++; + } + + const res = []; + if (pCount.toString() === sCount.toString()) res.push(0); + + let l = 0; + for (let r = p.length; r < s.length; r++) { + sCount[s.charCodeAt(r) - 97]++; + sCount[s.charCodeAt(l) - 97]--; + l++; + if (pCount.toString() === sCount.toString()) { + res.push(l); + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n + m)$ +* Space complexity: $O(1)$ since we have at most $26$ different characters. + +> Where $n$ is the length of the string $s$ and $m$ is the length of the string $p$. + +--- + +## 4. Sliding Window (Optimal) + +::tabs-start + +```python +class Solution: + def findAnagrams(self, s: str, p: str) -> List[int]: + n, m = len(s), len(p) + if m > n: + return [] + + pCount = [0] * 26 + sCount = [0] * 26 + for i in range(m): + pCount[ord(p[i]) - ord('a')] += 1 + sCount[ord(s[i]) - ord('a')] += 1 + + match = sum(1 for i in range(26) if pCount[i] == sCount[i]) + res = [] + if match == 26: + res.append(0) + + l = 0 + for r in range(m, n): + c = ord(s[l]) - ord('a') + if sCount[c] == pCount[c]: + match -= 1 + sCount[c] -= 1 + l += 1 + if sCount[c] == pCount[c]: + match += 1 + + c = ord(s[r]) - ord('a') + if sCount[c] == pCount[c]: + match -= 1 + sCount[c] += 1 + if sCount[c] == pCount[c]: + match += 1 + + if match == 26: + res.append(l) + + return res +``` + +```java +public class Solution { + public List findAnagrams(String s, String p) { + int n = s.length(), m = p.length(); + if (m > n) return new ArrayList<>(); + + int[] pCount = new int[26], sCount = new int[26]; + for (int i = 0; i < m; i++) { + pCount[p.charAt(i) - 'a']++; + sCount[s.charAt(i) - 'a']++; + } + + int match = 0; + for (int i = 0; i < 26; i++) { + if (pCount[i] == sCount[i]) match++; + } + + List res = new ArrayList<>(); + if (match == 26) res.add(0); + + int l = 0; + for (int r = m; r < n; r++) { + int c = s.charAt(l) - 'a'; + if (sCount[c] == pCount[c]) match--; + sCount[c]--; + l++; + if (sCount[c] == pCount[c]) match++; + + c = s.charAt(r) - 'a'; + if (sCount[c] == pCount[c]) match--; + sCount[c]++; + if (sCount[c] == pCount[c]) match++; + + if (match == 26) res.add(l); + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector findAnagrams(string s, string p) { + int n = s.size(), m = p.size(); + if (m > n) return {}; + + vector pCount(26, 0), sCount(26, 0); + for (int i = 0; i < m; i++) { + pCount[p[i] - 'a']++; + sCount[s[i] - 'a']++; + } + + int match = 0; + for (int i = 0; i < 26; i++) { + if (pCount[i] == sCount[i]) match++; + } + + vector res; + if (match == 26) res.push_back(0); + + int l = 0; + for (int r = m; r < n; r++) { + int c = s[l] - 'a'; + if (sCount[c] == pCount[c]) match--; + sCount[c]--; + l++; + if (sCount[c] == pCount[c]) match++; + + c = s[r] - 'a'; + if (sCount[c] == pCount[c]) match--; + sCount[c]++; + if (sCount[c] == pCount[c]) match++; + + if (match == 26) res.push_back(l); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @param {string} p + * @return {number[]} + */ + findAnagrams(s, p) { + const n = s.length, m = p.length; + if (m > n) return []; + + const pCount = new Array(26).fill(0); + const sCount = new Array(26).fill(0); + + for (let i = 0; i < m; i++) { + pCount[p.charCodeAt(i) - 97]++; + sCount[s.charCodeAt(i) - 97]++; + } + + let match = 0; + for (let i = 0; i < 26; i++) { + if (pCount[i] === sCount[i]) match++; + } + + const res = []; + if (match === 26) res.push(0); + + let l = 0; + for (let r = m; r < n; r++) { + let c = s.charCodeAt(l) - 97; + if (sCount[c] === pCount[c]) match--; + sCount[c]--; + l++; + if (sCount[c] === pCount[c]) match++; + + c = s.charCodeAt(r) - 97; + if (sCount[c] === pCount[c]) match--; + sCount[c]++; + if (sCount[c] === pCount[c]) match++; + + if (match === 26) res.push(l); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n + m)$ +* Space complexity: $O(1)$ since we have at most $26$ different characters. + +> Where $n$ is the length of the string $s$ and $m$ is the length of the string $p$. \ No newline at end of file diff --git a/articles/find-all-numbers-disappeared-in-an-array.md b/articles/find-all-numbers-disappeared-in-an-array.md new file mode 100644 index 000000000..f0462dc4f --- /dev/null +++ b/articles/find-all-numbers-disappeared-in-an-array.md @@ -0,0 +1,358 @@ +## 1. Hash Set + +::tabs-start + +```python +class Solution: + def findDisappearedNumbers(self, nums: List[int]) -> List[int]: + n = len(nums) + store = set(range(1, n + 1)) + + for num in nums: + store.discard(num) + + return list(store) +``` + +```java +public class Solution { + public List findDisappearedNumbers(int[] nums) { + int n = nums.length; + Set store = new HashSet<>(); + for (int i = 1; i <= n; i++) store.add(i); + + for (int num : nums) { + store.remove(num); + } + + return new ArrayList<>(store); + } +} +``` + +```cpp +class Solution { +public: + vector findDisappearedNumbers(vector& nums) { + int n = nums.size(); + unordered_set store; + for (int i = 1; i <= n; i++) store.insert(i); + + for (int num : nums) { + store.erase(num); + } + + vector result(store.begin(), store.end()); + return result; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number[]} + */ + findDisappearedNumbers(nums) { + const n = nums.length; + const store = new Set(); + for (let i = 1; i <= n; i++) store.add(i); + + for (let num of nums) { + store.delete(num); + } + + return Array.from(store); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Boolean Array + +::tabs-start + +```python +class Solution: + def findDisappearedNumbers(self, nums: List[int]) -> List[int]: + n = len(nums) + mark = [False] * n + + for num in nums: + mark[num - 1] = True + + res = [] + for i in range(1, n + 1): + if not mark[i - 1]: + res.append(i) + return res +``` + +```java +public class Solution { + public List findDisappearedNumbers(int[] nums) { + int n = nums.length; + boolean[] mark = new boolean[n]; + + for (int num : nums) { + mark[num - 1] = true; + } + + List res = new ArrayList<>(); + for (int i = 1; i <= n; i++) { + if (!mark[i - 1]) { + res.add(i); + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + vector findDisappearedNumbers(vector& nums) { + int n = nums.size(); + vector mark(n, false); + + for (int num : nums) { + mark[num - 1] = true; + } + + vector res; + for (int i = 1; i <= n; i++) { + if (!mark[i - 1]) { + res.push_back(i); + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number[]} + */ + findDisappearedNumbers(nums) { + const n = nums.length; + const mark = new Array(n).fill(false); + + for (let num of nums) { + mark[num - 1] = true; + } + + const res = []; + for (let i = 1; i <= n; i++) { + if (!mark[i - 1]) { + res.push(i); + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Sorting + +::tabs-start + +```python +class Solution: + def findDisappearedNumbers(self, nums: List[int]) -> List[int]: + n = len(nums) + nums.sort() + + res = [] + idx = 0 + for num in range(1, n + 1): + while idx < n and nums[idx] < num: + idx += 1 + if idx == n or nums[idx] > num: + res.append(num) + return res +``` + +```java +public class Solution { + public List findDisappearedNumbers(int[] nums) { + int n = nums.length; + Arrays.sort(nums); + + List res = new ArrayList<>(); + int idx = 0; + for (int num = 1; num <= n; num++) { + while (idx < n && nums[idx] < num) { + idx++; + } + if (idx == n || nums[idx] > num) { + res.add(num); + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + vector findDisappearedNumbers(vector& nums) { + int n = nums.size(); + sort(nums.begin(), nums.end()); + + vector res; + int idx = 0; + for (int num = 1; num <= n; num++) { + while (idx < n && nums[idx] < num) { + idx++; + } + if (idx == n || nums[idx] > num) { + res.push_back(num); + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number[]} + */ + findDisappearedNumbers(nums) { + nums.sort((a, b) => a - b); + + const res = []; + let idx = 0; + for (let num = 1; num <= nums.length; num++) { + while (idx < nums.length && nums[idx] < num) { + idx++; + } + if (idx === nums.length || nums[idx] > num) { + res.push(num); + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(1)$ or $O(n)$ depending on the sorting algorithm. + +--- + +## 4. Negative Marking + +::tabs-start + +```python +class Solution: + def findDisappearedNumbers(self, nums: List[int]) -> List[int]: + for num in nums: + i = abs(num) - 1 + nums[i] = -1 * abs(nums[i]) + + res = [] + for i, num in enumerate(nums): + if num > 0: + res.append(i + 1) + return res +``` + +```java +public class Solution { + public List findDisappearedNumbers(int[] nums) { + for (int num : nums) { + int i = Math.abs(num) - 1; + nums[i] = -Math.abs(nums[i]); + } + + List res = new ArrayList<>(); + for (int i = 0; i < nums.length; i++) { + if (nums[i] > 0) { + res.add(i + 1); + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + vector findDisappearedNumbers(vector& nums) { + for (int num : nums) { + int i = abs(num) - 1; + nums[i] = -abs(nums[i]); + } + + vector res; + for (int i = 0; i < nums.size(); i++) { + if (nums[i] > 0) { + res.push_back(i + 1); + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number[]} + */ + findDisappearedNumbers(nums) { + for (let num of nums) { + let i = Math.abs(num) - 1; + nums[i] = -Math.abs(nums[i]); + } + + const res = []; + for (let i = 0; i < nums.length; i++) { + if (nums[i] > 0) { + res.push(i + 1); + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ since we modified the input array without using extra space. \ No newline at end of file diff --git a/articles/find-the-index-of-the-first-occurrence-in-a-string.md b/articles/find-the-index-of-the-first-occurrence-in-a-string.md new file mode 100644 index 000000000..beb5b5893 --- /dev/null +++ b/articles/find-the-index-of-the-first-occurrence-in-a-string.md @@ -0,0 +1,633 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def strStr(self, haystack: str, needle: str) -> int: + n, m = len(haystack), len(needle) + for i in range(n - m + 1): + j = 0 + while j < m: + if haystack[i + j] != needle[j]: + break + j += 1 + if j == m: + return i + return -1 +``` + +```java +public class Solution { + public int strStr(String haystack, String needle) { + int n = haystack.length(), m = needle.length(); + for (int i = 0; i < n - m + 1; i++) { + int j = 0; + while (j < m) { + if (haystack.charAt(i + j) != needle.charAt(j)) { + break; + } + j++; + } + if (j == m) return i; + } + return -1; + } +} +``` + +```cpp +class Solution { +public: + int strStr(string haystack, string needle) { + int n = haystack.length(), m = needle.length(); + for (int i = 0; i < n - m + 1; i++) { + int j = 0; + while (j < m) { + if (haystack[i + j] != needle[j]) { + break; + } + j++; + } + if (j == m) return i; + } + return -1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} haystack + * @param {string} needle + * @return {number} + */ + strStr(haystack, needle) { + let n = haystack.length, m = needle.length; + for (let i = 0; i < n - m + 1; i++) { + let j = 0; + while (j < m) { + if (haystack[i + j] !== needle[j]) { + break; + } + j++; + } + if (j === m) return i; + } + return -1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * m)$ +* Space complexity: $O(1)$ + +> Where $n$ is the length of the string $heystack$ and $m$ is the length of the string $needle$. + +--- + +## 2. Knuth-Morris-Pratt (KMP) Algorithm + +::tabs-start + +```python +class Solution: + def strStr(self, haystack: str, needle: str) -> int: + if needle == "": return 0 + lps = [0] * len(needle) + + prevLPS, i = 0, 1 + while i < len(needle): + if needle[i] == needle[prevLPS]: + lps[i] = prevLPS + 1 + prevLPS += 1 + i += 1 + elif prevLPS == 0: + lps[i] = 0 + i += 1 + else: + prevLPS = lps[prevLPS - 1] + + i = 0 # ptr for haystack + j = 0 # ptr for needle + while i < len(haystack): + if haystack[i] == needle[j]: + i, j = i + 1, j + 1 + else: + if j == 0: + i += 1 + else: + j = lps[j - 1] + + if j == len(needle): + return i - len(needle) + + return -1 +``` + +```java +public class Solution { + public int strStr(String haystack, String needle) { + if (needle.isEmpty()) return 0; + + int m = needle.length(); + int[] lps = new int[m]; + int prevLPS = 0, i = 1; + + while (i < m) { + if (needle.charAt(i) == needle.charAt(prevLPS)) { + lps[i] = prevLPS + 1; + prevLPS++; + i++; + } else if (prevLPS == 0) { + lps[i] = 0; + i++; + } else { + prevLPS = lps[prevLPS - 1]; + } + } + + i = 0; // ptr for haystack + int j = 0; // ptr for needle + while (i < haystack.length()) { + if (haystack.charAt(i) == needle.charAt(j)) { + i++; + j++; + } else { + if (j == 0) { + i++; + } else { + j = lps[j - 1]; + } + } + + if (j == m) { + return i - m; + } + } + + return -1; + } +} +``` + +```cpp +class Solution { +public: + int strStr(string haystack, string needle) { + if (needle.empty()) return 0; + + int m = needle.size(); + vector lps(m, 0); + int prevLPS = 0, i = 1; + + while (i < m) { + if (needle[i] == needle[prevLPS]) { + lps[i] = prevLPS + 1; + prevLPS++; + i++; + } else if (prevLPS == 0) { + lps[i] = 0; + i++; + } else { + prevLPS = lps[prevLPS - 1]; + } + } + + i = 0; // ptr for haystack + int j = 0; // ptr for needle + while (i < haystack.size()) { + if (haystack[i] == needle[j]) { + i++; + j++; + } else { + if (j == 0) { + i++; + } else { + j = lps[j - 1]; + } + } + + if (j == m) { + return i - m; + } + } + + return -1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} haystack + * @param {string} needle + * @return {number} + */ + strStr(haystack, needle) { + if (needle === "") return 0; + + const m = needle.length; + const lps = new Array(m).fill(0); + + let prevLPS = 0, i = 1; + while (i < m) { + if (needle[i] === needle[prevLPS]) { + lps[i] = prevLPS + 1; + prevLPS++; + i++; + } else if (prevLPS === 0) { + lps[i] = 0; + i++; + } else { + prevLPS = lps[prevLPS - 1]; + } + } + + i = 0; // ptr for haystack + let j = 0; // ptr for needle + while (i < haystack.length) { + if (haystack[i] === needle[j]) { + i++; + j++; + } else { + if (j === 0) { + i++; + } else { + j = lps[j - 1]; + } + } + + if (j === m) { + return i - m; + } + } + + return -1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n + m)$ +* Space complexity: $O(m)$ + +> Where $n$ is the length of the string $heystack$ and $m$ is the length of the string $needle$. + +--- + +## 3. Z-Algorithm + +::tabs-start + +```python +class Solution: + def strStr(self, haystack: str, needle: str) -> int: + if not needle: + return 0 + + s = needle + "$" + haystack + n = len(s) + z = [0] * n + l, r = 0, 0 + + for i in range(1, n): + if i <= r: + z[i] = min(r - i + 1, z[i - l]) + while i + z[i] < n and s[z[i]] == s[i + z[i]]: + z[i] += 1 + if i + z[i] - 1 > r: + l, r = i, i + z[i] - 1 + + for i in range(len(needle) + 1, n): + if z[i] == len(needle): + return i - len(needle) - 1 + + return -1 +``` + +```java +public class Solution { + public int strStr(String haystack, String needle) { + if (needle.isEmpty()) return 0; + + String s = needle + "$" + haystack; + int n = s.length(); + int[] z = new int[n]; + int l = 0, r = 0; + + for (int i = 1; i < n; i++) { + if (i <= r) { + z[i] = Math.min(r - i + 1, z[i - l]); + } + while (i + z[i] < n && s.charAt(z[i]) == s.charAt(i + z[i])) { + z[i]++; + } + if (i + z[i] - 1 > r) { + l = i; + r = i + z[i] - 1; + } + } + + for (int i = needle.length() + 1; i < n; i++) { + if (z[i] == needle.length()) { + return i - needle.length() - 1; + } + } + + return -1; + } +} +``` + +```cpp +class Solution { +public: + int strStr(string haystack, string needle) { + if (needle.empty()) return 0; + + string s = needle + "$" + haystack; + int n = s.size(); + vector z(n, 0); + int l = 0, r = 0; + + for (int i = 1; i < n; i++) { + if (i <= r) { + z[i] = min(r - i + 1, z[i - l]); + } + while (i + z[i] < n && s[z[i]] == s[i + z[i]]) { + z[i]++; + } + if (i + z[i] - 1 > r) { + l = i; + r = i + z[i] - 1; + } + } + + for (int i = needle.size() + 1; i < n; i++) { + if (z[i] == needle.size()) { + return i - needle.size() - 1; + } + } + + return -1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} haystack + * @param {string} needle + * @return {number} + */ + strStr(haystack, needle) { + if (needle === "") return 0; + + const s = needle + "$" + haystack; + const n = s.length; + const z = new Array(n).fill(0); + let l = 0, r = 0; + + for (let i = 1; i < n; i++) { + if (i <= r) { + z[i] = Math.min(r - i + 1, z[i - l]); + } + while (i + z[i] < n && s[z[i]] === s[i + z[i]]) { + z[i]++; + } + if (i + z[i] - 1 > r) { + l = i; + r = i + z[i] - 1; + } + } + + for (let i = needle.length + 1; i < n; i++) { + if (z[i] === needle.length) { + return i - needle.length - 1; + } + } + + return -1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n + m)$ +* Space complexity: $O(n + m)$ + +> Where $n$ is the length of the string $heystack$ and $m$ is the length of the string $needle$. + +--- + +## 4. Rabin-Karp Algorithm (Rolling Hash) + +::tabs-start + +```python +class Solution: + def strStr(self, haystack: str, needle: str) -> int: + if not needle: + return 0 + + base1, mod1 = 31, 768258391 + base2, mod2 = 37, 685683731 + + n, m = len(haystack), len(needle) + if m > n: + return -1 + + power1, power2 = 1, 1 + for _ in range(m): + power1 = (power1 * base1) % mod1 + power2 = (power2 * base2) % mod2 + + needle_hash1, needle_hash2 = 0, 0 + haystack_hash1, haystack_hash2 = 0, 0 + + for i in range(m): + needle_hash1 = (needle_hash1 * base1 + ord(needle[i])) % mod1 + needle_hash2 = (needle_hash2 * base2 + ord(needle[i])) % mod2 + haystack_hash1 = (haystack_hash1 * base1 + ord(haystack[i])) % mod1 + haystack_hash2 = (haystack_hash2 * base2 + ord(haystack[i])) % mod2 + + for i in range(n - m + 1): + if haystack_hash1 == needle_hash1 and haystack_hash2 == needle_hash2: + return i + + if i + m < n: + haystack_hash1 = (haystack_hash1 * base1 - ord(haystack[i]) * power1 + ord(haystack[i + m])) % mod1 + haystack_hash2 = (haystack_hash2 * base2 - ord(haystack[i]) * power2 + ord(haystack[i + m])) % mod2 + + haystack_hash1 = (haystack_hash1 + mod1) % mod1 + haystack_hash2 = (haystack_hash2 + mod2) % mod2 + + return -1 +``` + +```java +public class Solution { + public int strStr(String haystack, String needle) { + if (needle.isEmpty()) return 0; + + int base1 = 31, mod1 = 768258391; + int base2 = 37, mod2 = 685683731; + + int n = haystack.length(), m = needle.length(); + if (m > n) return -1; + + long power1 = 1, power2 = 1; + for (int i = 0; i < m; i++) { + power1 = (power1 * base1) % mod1; + power2 = (power2 * base2) % mod2; + } + + long needleHash1 = 0, needleHash2 = 0; + long haystackHash1 = 0, haystackHash2 = 0; + + for (int i = 0; i < m; i++) { + needleHash1 = (needleHash1 * base1 + needle.charAt(i)) % mod1; + needleHash2 = (needleHash2 * base2 + needle.charAt(i)) % mod2; + haystackHash1 = (haystackHash1 * base1 + haystack.charAt(i)) % mod1; + haystackHash2 = (haystackHash2 * base2 + haystack.charAt(i)) % mod2; + } + + for (int i = 0; i <= n - m; i++) { + if (haystackHash1 == needleHash1 && haystackHash2 == needleHash2) { + return i; + } + + if (i + m < n) { + haystackHash1 = (haystackHash1 * base1 - haystack.charAt(i) * power1 + haystack.charAt(i + m)) % mod1; + haystackHash2 = (haystackHash2 * base2 - haystack.charAt(i) * power2 + haystack.charAt(i + m)) % mod2; + + if (haystackHash1 < 0) haystackHash1 += mod1; + if (haystackHash2 < 0) haystackHash2 += mod2; + } + } + + return -1; + } +} +``` + +```cpp +class Solution { +public: + int strStr(string haystack, string needle) { + if (needle.empty()) return 0; + + long long base1 = 31, mod1 = 768258391; + long long base2 = 37, mod2 = 685683731; + + int n = haystack.size(), m = needle.size(); + if (m > n) return -1; + + long long power1 = 1, power2 = 1; + for (int i = 0; i < m; i++) { + power1 = (power1 * base1) % mod1; + power2 = (power2 * base2) % mod2; + } + + long long needleHash1 = 0, needleHash2 = 0; + long long haystackHash1 = 0, haystackHash2 = 0; + + for (int i = 0; i < m; i++) { + needleHash1 = (needleHash1 * base1 + needle[i]) % mod1; + needleHash2 = (needleHash2 * base2 + needle[i]) % mod2; + haystackHash1 = (haystackHash1 * base1 + haystack[i]) % mod1; + haystackHash2 = (haystackHash2 * base2 + haystack[i]) % mod2; + } + + for (int i = 0; i <= n - m; i++) { + if (haystackHash1 == needleHash1 && haystackHash2 == needleHash2) { + return i; + } + + if (i + m < n) { + haystackHash1 = (haystackHash1 * base1 - haystack[i] * power1 + haystack[i + m]) % mod1; + haystackHash2 = (haystackHash2 * base2 - haystack[i] * power2 + haystack[i + m]) % mod2; + + if (haystackHash1 < 0) haystackHash1 += mod1; + if (haystackHash2 < 0) haystackHash2 += mod2; + } + } + + return -1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} haystack + * @param {string} needle + * @return {number} + */ + strStr(haystack, needle) { + if (needle === "") return 0; + + const base1 = 31, mod1 = 768258391; + const base2 = 37, mod2 = 685683731; + + const n = haystack.length, m = needle.length; + if (m > n) return -1; + + let power1 = 1, power2 = 1; + for (let i = 0; i < m; i++) { + power1 = (power1 * base1) % mod1; + power2 = (power2 * base2) % mod2; + } + + let needleHash1 = 0, needleHash2 = 0; + let haystackHash1 = 0, haystackHash2 = 0; + + for (let i = 0; i < m; i++) { + needleHash1 = (needleHash1 * base1 + needle.charCodeAt(i)) % mod1; + needleHash2 = (needleHash2 * base2 + needle.charCodeAt(i)) % mod2; + haystackHash1 = (haystackHash1 * base1 + haystack.charCodeAt(i)) % mod1; + haystackHash2 = (haystackHash2 * base2 + haystack.charCodeAt(i)) % mod2; + } + + for (let i = 0; i <= n - m; i++) { + if (haystackHash1 === needleHash1 && haystackHash2 === needleHash2) { + return i; + } + + if (i + m < n) { + haystackHash1 = (haystackHash1 * base1 - haystack.charCodeAt(i) * power1 + haystack.charCodeAt(i + m)) % mod1; + haystackHash2 = (haystackHash2 * base2 - haystack.charCodeAt(i) * power2 + haystack.charCodeAt(i + m)) % mod2; + + if (haystackHash1 < 0) haystackHash1 += mod1; + if (haystackHash2 < 0) haystackHash2 += mod2; + } + } + + return -1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n + m)$ +* Space complexity: $O(1)$ + +> Where $n$ is the length of the string $heystack$ and $m$ is the length of the string $needle$. \ No newline at end of file diff --git a/articles/find-words-that-can-be-formed-by-characters.md b/articles/find-words-that-can-be-formed-by-characters.md new file mode 100644 index 000000000..4dfdf7b44 --- /dev/null +++ b/articles/find-words-that-can-be-formed-by-characters.md @@ -0,0 +1,391 @@ +## 1. Hash Map (Two Pass) + +::tabs-start + +```python +class Solution: + def countCharacters(self, words: List[str], chars: str) -> int: + count = Counter(chars) + res = 0 + + for w in words: + cur_word = Counter(w) + good = True + for c in cur_word: + if cur_word[c] > count[c]: + good = False + break + if good: + res += len(w) + return res +``` + +```java +public class Solution { + public int countCharacters(String[] words, String chars) { + Map count = new HashMap<>(); + for (char c : chars.toCharArray()) { + count.put(c, count.getOrDefault(c, 0) + 1); + } + int res = 0; + for (String w : words) { + Map curWord = new HashMap<>(); + for (char c : w.toCharArray()) { + curWord.put(c, curWord.getOrDefault(c, 0) + 1); + } + boolean good = true; + for (char c : curWord.keySet()) { + if (curWord.get(c) > count.getOrDefault(c, 0)) { + good = false; + break; + } + } + if (good) { + res += w.length(); + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int countCharacters(vector& words, string chars) { + unordered_map count; + for (char c : chars) { + count[c]++; + } + int res = 0; + for (const string& w : words) { + unordered_map curWord; + for (char c : w) { + curWord[c]++; + } + bool good = true; + for (const auto& p : curWord) { + if (p.second > count[p.first]) { + good = false; + break; + } + } + if (good) { + res += w.size(); + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} words + * @param {string} chars + * @return {number} + */ + countCharacters(words, chars) { + const count = {}; + for (const c of chars) { + count[c] = (count[c] || 0) + 1; + } + let res = 0; + for (const w of words) { + const curWord = {}; + for (const c of w) { + curWord[c] = (curWord[c] || 0) + 1; + } + let good = true; + for (const c in curWord) { + if (curWord[c] > (count[c] || 0)) { + good = false; + break; + } + } + if (good) { + res += w.length; + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n + (m * k))$ +* Space complexity: $O(1)$ since we have at most $26$ different characters. + +> Where $n$ is the length of $chars$, $m$ is the number of words and $k$ is the average length of each word. + +--- + +## 2. Hash Map (One Pass) + +::tabs-start + +```python +class Solution: + def countCharacters(self, words: List[str], chars: str) -> int: + count = Counter(chars) + res = 0 + + for w in words: + cur_word = defaultdict(int) + good = True + for c in w: + cur_word[c] += 1 + if cur_word[c] > count[c]: + good = False + break + if good: + res += len(w) + return res +``` + +```java +public class Solution { + public int countCharacters(String[] words, String chars) { + Map count = new HashMap<>(); + for (char c : chars.toCharArray()) { + count.put(c, count.getOrDefault(c, 0) + 1); + } + int res = 0; + for (String w : words) { + Map curWord = new HashMap<>(); + boolean good = true; + for (char c : w.toCharArray()) { + curWord.put(c, curWord.getOrDefault(c, 0) + 1); + if (curWord.get(c) > count.getOrDefault(c, 0)) { + good = false; + break; + } + } + if (good) { + res += w.length(); + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int countCharacters(vector& words, string chars) { + unordered_map count; + for (char c : chars) { + count[c]++; + } + int res = 0; + for (const string& w : words) { + unordered_map curWord; + bool good = true; + for (char c : w) { + curWord[c]++; + if (curWord[c] > count[c]) { + good = false; + break; + } + } + if (good) { + res += w.size(); + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} words + * @param {string} chars + * @return {number} + */ + countCharacters(words, chars) { + const count = {}; + for (const c of chars) { + count[c] = (count[c] || 0) + 1; + } + let res = 0; + for (const w of words) { + const curWord = {}; + let good = true; + for (const c of w) { + curWord[c] = (curWord[c] || 0) + 1; + if (curWord[c] > (count[c] || 0)) { + good = false; + break; + } + } + if (good) { + res += w.length; + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n + (m * k))$ +* Space complexity: $O(1)$ since we have at most $26$ different characters. + +> Where $n$ is the length of $chars$, $m$ is the number of words and $k$ is the average length of each word. + +--- + +## 3. Hash Table + +::tabs-start + +```python +class Solution: + def countCharacters(self, words: List[str], chars: str) -> int: + count = [0] * 26 + for c in chars: + count[ord(c) - ord('a')] += 1 + + org = count[:] + res = 0 + + for w in words: + good = True + for c in w: + i = ord(c) - ord('a') + count[i] -= 1 + if count[i] < 0: + good = False + break + if good: + res += len(w) + + for i in range(26): + count[i] = org[i] + return res +``` + +```java +public class Solution { + public int countCharacters(String[] words, String chars) { + int[] count = new int[26]; + for (char c : chars.toCharArray()) { + count[c - 'a']++; + } + + int[] org = count.clone(); + int res = 0; + + for (String w : words) { + boolean good = true; + for (int i = 0; i < w.length(); i++) { + int j = w.charAt(i) - 'a'; + count[j]--; + if (count[j] < 0) { + good = false; + break; + } + } + if (good) { + res += w.length(); + } + for (int i = 0; i < 26; i++) { + count[i] = org[i]; + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int countCharacters(vector& words, string chars) { + vector count(26, 0); + for (char c : chars) { + count[c - 'a']++; + } + + vector org = count; + int res = 0; + + for (string& w : words) { + bool good = true; + for (char& c : w) { + int i = c - 'a'; + count[i]--; + if (count[i] < 0) { + good = false; + break; + } + } + if (good) { + res += w.length(); + } + for (int i = 0; i < 26; i++) { + count[i] = org[i]; + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} words + * @param {string} chars + * @return {number} + */ + countCharacters(words, chars) { + const count = new Array(26).fill(0); + for (let c of chars) { + count[c.charCodeAt(0) - 'a'.charCodeAt(0)]++; + } + + const org = [...count]; + let res = 0; + + for (let w of words) { + let good = true; + for (let c of w) { + const i = c.charCodeAt(0) - 'a'.charCodeAt(0); + count[i]--; + if (count[i] < 0) { + good = false; + break; + } + } + + if (good) { + res += w.length; + } + for (let i = 0; i < 26; i++) { + count[i] = org[i]; + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n + (m * k))$ +* Space complexity: $O(1)$ since we have at most $26$ different characters. + +> Where $n$ is the length of $chars$, $m$ is the number of words and $k$ is the average length of each word. \ No newline at end of file diff --git a/articles/first-unique-character-in-a-string.md b/articles/first-unique-character-in-a-string.md new file mode 100644 index 000000000..b60c2e4f6 --- /dev/null +++ b/articles/first-unique-character-in-a-string.md @@ -0,0 +1,365 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def firstUniqChar(self, s: str) -> int: + for i in range(len(s)): + flag = True + for j in range(len(s)): + if i == j: + continue + if s[i] == s[j]: + flag = False + break + if flag: + return i + return -1 +``` + +```java +public class Solution { + public int firstUniqChar(String s) { + for (int i = 0; i < s.length(); i++) { + boolean flag = true; + for (int j = 0; j < s.length(); j++) { + if (i == j) continue; + if (s.charAt(i) == s.charAt(j)) { + flag = false; + break; + } + } + if (flag) return i; + } + return -1; + } +} +``` + +```cpp +class Solution { +public: + int firstUniqChar(string s) { + for (int i = 0; i < s.size(); i++) { + bool flag = true; + for (int j = 0; j < s.size(); j++) { + if (i == j) continue; + if (s[i] == s[j]) { + flag = false; + break; + } + } + if (flag) return i; + } + return -1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {number} + */ + firstUniqChar(s) { + for (let i = 0; i < s.length; i++) { + let flag = true; + for (let j = 0; j < s.length; j++) { + if (i === j) continue; + if (s[i] === s[j]) { + flag = false; + break; + } + } + if (flag) return i; + } + return -1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(1)$ + +--- + +## 2. Hash Map + +::tabs-start + +```python +class Solution: + def firstUniqChar(self, s: str) -> int: + count = defaultdict(int) + for c in s: + count[c] += 1 + + for i, c in enumerate(s): + if count[c] == 1: + return i + return -1 +``` + +```java +public class Solution { + public int firstUniqChar(String s) { + Map count = new HashMap<>(); + for (char c : s.toCharArray()) { + count.put(c, count.getOrDefault(c, 0) + 1); + } + + for (int i = 0; i < s.length(); i++) { + if (count.get(s.charAt(i)) == 1) { + return i; + } + } + return -1; + } +} +``` + +```cpp +class Solution { +public: + int firstUniqChar(string s) { + unordered_map count; + for (char c : s) { + count[c]++; + } + + for (int i = 0; i < s.size(); i++) { + if (count[s[i]] == 1) { + return i; + } + } + return -1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {number} + */ + firstUniqChar(s) { + const count = new Map(); + for (const c of s) { + count.set(c, (count.get(c) || 0) + 1); + } + + for (let i = 0; i < s.length; i++) { + if (count.get(s[i]) === 1) { + return i; + } + } + return -1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ since we have at most $26$ different characters. + +--- + +## 3. Hash Map (Optimal) + +::tabs-start + +```python +class Solution: + def firstUniqChar(self, s: str) -> int: + n = len(s) + count = defaultdict(int) + for i, c in enumerate(s): + if c not in count: + count[c] = i + else: + count[c] = n + + res = n + for c in count: + res = min(res, count[c]) + + return -1 if res == n else res +``` + +```java +public class Solution { + public int firstUniqChar(String s) { + int n = s.length(); + Map count = new HashMap<>(); + + for (int i = 0; i < n; i++) { + char c = s.charAt(i); + if (!count.containsKey(c)) { + count.put(c, i); + } else { + count.put(c, n); + } + } + + int res = n; + for (int index : count.values()) { + res = Math.min(res, index); + } + + return res == n ? -1 : res; + } +} +``` + +```cpp +class Solution { +public: + int firstUniqChar(string s) { + int n = s.size(); + unordered_map count; + + for (int i = 0; i < n; i++) { + if (count.find(s[i]) == count.end()) { + count[s[i]] = i; + } else { + count[s[i]] = n; + } + } + + int res = n; + for (auto& [key, index] : count) { + res = min(res, index); + } + + return res == n ? -1 : res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {number} + */ + firstUniqChar(s) { + const n = s.length; + const count = new Map(); + + for (let i = 0; i < n; i++) { + const c = s[i]; + if (!count.has(c)) { + count.set(c, i); + } else { + count.set(c, n); + } + } + + let res = n; + for (const index of count.values()) { + res = Math.min(res, index); + } + + return res === n ? -1 : res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ since we have at most $26$ different characters. + +--- + +## 4. Iteration + +::tabs-start + +```python +class Solution: + def firstUniqChar(self, s: str) -> int: + res = n = len(s) + for ch in range(ord('a'), ord('z') + 1): + index = s.find(chr(ch)) + if index != -1 and s.rfind(chr(ch)) == index: + res = min(res, index) + + return -1 if res == n else res +``` + +```java +public class Solution { + public int firstUniqChar(String s) { + int res = s.length(); + + for (char ch = 'a'; ch <= 'z'; ch++) { + int firstIndex = s.indexOf(ch); + if (firstIndex != -1 && s.lastIndexOf(ch) == firstIndex) { + res = Math.min(res, firstIndex); + } + } + + return res == s.length() ? -1 : res; + } +} +``` + +```cpp +class Solution { +public: + int firstUniqChar(string s) { + int res = s.size(); + + for (char ch = 'a'; ch <= 'z'; ch++) { + int firstIndex = s.find(ch); + if (firstIndex != string::npos && s.rfind(ch) == firstIndex) { + res = min(res, firstIndex); + } + } + + return res == s.size() ? -1 : res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {number} + */ + firstUniqChar(s) { + let res = s.length; + + for (let ch = 'a'.charCodeAt(0); ch <= 'z'.charCodeAt(0); ch++) { + const char = String.fromCharCode(ch); + const firstIndex = s.indexOf(char); + if (firstIndex !== -1 && s.lastIndexOf(char) === firstIndex) { + res = Math.min(res, firstIndex); + } + } + + return res === s.length ? -1 : res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(26 * n)$ since we have at most $26$ different characters. +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/grid-game.md b/articles/grid-game.md new file mode 100644 index 000000000..236f53012 --- /dev/null +++ b/articles/grid-game.md @@ -0,0 +1,354 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def gridGame(self, grid: List[List[int]]) -> int: + cols = len(grid[0]) + res = float('inf') + + top1 = 0 + for i in range(cols): + top1 += grid[0][i] + bottom1 = 0 + for j in range(i, cols): + bottom1 += grid[1][j] + + top2 = robot2 = 0 + for j in range(cols): + if j > i: + top2 += grid[0][j] + + bottom2 = 0 + for k in range(j, i): + bottom2 += grid[1][k] + robot2 = max(robot2, top2 + bottom2) + + res = min(res, robot2) + + return res +``` + +```java +public class Solution { + public long gridGame(int[][] grid) { + int cols = grid[0].length; + long res = Long.MAX_VALUE; + + long top1 = 0; + for (int i = 0; i < cols; i++) { + top1 += grid[0][i]; + long bottom1 = 0; + for (int j = i; j < cols; j++) { + bottom1 += grid[1][j]; + } + + long top2 = 0, robot2 = 0; + for (int j = 0; j < cols; j++) { + if (j > i) { + top2 += grid[0][j]; + } + + long bottom2 = 0; + for (int k = j; k < i; k++) { + bottom2 += grid[1][k]; + } + robot2 = Math.max(robot2, top2 + bottom2); + } + + res = Math.min(res, robot2); + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + long long gridGame(vector>& grid) { + int cols = grid[0].size(); + long long res = LLONG_MAX; + + long long top1 = 0; + for (int i = 0; i < cols; i++) { + top1 += grid[0][i]; + long long bottom1 = 0; + for (int j = i; j < cols; j++) { + bottom1 += grid[1][j]; + } + + long long top2 = 0, robot2 = 0; + for (int j = 0; j < cols; j++) { + if (j > i) { + top2 += grid[0][j]; + } + + long long bottom2 = 0; + for (int k = j; k < i; k++) { + bottom2 += grid[1][k]; + } + robot2 = max(robot2, top2 + bottom2); + } + + res = min(res, robot2); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} grid + * @return {number} + */ + gridGame(grid) { + let cols = grid[0].length; + let res = Infinity; + + let top1 = 0; + for (let i = 0; i < cols; i++) { + top1 += grid[0][i]; + let bottom1 = 0; + for (let j = i; j < cols; j++) { + bottom1 += grid[1][j]; + } + + let top2 = 0, robot2 = 0; + for (let j = 0; j < cols; j++) { + if (j > i) { + top2 += grid[0][j]; + } + + let bottom2 = 0; + for (let k = j; k < i; k++) { + bottom2 += grid[1][k]; + } + robot2 = Math.max(robot2, top2 + bottom2); + } + + res = Math.min(res, robot2); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 3)$ +* Space complexity: $O(1)$ + +--- + +## 2. Prefix Sum + +::tabs-start + +```python +class Solution: + def gridGame(self, grid: List[List[int]]) -> int: + N = len(grid[0]) + preRow1, preRow2 = grid[0].copy(), grid[1].copy() + + for i in range(1, N): + preRow1[i] += preRow1[i - 1] + preRow2[i] += preRow2[i - 1] + + res = float("inf") + for i in range(N): + top = preRow1[-1] - preRow1[i] + bottom = preRow2[i - 1] if i > 0 else 0 + secondRobot = max(top, bottom) + res = min(res, secondRobot) + return res +``` + +```java +class Solution { + public long gridGame(int[][] grid) { + int N = grid[0].length; + long[] preRow1 = new long[N]; + long[] preRow2 = new long[N]; + for (int i = 0; i < N; i++) { + preRow1[i] = (long)grid[0][i]; + preRow2[i] = (long)grid[1][i]; + } + + for (int i = 1; i < N; i++) { + preRow1[i] += preRow1[i - 1]; + preRow2[i] += preRow2[i - 1]; + } + + long res = Long.MAX_VALUE; + for (int i = 0; i < N; i++) { + long top = preRow1[N - 1] - preRow1[i]; + long bottom = i > 0 ? preRow2[i - 1] : 0; + long secondRobot = Math.max(top, bottom); + res = Math.min(res, secondRobot); + } + return res; + } +} +``` + +```cpp +class Solution { +public: + long long gridGame(vector>& grid) { + int N = grid[0].size(); + vector preRow1, preRow2; + for (int i = 0; i < N; i++) { + preRow1.push_back((long)grid[0][i]); + preRow2.push_back((long)grid[1][i]); + } + + for (int i = 1; i < N; i++) { + preRow1[i] += preRow1[i - 1]; + preRow2[i] += preRow2[i - 1]; + } + + long long res = LLONG_MAX; + for (int i = 0; i < N; i++) { + long long top = preRow1[N - 1] - preRow1[i]; + long long bottom = i > 0 ? preRow2[i - 1] : 0; + long long secondRobot = max(top, bottom); + res = min(res, secondRobot); + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} grid + * @return {number} + */ + gridGame(grid) { + const N = grid[0].length; + const preRow1 = [...grid[0]]; + const preRow2 = [...grid[1]]; + + for (let i = 1; i < N; i++) { + preRow1[i] += preRow1[i - 1]; + preRow2[i] += preRow2[i - 1]; + } + + let res = Infinity; + for (let i = 0; i < N; i++) { + const top = preRow1[N - 1] - preRow1[i]; + const bottom = i > 0 ? preRow2[i - 1] : 0; + const secondRobot = Math.max(top, bottom); + res = Math.min(res, secondRobot); + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Prefix Sum (Space Optimized) + +::tabs-start + +```python +class Solution: + def gridGame(self, grid: List[List[int]]) -> int: + res = float("inf") + topSum = sum(grid[0]) + bottomSum = 0 + + for i in range(len(grid[0])): + topSum -= grid[0][i] + res = min(res, max(topSum, bottomSum)) + bottomSum += grid[1][i] + + return res +``` + +```java +public class Solution { + public long gridGame(int[][] grid) { + long res = Long.MAX_VALUE; + long topSum = 0, bottomSum = 0; + + for (int i = 0; i < grid[0].length; i++) { + topSum += grid[0][i]; + } + + for (int i = 0; i < grid[0].length; i++) { + topSum -= grid[0][i]; + res = Math.min(res, Math.max(topSum, bottomSum)); + bottomSum += grid[1][i]; + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + long long gridGame(vector>& grid) { + long long res = LLONG_MAX; + long long topSum = accumulate(grid[0].begin(), grid[0].end(), 0LL); + long long bottomSum = 0; + + for (int i = 0; i < grid[0].size(); i++) { + topSum -= grid[0][i]; + res = min(res, max(topSum, bottomSum)); + bottomSum += grid[1][i]; + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} grid + * @return {number} + */ + gridGame(grid) { + let res = Infinity; + let topSum = grid[0].reduce((a, b) => a + b, 0); + let bottomSum = 0; + + for (let i = 0; i < grid[0].length; i++) { + topSum -= grid[0][i]; + res = Math.min(res, Math.max(topSum, bottomSum)); + bottomSum += grid[1][i]; + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/intersection-of-two-arrays.md b/articles/intersection-of-two-arrays.md new file mode 100644 index 000000000..5892704ec --- /dev/null +++ b/articles/intersection-of-two-arrays.md @@ -0,0 +1,566 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def intersection(self, nums1: List[int], nums2: List[int]) -> List[int]: + res = set() + for i in nums1: + for j in nums2: + if i == j: + res.add(i) + break + return list(res) +``` + +```java +public class Solution { + public int[] intersection(int[] nums1, int[] nums2) { + Set res = new HashSet<>(); + for (int i : nums1) { + for (int j : nums2) { + if (i == j) { + res.add(i); + break; + } + } + } + int[] result = new int[res.size()]; + int idx = 0; + for (int num : res) { + result[idx++] = num; + } + return result; + } +} +``` + +```cpp +class Solution { +public: + vector intersection(vector& nums1, vector& nums2) { + unordered_set res; + for (int i : nums1) { + for (int j : nums2) { + if (i == j) { + res.insert(i); + break; + } + } + } + return vector(res.begin(), res.end()); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums1 + * @param {number[]} nums2 + * @return {number[]} + */ + intersection(nums1, nums2) { + const res = new Set(); + for (const i of nums1) { + for (const j of nums2) { + if (i === j) { + res.add(i); + break; + } + } + } + return Array.from(res); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * m)$ +* Space complexity: $O(n)$ + +> Where $n$ is the size of the array $nums1$ and $m$ is the size of the array $nums2$. + +--- + +## 2. Sorting + Two Pointers + +::tabs-start + +```python +class Solution: + def intersection(self, nums1: List[int], nums2: List[int]) -> List[int]: + nums1.sort() + nums2.sort() + + n, m = len(nums1), len(nums2) + res, i, j = [], 0, 0 + + while i < n and j < m: + while j < m and nums2[j] < nums1[i]: + j += 1 + if j < m: + if nums1[i] == nums2[j]: + res.append(nums1[i]) + i += 1 + while i < n and nums1[i] == nums1[i - 1]: + i += 1 + + return res +``` + +```java +public class Solution { + public int[] intersection(int[] nums1, int[] nums2) { + Arrays.sort(nums1); + Arrays.sort(nums2); + + List res = new ArrayList<>(); + int i = 0, j = 0; + + while (i < nums1.length && j < nums2.length) { + while (j < nums2.length && nums2[j] < nums1[i]) { + j++; + } + if (j < nums2.length) { + if (nums1[i] == nums2[j]) { + res.add(nums1[i]); + } + i++; + while (i < nums1.length && nums1[i] == nums1[i - 1]) { + i++; + } + } + } + + return res.stream().mapToInt(Integer::intValue).toArray(); + } +} +``` + +```cpp +class Solution { +public: + vector intersection(vector& nums1, vector& nums2) { + sort(nums1.begin(), nums1.end()); + sort(nums2.begin(), nums2.end()); + + int n = nums1.size(), m = nums2.size(); + vector res; + int i = 0, j = 0; + + while (i < n && j < m) { + while (j < m && nums2[j] < nums1[i]) { + ++j; + } + if (j < m) { + if (nums1[i] == nums2[j]) { + res.push_back(nums1[i]); + } + ++i; + while (i < n && nums1[i] == nums1[i - 1]) { + ++i; + } + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums1 + * @param {number[]} nums2 + * @return {number[]} + */ + intersection(nums1, nums2) { + nums1.sort((a, b) => a - b); + nums2.sort((a, b) => a - b); + + const res = []; + let i = 0, j = 0; + + while (i < nums1.length && j < nums2.length) { + while (j < nums2.length && nums2[j] < nums1[i]) { + j++; + } + if (j < nums2.length) { + if (nums1[i] === nums2[j]) { + res.push(nums1[i]); + } + i++; + while (i < nums1.length && nums1[i] === nums1[i - 1]) { + i++; + } + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n + m \log m)$ +* Space complexity: $O(n)$ + +> Where $n$ is the size of the array $nums1$ and $m$ is the size of the array $nums2$. + +--- + +## 3. Hash Set + +::tabs-start + +```python +class Solution: + def intersection(self, nums1: List[int], nums2: List[int]) -> List[int]: + set1 = set(nums1) + set2 = set(nums2) + + res = [] + for num in set1: + if num in set2: + res.append(num) + return res +``` + +```java +public class Solution { + public int[] intersection(int[] nums1, int[] nums2) { + Set set1 = new HashSet<>(); + for (int num : nums1) { + set1.add(num); + } + + Set set2 = new HashSet<>(); + for (int num : nums2) { + set2.add(num); + } + + List res = new ArrayList<>(); + for (int num : set1) { + if (set2.contains(num)) { + res.add(num); + } + } + + return res.stream().mapToInt(Integer::intValue).toArray(); + } +} +``` + +```cpp +class Solution { +public: + vector intersection(vector& nums1, vector& nums2) { + unordered_set set1(nums1.begin(), nums1.end()); + unordered_set set2(nums2.begin(), nums2.end()); + + vector res; + for (int num : set1) { + if (set2.find(num) != set2.end()) { + res.push_back(num); + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums1 + * @param {number[]} nums2 + * @return {number[]} + */ + intersection(nums1, nums2) { + const set1 = new Set(nums1); + const set2 = new Set(nums2); + + const res = []; + for (const num of set1) { + if (set2.has(num)) { + res.push(num); + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n + m)$ +* Space complexity: $O(n + m)$ + +> Where $n$ is the size of the array $nums1$ and $m$ is the size of the array $nums2$. + +--- + +## 4. Hash Map + +::tabs-start + +```python +class Solution: + def intersection(self, nums1: List[int], nums2: List[int]) -> List[int]: + seen = defaultdict(int) + for num in nums1: + seen[num] = 1 + + res = [] + for num in nums2: + if seen[num] == 1: + seen[num] = 0 + res.append(num) + return res +``` + +```java +public class Solution { + public int[] intersection(int[] nums1, int[] nums2) { + Map seen = new HashMap<>(); + for (int num : nums1) { + seen.put(num, 1); + } + + List res = new ArrayList<>(); + for (int num : nums2) { + if (seen.getOrDefault(num, 0) == 1) { + seen.put(num, 0); + res.add(num); + } + } + + return res.stream().mapToInt(Integer::intValue).toArray(); + } +} +``` + +```cpp +class Solution { +public: + vector intersection(vector& nums1, vector& nums2) { + unordered_map seen; + for (int num : nums1) { + seen[num] = 1; + } + + vector res; + for (int num : nums2) { + if (seen[num] == 1) { + seen[num] = 0; + res.push_back(num); + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums1 + * @param {number[]} nums2 + * @return {number[]} + */ + intersection(nums1, nums2) { + const seen = {}; + for (const num of nums1) { + seen[num] = 1; + } + + const res = []; + for (const num of nums2) { + if (seen[num] === 1) { + seen[num] = 0; + res.push(num); + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n + m)$ +* Space complexity: $O(n)$ + +> Where $n$ is the size of the array $nums1$ and $m$ is the size of the array $nums2$. + +--- + +## 5. Hash Set (Optimal) + +::tabs-start + +```python +class Solution: + def intersection(self, nums1: List[int], nums2: List[int]) -> List[int]: + seen = set(nums1) + + res = [] + for num in nums2: + if num in seen: + res.append(num) + seen.remove(num) + return res +``` + +```java +public class Solution { + public int[] intersection(int[] nums1, int[] nums2) { + Set seen = new HashSet<>(); + for (int num : nums1) { + seen.add(num); + } + + List res = new ArrayList<>(); + for (int num : nums2) { + if (seen.contains(num)) { + res.add(num); + seen.remove(num); + } + } + + return res.stream().mapToInt(Integer::intValue).toArray(); + } +} +``` + +```cpp +class Solution { +public: + vector intersection(vector& nums1, vector& nums2) { + unordered_set seen(nums1.begin(), nums1.end()); + vector res; + + for (int num : nums2) { + if (seen.count(num)) { + res.push_back(num); + seen.erase(num); + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums1 + * @param {number[]} nums2 + * @return {number[]} + */ + intersection(nums1, nums2) { + const seen = new Set(nums1); + const res = []; + + for (const num of nums2) { + if (seen.has(num)) { + res.push(num); + seen.delete(num); + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n + m)$ +* Space complexity: $O(n)$ + +> Where $n$ is the size of the array $nums1$ and $m$ is the size of the array $nums2$. + +--- + +## 6. Built-In Functions + +::tabs-start + +```python +class Solution: + def intersection(self, nums1: List[int], nums2: List[int]) -> List[int]: + return list(set(nums1) & set(nums2)) +``` + +```java +public class Solution { + public int[] intersection(int[] nums1, int[] nums2) { + Set set1 = new HashSet<>(); + for (Integer n : nums1) { + set1.add(n); + } + + Set set2 = new HashSet<>(); + for (Integer n : nums2) { + set2.add(n); + } + + set1.retainAll(set2); + int[] res= new int[set1.size()]; + int idx = 0; + for (int s : set1) { + res[idx++] = s; + } + return res; + } +} +``` + +```cpp +class Solution { +public: + vector intersection(vector& nums1, vector& nums2) { + set set1(nums1.begin(), nums1.end()), set2(nums2.begin(), nums2.end()); + vector res; + set_intersection(set1.begin(), set1.end(), set2.begin(), set2.end(), back_inserter(res)); + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums1 + * @param {number[]} nums2 + * @return {number[]} + */ + intersection(nums1, nums2) { + const set2 = new Set(nums2); + return [...new Set(nums1)].filter(num => set2.has(num)); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n + m)$ in average case, $O(n * m)$ in worst case. +* Space complexity: $O(n + m)$ + +> Where $n$ is the size of the array $nums1$ and $m$ is the size of the array $nums2$. \ No newline at end of file diff --git a/articles/largest-3-same-digit-number-in-string.md b/articles/largest-3-same-digit-number-in-string.md new file mode 100644 index 000000000..f96e85ee3 --- /dev/null +++ b/articles/largest-3-same-digit-number-in-string.md @@ -0,0 +1,255 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def largestGoodInteger(self, num: str) -> str: + res = "" + val = 0 + + for i in range(len(num) - 2): + if num[i] == num[i + 1] == num[i + 2]: + tmp = num[i : i + 3] + if val <= int(tmp): + val = int(tmp) + res = tmp + + return res +``` + +```java +public class Solution { + public String largestGoodInteger(String num) { + String res = ""; + int val = 0; + + for (int i = 0; i < num.length() - 2; i++) { + if (num.charAt(i) == num.charAt(i + 1) && + num.charAt(i) == num.charAt(i + 2)) { + String tmp = num.substring(i, i + 3); + if (val <= Integer.parseInt(tmp)) { + val = Integer.parseInt(tmp); + res = tmp; + } + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + string largestGoodInteger(string num) { + string res = ""; + int val = 0; + + for (int i = 0; i < num.length() - 2; i++) { + if (num[i] == num[i + 1] && num[i] == num[i + 2]) { + string tmp = num.substr(i, 3); + if (val <= stoi(tmp)) { + val = stoi(tmp); + res = tmp; + } + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} num + * @return {string} + */ + largestGoodInteger(num) { + let res = ""; + let val = 0; + + for (let i = 0; i < num.length - 2; i++) { + if (num[i] === num[i + 1] && num[i] === num[i + 2]) { + const tmp = num.slice(i, i + 3); + if (val <= parseInt(tmp)) { + val = parseInt(tmp); + res = tmp; + } + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ + +--- + +## 2. Iteration + +::tabs-start + +```python +class Solution: + def largestGoodInteger(self, num: str) -> str: + res = "0" + + for i in range(len(num) - 2): + if num[i] == num[i + 1] == num[i + 2]: + res = max(res, num[i : i + 3]) + + return "" if res == "0" else res +``` + +```java +public class Solution { + public String largestGoodInteger(String num) { + String res = ""; + + for (int i = 0; i < num.length() - 2; i++) { + if (num.charAt(i) == num.charAt(i + 1) && + num.charAt(i) == num.charAt(i + 2)) { + String curr = num.substring(i, i + 3); + if (curr.compareTo(res) > 0) { + res = curr; + } + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + string largestGoodInteger(string num) { + string res = "0"; + + for (int i = 0; i < num.length() - 2; i++) { + if (num[i] == num[i + 1] && num[i] == num[i + 2]) { + res = max(res, num.substr(i, 3)); + } + } + + return res == "0" ? "" : res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} num + * @return {string} + */ + largestGoodInteger(num) { + let res = "0"; + + for (let i = 0; i < num.length - 2; i++) { + if (num[i] === num[i + 1] && num[i] === num[i + 2]) { + res = res > num.slice(i, i + 3) ? res : num.slice(i, i + 3); + } + } + + return res === "0" ? "" : res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ + +--- + +## 3. Iteration (Optimal) + +::tabs-start + +```python +class Solution: + def largestGoodInteger(self, num: str) -> str: + res = -1 + + for i in range(len(num) - 2): + if num[i] == num[i + 1] == num[i + 2]: + res = max(res, int(num[i])) + return str(res) * 3 if res != -1 else "" +``` + +```java +public class Solution { + public String largestGoodInteger(String num) { + int res = -1; + + for (int i = 0; i < num.length() - 2; i++) { + if (num.charAt(i) == num.charAt(i + 1) && + num.charAt(i) == num.charAt(i + 2)) { + res = Math.max(res, num.charAt(i) - '0'); + } + } + + return res != -1 ? String.valueOf(res).repeat(3) : ""; + } +} +``` + +```cpp +class Solution { +public: + string largestGoodInteger(string num) { + int res = -1; + + for (int i = 0; i < num.length() - 2; i++) { + if (num[i] == num[i + 1] && num[i] == num[i + 2]) { + res = max(res, num[i] - '0'); + } + } + + return res != -1 ? string(3, res + '0') : ""; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} num + * @return {string} + */ + largestGoodInteger(num) { + let res = -1; + + for (let i = 0; i < num.length - 2; i++) { + if (num[i] === num[i + 1] && num[i] === num[i + 2]) { + res = Math.max(res, Number(num[i])); + } + } + + return res !== -1 ? String(res).repeat(3) : ""; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/largest-number.md b/articles/largest-number.md new file mode 100644 index 000000000..5b1822fb5 --- /dev/null +++ b/articles/largest-number.md @@ -0,0 +1,188 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def largestNumber(self, nums: List[int]) -> str: + arr = [str(num) for num in nums] + + res = [] + while arr: + maxi = 0 + for i in range(1, len(arr)): + if arr[i] + arr[maxi] > arr[maxi] + arr[i]: + maxi = i + res.append(arr[maxi]) + arr.pop(maxi) + + result = "".join(res) + return result if result[0] != '0' else '0' +``` + +```java +public class Solution { + public String largestNumber(int[] nums) { + List arr = new ArrayList<>(); + for (int num : nums) { + arr.add(String.valueOf(num)); + } + + StringBuilder res = new StringBuilder(); + while (!arr.isEmpty()) { + int maxi = 0; + for (int i = 1; i < arr.size(); i++) { + if ((arr.get(i) + arr.get(maxi)).compareTo(arr.get(maxi) + arr.get(i)) > 0) { + maxi = i; + } + } + res.append(arr.get(maxi)); + arr.remove(maxi); + } + + String result = res.toString(); + return result.charAt(0) == '0' ? "0" : result; + } +} +``` + +```cpp +class Solution { +public: + string largestNumber(vector& nums) { + vector arr; + for (int num : nums) { + arr.push_back(to_string(num)); + } + + string res; + while (!arr.empty()) { + int maxi = 0; + for (int i = 1; i < arr.size(); i++) { + if (arr[i] + arr[maxi] > arr[maxi] + arr[i]) { + maxi = i; + } + } + res += arr[maxi]; + arr.erase(arr.begin() + maxi); + } + + return res[0] == '0' ? "0" : res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {string} + */ + largestNumber(nums) { + let arr = nums.map(String); + + let res = []; + while (arr.length > 0) { + let maxi = 0; + for (let i = 1; i < arr.length; i++) { + if (arr[i] + arr[maxi] > arr[maxi] + arr[i]) { + maxi = i; + } + } + res.push(arr[maxi]); + arr.splice(maxi, 1); + } + + let result = res.join(""); + return result[0] === "0" ? "0" : result; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * N)$ +* Space complexity: $O(N)$ + +> Where $n$ is the size of the array $nums$ and $N$ is the total number of digits in the array $nums$. + +--- + +## 2. Sorting + +::tabs-start + +```python +class Solution: + def largestNumber(self, nums: List[int]) -> str: + for i, num in enumerate(nums): + nums[i] = str(num) + + def compare(n1, n2): + if n1 + n2 > n2 + n1: + return -1 + else: + return 1 + nums = sorted(nums, key=cmp_to_key(compare)) + return str(int("".join(nums))) +``` + +```java +public class Solution { + public String largestNumber(int[] nums) { + String[] arr = Arrays.stream(nums).mapToObj(String::valueOf).toArray(String[]::new); + Arrays.sort(arr, (a, b) -> (b + a).compareTo(a + b)); + String res = String.join("", arr); + return res.charAt(0) == '0' ? "0" : res; + } +} +``` + +```cpp +class Solution { +public: + string largestNumber(vector& nums) { + vector arr; + for (int num : nums) { + arr.push_back(to_string(num)); + } + + sort(arr.begin(), arr.end(), [](string& a, string& b) { + return a + b > b + a; + }); + + string res; + for (string& num : arr) { + res += num; + } + + return res[0] == '0' ? "0" : res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {string} + */ + largestNumber(nums) { + let arr = nums.map(String); + arr.sort((a, b) => ((b + a) - (a + b))); + let res = arr.join(""); + return res[0] === "0" ? "0" : res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(N \log N)$ +* Space complexity: $O(N)$ + +> Where $N$ is the total number of digits in the array $nums$. \ No newline at end of file diff --git a/articles/largest-substring-between-two-equal-characters.md b/articles/largest-substring-between-two-equal-characters.md new file mode 100644 index 000000000..dc4108a60 --- /dev/null +++ b/articles/largest-substring-between-two-equal-characters.md @@ -0,0 +1,380 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def maxLengthBetweenEqualCharacters(self, s: str) -> int: + n = len(s) + res = -1 + + for i in range(n): + for j in range(i + 1, n): + if s[i] == s[j]: + res = max(res, j - i - 1) + return res +``` + +```java +public class Solution { + public int maxLengthBetweenEqualCharacters(String s) { + int n = s.length(); + int res = -1; + + for (int i = 0; i < n; i++) { + for (int j = i + 1; j < n; j++) { + if (s.charAt(i) == s.charAt(j)) { + res = Math.max(res, j - i - 1); + } + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int maxLengthBetweenEqualCharacters(string s) { + int n = s.size(); + int res = -1; + + for (int i = 0; i < n; i++) { + for (int j = i + 1; j < n; j++) { + if (s[i] == s[j]) { + res = max(res, j - i - 1); + } + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {number} + */ + maxLengthBetweenEqualCharacters(s) { + const n = s.length; + let res = -1; + + for (let i = 0; i < n; i++) { + for (let j = i + 1; j < n; j++) { + if (s[i] === s[j]) { + res = Math.max(res, j - i - 1); + } + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(1)$ + +--- + +## 2. First And Last Index + +::tabs-start + +```python +class Solution: + def maxLengthBetweenEqualCharacters(self, s: str) -> int: + res = -1 + firstIdx = {} + lastIdx = {} + + for i, c in enumerate(s): + if c not in firstIdx: + firstIdx[c] = i + else: + lastIdx[c] = i + + for c in lastIdx: + res = max(res, lastIdx[c] - firstIdx[c] - 1) + + return res +``` + +```java +public class Solution { + public int maxLengthBetweenEqualCharacters(String s) { + Map firstIdx = new HashMap<>(); + Map lastIdx = new HashMap<>(); + int res = -1; + + for (int i = 0; i < s.length(); i++) { + char c = s.charAt(i); + if (!firstIdx.containsKey(c)) { + firstIdx.put(c, i); + } else { + lastIdx.put(c, i); + } + } + + for (char c : lastIdx.keySet()) { + res = Math.max(res, lastIdx.get(c) - firstIdx.get(c) - 1); + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int maxLengthBetweenEqualCharacters(string s) { + unordered_map firstIdx, lastIdx; + int res = -1; + + for (int i = 0; i < s.size(); i++) { + if (firstIdx.find(s[i]) == firstIdx.end()) { + firstIdx[s[i]] = i; + } else { + lastIdx[s[i]] = i; + } + } + + for (auto& [c, idx] : lastIdx) { + res = max(res, lastIdx[c] - firstIdx[c] - 1); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {number} + */ + maxLengthBetweenEqualCharacters(s) { + const firstIdx = new Map(); + const lastIdx = new Map(); + let res = -1; + + for (let i = 0; i < s.length; i++) { + if (!firstIdx.has(s[i])) { + firstIdx.set(s[i], i); + } else { + lastIdx.set(s[i], i); + } + } + + for (const [char, idx] of lastIdx) { + res = Math.max(res, lastIdx.get(char) - firstIdx.get(char) - 1); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ since we have at most $26$ different characters. + +--- + +## 3. First Index (Hash Map) + +::tabs-start + +```python +class Solution: + def maxLengthBetweenEqualCharacters(self, s: str) -> int: + char_index = {} # char -> first index + res = -1 + + for i, c in enumerate(s): + if c in char_index: + res = max(res, i - char_index[c] - 1) + else: + char_index[c] = i + + return res +``` + +```java +public class Solution { + public int maxLengthBetweenEqualCharacters(String s) { + Map charIndex = new HashMap<>(); + int res = -1; + + for (int i = 0; i < s.length(); i++) { + char c = s.charAt(i); + if (charIndex.containsKey(c)) { + res = Math.max(res, i - charIndex.get(c) - 1); + } else { + charIndex.put(c, i); + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int maxLengthBetweenEqualCharacters(string s) { + unordered_map charIndex; + int res = -1; + + for (int i = 0; i < s.size(); i++) { + if (charIndex.find(s[i]) != charIndex.end()) { + res = max(res, i - charIndex[s[i]] - 1); + } else { + charIndex[s[i]] = i; + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {number} + */ + maxLengthBetweenEqualCharacters(s) { + const charIndex = new Map(); + let res = -1; + + for (let i = 0; i < s.length; i++) { + if (charIndex.has(s[i])) { + res = Math.max(res, i - charIndex.get(s[i]) - 1); + } else { + charIndex.set(s[i], i); + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ since we have at most $26$ different characters. + +--- + +## 4. First Index (Array) + +::tabs-start + +```python +class Solution: + def maxLengthBetweenEqualCharacters(self, s: str) -> int: + firstIdx = [-1] * 26 + res = -1 + + for i, c in enumerate(s): + j = ord(c) - ord('a') + if firstIdx[j] != -1: + res = max(res, i - firstIdx[j] - 1) + else: + firstIdx[j] = i + + return res +``` + +```java +public class Solution { + public int maxLengthBetweenEqualCharacters(String s) { + int[] firstIdx = new int[26]; + for (int i = 0; i < 26; i++) { + firstIdx[i] = -1; + } + int res = -1; + + for (int i = 0; i < s.length(); i++) { + int j = s.charAt(i) - 'a'; + if (firstIdx[j] != -1) { + res = Math.max(res, i - firstIdx[j] - 1); + } else { + firstIdx[j] = i; + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int maxLengthBetweenEqualCharacters(string s) { + int firstIdx[26]; + fill(begin(firstIdx), end(firstIdx), -1); + int res = -1; + + for (int i = 0; i < s.size(); i++) { + int j = s[i] - 'a'; + if (firstIdx[j] != -1) { + res = max(res, i - firstIdx[j] - 1); + } else { + firstIdx[j] = i; + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {number} + */ + maxLengthBetweenEqualCharacters(s) { + const firstIdx = Array(26).fill(-1); + let res = -1; + + for (let i = 0; i < s.length; i++) { + const j = s.charCodeAt(i) - 'a'.charCodeAt(0); + if (firstIdx[j] !== -1) { + res = Math.max(res, i - firstIdx[j] - 1); + } else { + firstIdx[j] = i; + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ since we have at most $26$ different characters. \ No newline at end of file diff --git a/articles/longest-common-prefix.md b/articles/longest-common-prefix.md index c7f5658fb..27c30160f 100644 --- a/articles/longest-common-prefix.md +++ b/articles/longest-common-prefix.md @@ -1,4 +1,4 @@ -## 1. Iteration - I +## 1. Horizontal Scanning ::tabs-start @@ -89,7 +89,7 @@ class Solution { --- -## 2. Iteration - II +## 2. Vertical Scanning ::tabs-start diff --git a/articles/maximum-product-difference-between-two-pairs.md b/articles/maximum-product-difference-between-two-pairs.md new file mode 100644 index 000000000..e5425c2f8 --- /dev/null +++ b/articles/maximum-product-difference-between-two-pairs.md @@ -0,0 +1,255 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def maxProductDifference(self, nums: List[int]) -> int: + n, res = len(nums), 0 + for a in range(n): + for b in range(n): + if a == b: continue + for c in range(n): + if a == c or b == c: continue + for d in range(n): + if a == d or b == d or c == d: continue + res = max(res, nums[a] * nums[b] - nums[c] * nums[d]) + return res +``` + +```java +public class Solution { + public int maxProductDifference(int[] nums) { + int n = nums.length, res = 0; + for (int a = 0; a < n; a++) { + for (int b = 0; b < n; b++) { + if (a == b) continue; + for (int c = 0; c < n; c++) { + if (a == c || b == c) continue; + for (int d = 0; d < n; d++) { + if (a == d || b == d || c == d) continue; + res = Math.max(res, nums[a] * nums[b] - nums[c] * nums[d]); + } + } + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int maxProductDifference(vector& nums) { + int n = nums.size(), res = 0; + for (int a = 0; a < n; a++) { + for (int b = 0; b < n; b++) { + if (a == b) continue; + for (int c = 0; c < n; c++) { + if (a == c || b == c) continue; + for (int d = 0; d < n; d++) { + if (a == d || b == d || c == d) continue; + res = max(res, nums[a] * nums[b] - nums[c] * nums[d]); + } + } + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + maxProductDifference(nums) { + const n = nums.length; + let res = 0; + for (let a = 0; a < n; a++) { + for (let b = 0; b < n; b++) { + if (a === b) continue; + for (let c = 0; c < n; c++) { + if (a === c || b === c) continue; + for (let d = 0; d < n; d++) { + if (a === d || b === d || c === d) continue; + res = Math.max(res, nums[a] * nums[b] - nums[c] * nums[d]); + } + } + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 4)$ +* Space complexity: $O(1)$ + +--- + +## 2. Sorting + +::tabs-start + +```python +class Solution: + def maxProductDifference(self, nums: List[int]) -> int: + nums.sort() + return nums[-1] * nums[-2] - nums[0] * nums[1] +``` + +```java +public class Solution { + public int maxProductDifference(int[] nums) { + Arrays.sort(nums); + return nums[nums.length - 1] * nums[nums.length - 2] - nums[0] * nums[1]; + } +} +``` + +```cpp +class Solution { +public: + int maxProductDifference(vector& nums) { + sort(nums.begin(), nums.end()); + return nums[nums.size() - 1] * nums[nums.size() - 2] - nums[0] * nums[1]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + maxProductDifference(nums) { + nums.sort((a, b) => a - b); + return nums[nums.length - 1] * nums[nums.length - 2] - nums[0] * nums[1]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(1)$ or $O(n)$ depending on the sorting algorithm. + +--- + +## 3. Two Maximums and Two Minimums + +::tabs-start + +```python +class Solution: + def maxProductDifference(self, nums: List[int]) -> int: + max1 = max2 = 0 + min1 = min2 = float('inf') + + for num in nums: + if num > max1: + max1, max2 = num, max1 + elif num > max2: + max2 = num + if num < min1: + min1, min2 = num, min1 + elif num < min2: + min2 = num + + return (max1 * max2) - (min1 * min2) +``` + +```java +public class Solution { + public int maxProductDifference(int[] nums) { + int max1 = 0, max2 = 0; + int min1 = Integer.MAX_VALUE, min2 = Integer.MAX_VALUE; + for (int num : nums) { + if (num > max1) { + max2 = max1; + max1 = num; + } else if (num > max2) { + max2 = num; + } + if (num < min1) { + min2 = min1; + min1 = num; + } else if (num < min2) { + min2 = num; + } + } + return (max1 * max2) - (min1 * min2); + } +} +``` + +```cpp +class Solution { +public: + int maxProductDifference(vector& nums) { + int max1 = 0, max2 = 0; + int min1 = INT_MAX, min2 = INT_MAX; + for (int num : nums) { + if (num > max1) { + max2 = max1; + max1 = num; + } else if (num > max2) { + max2 = num; + } + if (num < min1) { + min2 = min1; + min1 = num; + } else if (num < min2) { + min2 = num; + } + } + return (max1 * max2) - (min1 * min2); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + maxProductDifference(nums) { + let max1 = 0, max2 = 0; + let min1 = Infinity, min2 = Infinity; + for (const num of nums) { + if (num > max1) { + max2 = max1; + max1 = num; + } else if (num > max2) { + max2 = num; + } + if (num < min1) { + min2 = min1; + min1 = num; + } else if (num < min2) { + min2 = num; + } + } + return (max1 * max2) - (min1 * min2); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/maximum-product-of-the-length-of-two-palindromic-subsequences.md b/articles/maximum-product-of-the-length-of-two-palindromic-subsequences.md new file mode 100644 index 000000000..bd17730e3 --- /dev/null +++ b/articles/maximum-product-of-the-length-of-two-palindromic-subsequences.md @@ -0,0 +1,854 @@ +## 1. Recursion (Backtracking) + +::tabs-start + +```python +class Solution: + def maxProduct(self, s: str) -> int: + def isPal(s): + i, j = 0, len(s) - 1 + while i < j: + if s[i] != s[j]: + return False + i += 1 + j -= 1 + return True + + res = 0 + def rec(i, seq1, seq2): + nonlocal res + if i == len(s): + if isPal(seq1) and isPal(seq2): + res = max(res, len(seq1) * len(seq2)) + return + + rec(i + 1, seq1, seq2) + rec(i + 1, seq1 + s[i], seq2) + rec(i + 1, seq1, seq2 + s[i]) + + rec(0, "", "") + return res +``` + +```java +public class Solution { + private boolean isPal(String s) { + int i = 0, j = s.length() - 1; + while (i < j) { + if (s.charAt(i) != s.charAt(j)) return false; + i++; + j--; + } + return true; + } + + public void rec(int i, String s, StringBuilder seq1, StringBuilder seq2, int[] res) { + if (i == s.length()) { + if (isPal(seq1.toString()) && isPal(seq2.toString())) { + res[0] = Math.max(res[0], seq1.length() * seq2.length()); + } + return; + } + + rec(i + 1, s, seq1, seq2, res); + seq1.append(s.charAt(i)); + rec(i + 1, s, seq1, seq2, res); + seq1.deleteCharAt(seq1.length() - 1); + + seq2.append(s.charAt(i)); + rec(i + 1, s, seq1, seq2, res); + seq2.deleteCharAt(seq2.length() - 1); + } + + public int maxProduct(String s) { + int[] res = new int[1]; + rec(0, s, new StringBuilder(), new StringBuilder(), res); + return res[0]; + } +} +``` + +```cpp +class Solution { +public: + bool isPal(const string &s) { + int i = 0, j = s.length() - 1; + while (i < j) { + if (s[i] != s[j]) return false; + i++; + j--; + } + return true; + } + + void rec(int i, string& s, string& seq1, string& seq2, int &res) { + if (i == s.length()) { + if (isPal(seq1) && isPal(seq2)) { + res = max(res, (int)seq1.length() * (int)seq2.length()); + } + return; + } + + rec(i + 1, s, seq1, seq2, res); + seq1.push_back(s[i]); + rec(i + 1, s, seq1, seq2, res); + seq1.pop_back(); + seq2.push_back(s[i]); + rec(i + 1, s, seq1, seq2, res); + seq2.pop_back(); + } + + int maxProduct(string s) { + int res = 0; + string seq1 = "", seq2 = ""; + rec(0, s, seq1, seq2, res); + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {number} + */ + maxProduct(s) { + const isPal = (str) => { + let i = 0, j = str.length - 1; + while (i < j) { + if (str[i] !== str[j]) return false; + i++; + j--; + } + return true; + }; + + let res = 0; + + const rec = (i, seq1, seq2) => { + if (i === s.length) { + if (isPal(seq1) && isPal(seq2)) { + res = Math.max(res, seq1.length * seq2.length); + } + return; + } + + rec(i + 1, seq1, seq2); + rec(i + 1, seq1 + s[i], seq2); + rec(i + 1, seq1, seq2 + s[i]); + }; + + rec(0, "", ""); + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * 3 ^ n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Bit Mask + +::tabs-start + +```python +class Solution: + def maxProduct(self, s: str) -> int: + def isPal(s): + i, j = 0, len(s) - 1 + while i < j: + if s[i] != s[j]: + return False + i += 1 + j -= 1 + return True + + N, pali = len(s), {} + + for mask in range(1, 1 << N): + subseq = "" + for i in range(N): + if mask & (1 << i): + subseq += s[i] + + if isPal(subseq): + pali[mask] = len(subseq) + + res = 0 + for m1 in pali: + for m2 in pali: + if m1 & m2 == 0: + res = max(res, pali[m1] * pali[m2]) + + return res +``` + +```java +public class Solution { + public int maxProduct(String s) { + int N = s.length(); + int res = 0; + Map pali = new HashMap<>(); + + for (int mask = 1; mask < (1 << N); mask++) { + StringBuilder subseq = new StringBuilder(); + for (int i = 0; i < N; i++) { + if ((mask & (1 << i)) != 0) { + subseq.append(s.charAt(i)); + } + } + + if (isPal(subseq.toString())) { + pali.put(mask, subseq.length()); + } + } + + for (int m1 : pali.keySet()) { + for (int m2 : pali.keySet()) { + if ((m1 & m2) == 0) { + res = Math.max(res, pali.get(m1) * pali.get(m2)); + } + } + } + + return res; + } + + private boolean isPal(String s) { + int i = 0, j = s.length() - 1; + while (i < j) { + if (s.charAt(i) != s.charAt(j)) { + return false; + } + i++; + j--; + } + return true; + } +} +``` + +```cpp +class Solution { +public: + int maxProduct(string s) { + int N = s.length(); + int res = 0; + unordered_map pali; + + for (int mask = 1; mask < (1 << N); mask++) { + string subseq = ""; + for (int i = 0; i < N; i++) { + if (mask & (1 << i)) { + subseq += s[i]; + } + } + + if (isPal(subseq)) { + pali[mask] = subseq.length(); + } + } + + for (auto& m1 : pali) { + for (auto& m2 : pali) { + if ((m1.first & m2.first) == 0) { + res = max(res, m1.second * m2.second); + } + } + } + + return res; + } + +private: + bool isPal(const string& s) { + int i = 0, j = s.length() - 1; + while (i < j) { + if (s[i] != s[j]) { + return false; + } + i++; + j--; + } + return true; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {number} + */ + maxProduct(s) { + const isPal = (str) => { + let i = 0, j = str.length - 1; + while (i < j) { + if (str[i] !== str[j]) return false; + i++; + j--; + } + return true; + }; + + const N = s.length; + let res = 0; + const pali = new Map(); + + for (let mask = 1; mask < (1 << N); mask++) { + let subseq = ""; + for (let i = 0; i < N; i++) { + if ((mask & (1 << i)) !== 0) { + subseq += s[i]; + } + } + + if (isPal(subseq)) { + pali.set(mask, subseq.length); + } + } + + for (let [m1, len1] of pali) { + for (let [m2, len2] of pali) { + if ((m1 & m2) === 0) { + res = Math.max(res, len1 * len2); + } + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(4 ^ n)$ +* Space complexity: $O(2 ^ n)$ + +--- + +## 3. Bit Mask + Longest Pallindromic Subsequence + +::tabs-start + +```python +class Solution: + def longestPalindromeSubseq(self, s: str) -> int: + n = len(s) + if n == 0: + return 0 + + dp = [1] * n + for i in range(n - 1, -1, -1): + prev = 0 + for j in range(i + 1, n): + tmp = dp[j] + if s[i] == s[j]: + dp[j] = 2 + prev + else: + dp[j] = max(dp[j - 1], dp[j]) + prev = tmp + return dp[n - 1] + + def maxProduct(self, s: str) -> int: + n = len(s) + res = 0 + + for i in range(1, 1 << n): + seq1, seq2 = [], [] + + for j in range(n): + if (i & (1 << j)) != 0: + seq1.append(s[j]) + else: + seq2.append(s[j]) + + if not self.isPal(seq1): + continue + + lps = self.longestPalindromeSubseq(''.join(seq2)) + res = max(res, len(seq1) * lps) + + return res + + def isPal(self, s: str) -> bool: + i, j = 0, len(s) - 1 + while i < j: + if s[i] != s[j]: + return False + i += 1 + j -= 1 + return True +``` + +```java +public class Solution { + public int longestPalindromeSubseq(String s) { + int n = s.length(); + if (n == 0) return 0; + + int[] dp = new int[n]; + Arrays.fill(dp, 1); + for (int i = n - 1; i >= 0; i--) { + int prev = 0; + for (int j = i + 1; j < n; j++) { + int tmp = dp[j]; + if (s.charAt(i) == s.charAt(j)) { + dp[j] = 2 + prev; + } else { + dp[j] = Math.max(dp[j - 1], dp[j]); + } + prev = tmp; + } + } + return dp[n - 1]; + } + + public int maxProduct(String s) { + int n = s.length(); + int res = 0; + + for (int i = 1; i < (1 << n); i++) { + StringBuilder seq1 = new StringBuilder(), seq2 = new StringBuilder(); + + for (int j = 0; j < n; j++) { + char c = s.charAt(j); + if ((i & (1 << j)) != 0) seq1.append(c); + else seq2.append(c); + } + + if (!isPal(seq1.toString())) continue; + int lps = longestPalindromeSubseq(seq2.toString()); + res = Math.max(res, seq1.length() * lps); + } + + return res; + } + + private boolean isPal(String s) { + int i = 0, j = s.length() - 1; + while (i < j) { + if (s.charAt(i) != s.charAt(j)) { + return false; + } + i++; + j--; + } + return true; + } +} +``` + +```cpp +class Solution { +public: + int longestPalindromeSubseq(string& s) { + int n = s.length(); + if (n == 0) return 0; + + vector dp(n, 1); + for (int i = n - 1; i >= 0; i--) { + int prev = 0; + for (int j = i + 1; j < n; j++) { + int tmp = dp[j]; + if (s[i] == s[j]) { + dp[j] = 2 + prev; + } else { + dp[j] = max(dp[j - 1], dp[j]); + } + prev = tmp; + } + } + return dp[n - 1]; + } + + int maxProduct(string s) { + int n = s.length(); + int res = 0; + + for (int i = 1; i < (1 << n); i++) { + string seq1 = "", seq2 = ""; + + for (int j = 0; j < n; j++) { + if ((i & (1 << j)) != 0) seq1 += s[j]; + else seq2 += s[j]; + } + + if (!isPal(seq1)) continue; + + int lps = longestPalindromeSubseq(seq2); + res = max(res, int(seq1.length()) * lps); + } + + return res; + } + + bool isPal(string& s) { + int i = 0, j = s.length() - 1; + while (i < j) { + if (s[i] != s[j]) { + return false; + } + i++; + j--; + } + return true; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {number} + */ + longestPalindromeSubseq(s) { + const n = s.length; + if (n === 0) return 0; + + const dp = new Array(n).fill(1); + for (let i = n - 1; i >= 0; i--) { + let prev = 0; + for (let j = i + 1; j < n; j++) { + let tmp = dp[j]; + if (s[i] === s[j]) { + dp[j] = 2 + prev; + } else { + dp[j] = Math.max(dp[j - 1], dp[j]); + } + prev = tmp; + } + } + return dp[n - 1]; + } + + /** + * @param {string} s + * @return {number} + */ + maxProduct(s) { + const n = s.length; + let res = 0; + + for (let i = 1; i < (1 << n); i++) { + let seq1 = "", seq2 = ""; + + for (let j = 0; j < n; j++) { + if ((i & (1 << j)) !== 0) seq1 += s[j]; + else seq2 += s[j]; + } + + if (!this.isPal(seq1)) continue; + + const lps = this.longestPalindromeSubseq(seq2); + res = Math.max(res, seq1.length * lps); + } + + return res; + } + + /** + * @param {string} s + * @return {boolean} + */ + isPal(s) { + let i = 0, j = s.length - 1; + while (i < j) { + if (s[i] !== s[j]) { + return false; + } + i++; + j--; + } + return true; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2 * 2 ^ n)$ +* Space complexity: $O(n)$ + +--- + +## 4. Bit Mask + LPS (Optimal) + +::tabs-start + +```python +class Solution: + def longestPalindromeSubseq(self, s: str, mask: int, dp: List[int]) -> int: + n = len(s) + for i in range(n - 1, -1, -1): + if (mask & (1 << i)) != 0: + continue + + prev = 0 + for j in range(i + 1, n): + tmp = dp[j] + if (mask & (1 << j)) == 0 and s[i] == s[j]: + dp[j] = 2 + prev + else: + dp[j] = max(dp[j - 1], dp[j]) + prev = tmp + return dp[-1] + + def maxProduct(self, s: str) -> int: + n = len(s) + res = 0 + dp = [1] * n + + for i in range(1, 1 << n): + m1 = self.palsize(s, i) + if m1 == 0: + continue + + for j in range(n): + if (i & (1 << j)) == 0: + dp[j] = 1 + else: + dp[j] = 0 + + m2 = self.longestPalindromeSubseq(s, i, dp) + res = max(res, m1 * m2) + + return res + + def palsize(self, s: str, mask: int) -> int: + i, j = 0, len(s) - 1 + res = 0 + while i <= j: + if (mask & (1 << i)) == 0: + i += 1 + elif (mask & (1 << j)) == 0: + j -= 1 + else: + if s[i] != s[j]: + return 0 + res += 1 if i == j else 2 + i += 1 + j -= 1 + return res +``` + +```java +public class Solution { + public int longestPalindromeSubseq(String s, int mask, int[] dp) { + int n = s.length(); + for (int i = n - 1; i >= 0; i--) { + if ((mask & (1 << i)) != 0) continue; + + int prev = 0; + for (int j = i + 1; j < n; j++) { + int tmp = dp[j]; + if ((mask & (1 << j)) == 0 && s.charAt(i) == s.charAt(j)) { + dp[j] = 2 + prev; + } else { + dp[j] = Math.max(dp[j - 1], dp[j]); + } + prev = tmp; + } + } + return dp[n - 1]; + } + + public int maxProduct(String s) { + int n = s.length(); + int res = 0; + int[] dp = new int[n]; + + for (int i = 1; i < (1 << n); i++) { + int m1 = palsize(s, i); + if (m1 == 0) continue; + + for (int j = 0; j < n; j++) { + if ((i & (1 << j)) == 0) { + dp[j] = 1; + } else { + dp[j] = 0; + } + } + int m2 = longestPalindromeSubseq(s, i, dp); + res = Math.max(res, m1 * m2); + } + return res; + } + + public int palsize(String s, int mask) { + int i = 0, j = s.length() - 1; + int res = 0; + while (i <= j) { + if ((mask & (1 << i)) == 0) i++; + else if ((mask & (1 << j)) == 0) j--; + else { + if (s.charAt(i) != s.charAt(j)) return 0; + res += (i == j) ? 1 : 2; + i++; + j--; + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int longestPalindromeSubseq(string& s, int mask, vector& dp) { + int n = s.length(); + for (int i = n - 1; i >= 0; i--) { + if ((mask & (1 << i)) != 0) continue; + + int prev = 0; + for (int j = i + 1; j < n; j++) { + int tmp = dp[j]; + if ((mask & (1 << j)) == 0 && s[i] == s[j]) { + dp[j] = 2 + prev; + } else { + dp[j] = max(dp[j - 1], dp[j]); + } + prev = tmp; + } + } + return dp[n - 1]; + } + + int maxProduct(string s) { + int n = s.length(); + int res = 0; + vector dp(n, 1); + + for (int i = 1; i < (1 << n); i++) { + int m1 = palsize(s, i); + if (m1 == 0) continue; + + for (int j = 0; j < n; j++) { + if ((i & (1 << j)) == 0) { + dp[j] = 1; + } else { + dp[j] = 0; + } + } + int m2 = longestPalindromeSubseq(s, i, dp); + res = max(res, m1 * m2); + } + + return res; + } + + int palsize(string& s, int mask) { + int i = 0, j = s.length() - 1; + int res = 0; + while (i <= j) { + if ((mask & (1 << i)) == 0) i++; + else if ((mask & (1 << j)) == 0) j--; + else { + if (s[i] != s[j]) return 0; + res += (i == j) ? 1 : 2; + i++; + j--; + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @param {number} mask + * @param {number[]} dp + * @return {number} + */ + longestPalindromeSubseq(s, mask, dp) { + const n = s.length; + for (let i = n - 1; i >= 0; i--) { + if ((mask & (1 << i)) !== 0) continue; + + let prev = 0; + for (let j = i + 1; j < n; j++) { + const tmp = dp[j]; + if ((mask & (1 << j)) === 0 && s[i] === s[j]) { + dp[j] = 2 + prev; + } else { + dp[j] = Math.max(dp[j - 1], dp[j]); + } + prev = tmp; + } + } + return dp[n - 1]; + } + + /** + * @param {string} s + * @return {boolean} + */ + maxProduct(s) { + const n = s.length; + let res = 0; + const dp = Array(n).fill(1); + + for (let i = 1; i < (1 << n); i++) { + const m1 = this.palsize(s, i); + if (m1 === 0) continue; + + for (let j = 0; j < n; j++) { + if ((i & (1 << j)) === 0) { + dp[j] = 1; + } else { + dp[j] = 0; + } + } + const m2 = this.longestPalindromeSubseq(s, i, dp); + res = Math.max(res, m1 * m2); + } + return res; + } + + /** + * @param {string} s + * @param {number} mask + * @return {number} + */ + palsize(s, mask) { + let i = 0, j = s.length - 1; + let res = 0; + while (i <= j) { + if ((mask & (1 << i)) === 0) i++; + else if ((mask & (1 << j)) === 0) j--; + else { + if (s[i] !== s[j]) return 0; + res += (i === j) ? 1 : 2; + i++; + j--; + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2 * 2 ^ n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/maximum-score-after-splitting-a-string.md b/articles/maximum-score-after-splitting-a-string.md new file mode 100644 index 000000000..04d95fb31 --- /dev/null +++ b/articles/maximum-score-after-splitting-a-string.md @@ -0,0 +1,495 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def maxScore(self, s: str) -> int: + n, res = len(s), 0 + for i in range(1, n): + left_zero = 0 + for j in range(i): + if s[j] == '0': + left_zero += 1 + right_one = 0 + for j in range(i, n): + if s[j] == '1': + right_one += 1 + res = max(res, left_zero + right_one) + return res +``` + +```java +public class Solution { + public int maxScore(String s) { + int n = s.length(), res = 0; + for (int i = 1; i < n; i++) { + int leftZero = 0, rightOne = 0; + for (int j = 0; j < i; j++) { + if (s.charAt(j) == '0') { + leftZero++; + } + } + for (int j = i; j < n; j++) { + if (s.charAt(j) == '1') { + rightOne++; + } + } + res = Math.max(res, leftZero + rightOne); + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int maxScore(string s) { + int n = s.size(), res = 0; + for (int i = 1; i < n; i++) { + int leftZero = 0, rightOne = 0; + for (int j = 0; j < i; j++) { + if (s[j] == '0') { + leftZero++; + } + } + for (int j = i; j < n; j++) { + if (s[j] == '1') { + rightOne++; + } + } + res = max(res, leftZero + rightOne); + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {number} + */ + maxScore(s) { + const n = s.length; + let res = 0; + for (let i = 1; i < n; i++) { + let leftZero = 0, rightOne = 0; + for (let j = 0; j < i; j++) { + if (s[j] === '0') { + leftZero++; + } + } + for (let j = i; j < n; j++) { + if (s[j] === '1') { + rightOne++; + } + } + res = Math.max(res, leftZero + rightOne); + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(1)$ + +--- + +## 2. Prefix & Suffix Arrays + +::tabs-start + +```python +class Solution: + def maxScore(self, s: str) -> int: + n = len(s) + left_zero = [0] * n + right_one = [0] * n + + if s[0] == '0': + left_zero[0] = 1 + for i in range(1, n): + left_zero[i] = left_zero[i - 1] + if s[i] == '0': + left_zero[i] += 1 + + if s[n - 1] == '1': + right_one[n - 1] = 1 + for i in range(n - 2, -1, -1): + right_one[i] = right_one[i + 1] + if s[i] == '1': + right_one[i] += 1 + + res = 0 + for i in range(1, n): + res = max(res, left_zero[i - 1] + right_one[i]) + return res +``` + +```java +public class Solution { + public int maxScore(String s) { + int n = s.length(); + int[] leftZero = new int[n]; + int[] rightOne = new int[n]; + + if (s.charAt(0) == '0') { + leftZero[0] = 1; + } + for (int i = 1; i < n; i++) { + leftZero[i] = leftZero[i - 1]; + if (s.charAt(i) == '0') { + leftZero[i]++; + } + } + + if (s.charAt(n - 1) == '1') { + rightOne[n - 1] = 1; + } + for (int i = n - 2; i >= 0; i--) { + rightOne[i] = rightOne[i + 1]; + if (s.charAt(i) == '1') { + rightOne[i]++; + } + } + + int res = 0; + for (int i = 1; i < n; i++) { + res = Math.max(res, leftZero[i - 1] + rightOne[i]); + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int maxScore(string s) { + int n = s.size(); + vector leftZero(n, 0), rightOne(n, 0); + + if (s[0] == '0') { + leftZero[0] = 1; + } + for (int i = 1; i < n; i++) { + leftZero[i] = leftZero[i - 1]; + if (s[i] == '0') { + leftZero[i]++; + } + } + + if (s[n - 1] == '1') { + rightOne[n - 1] = 1; + } + for (int i = n - 2; i >= 0; i--) { + rightOne[i] = rightOne[i + 1]; + if (s[i] == '1') { + rightOne[i]++; + } + } + + int res = 0; + for (int i = 1; i < n; i++) { + res = max(res, leftZero[i - 1] + rightOne[i]); + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {number} + */ + maxScore(s) { + const n = s.length; + let leftZero = new Array(n).fill(0); + let rightOne = new Array(n).fill(0); + + if (s[0] === '0') { + leftZero[0] = 1; + } + for (let i = 1; i < n; i++) { + leftZero[i] = leftZero[i - 1]; + if (s[i] === '0') { + leftZero[i]++; + } + } + + if (s[n - 1] === '1') { + rightOne[n - 1] = 1; + } + for (let i = n - 2; i >= 0; i--) { + rightOne[i] = rightOne[i + 1]; + if (s[i] === '1') { + rightOne[i]++; + } + } + + let res = 0; + for (let i = 1; i < n; i++) { + res = Math.max(res, leftZero[i - 1] + rightOne[i]); + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Iteration (Two Pass) + +::tabs-start + +```python +class Solution: + def maxScore(self, s: str) -> int: + zero = 0 + one = s.count('1') + res = 0 + + for i in range(len(s) - 1): + if s[i] == '0': + zero += 1 + else: + one -= 1 + res = max(res, zero + one) + + return res +``` + +```java +public class Solution { + public int maxScore(String s) { + int zero = 0, one = 0, res = 0; + + for (char c : s.toCharArray()) { + if (c == '1') { + one++; + } + } + + for (int i = 0; i < s.length() - 1; i++) { + if (s.charAt(i) == '0') { + zero++; + } else { + one--; + } + res = Math.max(res, zero + one); + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int maxScore(string s) { + int zero = 0, one = 0, res = 0; + + for (char c : s) { + if (c == '1') { + one++; + } + } + + for (int i = 0; i < s.size() - 1; i++) { + if (s[i] == '0') { + zero++; + } else { + one--; + } + res = max(res, zero + one); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {number} + */ + maxScore(s) { + let zero = 0, one = 0, res = 0; + + for (const c of s) { + if (c === '1') { + one++; + } + } + + for (let i = 0; i < s.length - 1; i++) { + if (s[i] === '0') { + zero++; + } else { + one--; + } + res = Math.max(res, zero + one); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ + +--- + +## 4. Iteration (One Pass) + +::tabs-start + +```python +class Solution: + def maxScore(self, s: str) -> int: + # res = Max of all (left_zeros + right_ones) + # res = Max of all (left_zeros + (total_ones - left_ones)) + # res = total_ones (constant) + Max of all (left_zeros - left_ones) + + zeros = 0 + ones = 0 + + if s[0] == '0': + zeros += 1 + else: + ones += 1 + + res = float('-inf') + for i in range(1, len(s)): + res = max(res, zeros - ones) + if s[i] == '0': + zeros += 1 + else: + ones += 1 + + return res + ones +``` + +```java +public class Solution { + public int maxScore(String s) { + // res = Max of all (leftZeros + rightOnes) + // res = Max of all (leftZeros + (totalOnes - leftOnes)) + // res = totalOnes (constant) + Max of all (leftZeros - leftOnes) + + int zeros = 0, ones = 0, res = Integer.MIN_VALUE; + + if (s.charAt(0) == '0') { + zeros++; + } else { + ones++; + } + + for (int i = 1; i < s.length(); i++) { + res = Math.max(res, zeros - ones); + if (s.charAt(i) == '0') { + zeros++; + } else { + ones++; + } + } + + return res + ones; + } +} +``` + +```cpp +class Solution { +public: + int maxScore(string s) { + // res = Max of all (leftZeros + rightOnes) + // res = Max of all (leftZeros + (totalOnes - leftOnes)) + // res = totalOnes (constant) + Max of all (leftZeros - leftOnes) + + int zeros = 0, ones = 0, res = INT_MIN; + + if (s[0] == '0') { + zeros++; + } else { + ones++; + } + + for (int i = 1; i < s.size(); i++) { + res = max(res, zeros - ones); + if (s[i] == '0') { + zeros++; + } else { + ones++; + } + } + + return res + ones; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {number} + */ + maxScore(s) { + // res = Max of all (leftZeros + rightOnes) + // res = Max of all (leftZeros + (totalOnes - leftOnes)) + // res = totalOnes (constant) + Max of all (leftZeros - leftOnes) + + let zeros = 0, ones = 0, res = -Infinity; + + if (s[0] === '0') { + zeros++; + } else { + ones++; + } + + for (let i = 1; i < s.length; i++) { + res = Math.max(res, zeros - ones); + if (s[i] === '0') { + zeros++; + } else { + ones++; + } + } + + return res + ones; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/minimum-changes-to-make-alternating-binary-string.md b/articles/minimum-changes-to-make-alternating-binary-string.md new file mode 100644 index 000000000..05b5629b5 --- /dev/null +++ b/articles/minimum-changes-to-make-alternating-binary-string.md @@ -0,0 +1,207 @@ +## 1. Start with Zero and One + +::tabs-start + +```python +class Solution: + def minOperations(self, s: str) -> int: + cur = cnt1 = 0 + for c in s: + if int(c) != cur: + cnt1 += 1 + cur ^= 1 + + cur = 1 + cnt2 = 0 + for c in s: + if int(c) != cur: + cnt2 += 1 + cur ^= 1 + + return min(cnt1, cnt2) +``` + +```java +public class Solution { + public int minOperations(String s) { + int cur = 0, cnt1 = 0; + for (char c : s.toCharArray()) { + if (c - '0' != cur) { + cnt1++; + } + cur ^= 1; + } + + cur = 1; + int cnt2 = 0; + for (char c : s.toCharArray()) { + if (c - '0' != cur) { + cnt2++; + } + cur ^= 1; + } + + return Math.min(cnt1, cnt2); + } +} +``` + +```cpp +class Solution { +public: + int minOperations(string s) { + int cur = 0, cnt1 = 0; + for (char c : s) { + if (c - '0' != cur) { + cnt1++; + } + cur ^= 1; + } + + cur = 1; + int cnt2 = 0; + for (char c : s) { + if (c - '0' != cur) { + cnt2++; + } + cur ^= 1; + } + + return min(cnt1, cnt2); + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {number} + */ + minOperations(s) { + let cur = 0, cnt1 = 0; + for (let c of s) { + if (parseInt(c) !== cur) { + cnt1++; + } + cur ^= 1; + } + + cur = 1; + let cnt2 = 0; + for (let c of s) { + if (parseInt(c) !== cur) { + cnt2++; + } + cur ^= 1; + } + + return Math.min(cnt1, cnt2); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ + +--- + +## 2. Start with Zero or One + +::tabs-start + +```python +class Solution: + def minOperations(self, s: str) -> int: + count = 0 + + for i in range(len(s)): + if i % 2 == 0: + count += 1 if s[i] == '0' else 0 + else: + count += 1 if s[i] == '1' else 0 + + return min(count, len(s) - count) +``` + +```java +public class Solution { + public int minOperations(String s) { + int count = 0; + + for (int i = 0; i < s.length(); i++) { + if (i % 2 == 0) { + if (s.charAt(i) == '0') { + count++; + } + } else { + if (s.charAt(i) == '1') { + count++; + } + } + } + + return Math.min(count, s.length() - count); + } +} +``` + +```cpp +class Solution { +public: + int minOperations(string s) { + int count = 0; + + for (int i = 0; i < s.size(); i++) { + if (i % 2 == 0) { + if (s[i] == '0') { + count++; + } + } else { + if (s[i] == '1') { + count++; + } + } + } + + return min(count, (int)s.size() - count); + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {number} + */ + minOperations(s) { + let count = 0; + + for (let i = 0; i < s.length; i++) { + if (i % 2 === 0) { + if (s[i] === '0') { + count++; + } + } else { + if (s[i] === '1') { + count++; + } + } + } + + return Math.min(count, s.length - count); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/minimum-number-of-swaps-to-make-the-string-balanced.md b/articles/minimum-number-of-swaps-to-make-the-string-balanced.md new file mode 100644 index 000000000..8c18a3ae2 --- /dev/null +++ b/articles/minimum-number-of-swaps-to-make-the-string-balanced.md @@ -0,0 +1,219 @@ +## 1. Stack + +::tabs-start + +```python +class Solution: + def minSwaps(self, s: str) -> int: + stack = [] + for c in s: + if c == '[': + stack.append(c) + elif stack: + stack.pop() + return (len(stack) + 1) // 2 +``` + +```java +public class Solution { + public int minSwaps(String s) { + Stack stack = new Stack<>(); + for (char c : s.toCharArray()) { + if (c == '[') { + stack.push(c); + } else if (!stack.isEmpty()) { + stack.pop(); + } + } + return (stack.size() + 1) / 2; + } +} +``` + +```cpp +class Solution { +public: + int minSwaps(string s) { + vector stack; + for (char c : s) { + if (c == '[') { + stack.push_back(c); + } else if (!stack.empty()) { + stack.pop_back(); + } + } + return (stack.size() + 1) / 2; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {number} + */ + minSwaps(s) { + let stack = []; + for (const c of s) { + if (c === '[') { + stack.push(c); + } else if (stack.length > 0) { + stack.pop(); + } + } + return Math.floor((stack.length + 1) / 2); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Greedy - I + +::tabs-start + +```python +class Solution: + def minSwaps(self, s: str) -> int: + close = maxClose = 0 + + for c in s: + if c == '[': + close -= 1 + else: + close += 1 + maxClose = max(maxClose, close) + + return (maxClose + 1) // 2 +``` + +```java +public class Solution { + public int minSwaps(String s) { + int close = 0, maxClose = 0; + for (int i = 0; i < s.length(); i++) { + if (s.charAt(i) == '[') close--; + else close++; + maxClose = Math.max(maxClose, close); + } + return (maxClose + 1) / 2; + } +} +``` + +```cpp +class Solution { +public: + int minSwaps(string s) { + int close = 0, maxClose = 0; + for (auto& c : s) { + if (c == '[') close--; + else close++; + maxClose = max(maxClose, close); + } + return (maxClose + 1) / 2; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {number} + */ + minSwaps(s) { + let close = 0, maxClose = 0; + for (let i = 0; i < s.length; i++) { + if (s.charAt(i) == '[') close--; + else close++; + maxClose = Math.max(maxClose, close); + } + return Math.floor((maxClose + 1) / 2); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ + +--- + +## 3. Greedy - II + +::tabs-start + +```python +class Solution: + def minSwaps(self, s: str) -> int: + stackSize = 0 + for c in s: + if c == '[': + stackSize += 1 + elif stackSize > 0: + stackSize -= 1 + return (stackSize + 1) // 2 +``` + +```java +public class Solution { + public int minSwaps(String s) { + int stackSize = 0; + for (int i = 0; i < s.length(); i++) { + if (s.charAt(i) == '[') stackSize++; + else if (stackSize > 0) stackSize--; + } + return (stackSize + 1) / 2; + } +} +``` + +```cpp +class Solution { +public: + int minSwaps(string s) { + int stackSize = 0; + for (auto& c : s) { + if (c == '[') stackSize++; + else if (stackSize > 0) stackSize--; + } + return (stackSize + 1) / 2; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {number} + */ + minSwaps(s) { + let stackSize = 0; + for (let i = 0; i < s.length; i++) { + if (s.charAt(i) == '[') stackSize++; + else if (stackSize > 0) stackSize--; + } + return Math.floor((stackSize + 1) / 2); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/monotonic-array.md b/articles/monotonic-array.md new file mode 100644 index 000000000..317997838 --- /dev/null +++ b/articles/monotonic-array.md @@ -0,0 +1,305 @@ +## 1. Two Pass + +::tabs-start + +```python +class Solution: + def isMonotonic(self, nums: List[int]) -> bool: + n = len(nums) + increase = True + for i in range(1, n): + if nums[i] < nums[i - 1]: + increase = False + break + + if increase: + return True + + decrease = True + for i in range(1, n): + if nums[i] > nums[i - 1]: + decrease = False + break + return decrease +``` + +```java +public class Solution { + public boolean isMonotonic(int[] nums) { + int n = nums.length; + boolean increase = true; + for (int i = 1; i < n; i++) { + if (nums[i] < nums[i - 1]) { + increase = false; + break; + } + } + if (increase) { + return true; + } + + boolean decrease = true; + for (int i = 1; i < n; i++) { + if (nums[i] > nums[i - 1]) { + decrease = false; + break; + } + } + return decrease; + } +} +``` + +```cpp +class Solution { +public: + bool isMonotonic(vector& nums) { + int n = nums.size(); + bool increase = true; + for (int i = 1; i < n; ++i) { + if (nums[i] < nums[i - 1]) { + increase = false; + break; + } + } + if (increase) { + return true; + } + + bool decrease = true; + for (int i = 1; i < n; ++i) { + if (nums[i] > nums[i - 1]) { + decrease = false; + break; + } + } + return decrease; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {boolean} + */ + isMonotonic(nums) { + const n = nums.length; + let increase = true; + for (let i = 1; i < n; i++) { + if (nums[i] < nums[i - 1]) { + increase = false; + break; + } + } + if (increase) { + return true; + } + + let decrease = true; + for (let i = 1; i < n; i++) { + if (nums[i] > nums[i - 1]) { + decrease = false; + break; + } + } + return decrease; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ + +--- + +## 2. One Pass - I + +::tabs-start + +```python +class Solution: + def isMonotonic(self, nums: List[int]) -> bool: + n = len(nums) + if nums[0] <= nums[-1]: + for i in range(1, n): + if nums[i] < nums[i - 1]: + return False + return True + else: + for i in range(1, n): + if nums[i] > nums[i - 1]: + return False + return True +``` + +```java +public class Solution { + public boolean isMonotonic(int[] nums) { + int n = nums.length; + if (nums[0] <= nums[n - 1]) { + for (int i = 1; i < n; i++) { + if (nums[i] < nums[i - 1]) { + return false; + } + } + return true; + } else { + for (int i = 1; i < n; i++) { + if (nums[i] > nums[i - 1]) { + return false; + } + } + return true; + } + } +} +``` + +```cpp +class Solution { +public: + bool isMonotonic(vector& nums) { + int n = nums.size(); + if (nums[0] <= nums[n - 1]) { + for (int i = 1; i < n; ++i) { + if (nums[i] < nums[i - 1]) { + return false; + } + } + return true; + } else { + for (int i = 1; i < n; ++i) { + if (nums[i] > nums[i - 1]) { + return false; + } + } + return true; + } + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {boolean} + */ + isMonotonic(nums) { + const n = nums.length; + if (nums[0] <= nums[n - 1]) { + for (let i = 1; i < n; i++) { + if (nums[i] < nums[i - 1]) { + return false; + } + } + return true; + } else { + for (let i = 1; i < n; i++) { + if (nums[i] > nums[i - 1]) { + return false; + } + } + return true; + } + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ + +--- + +## 3. One Pass - II + +::tabs-start + +```python +class Solution: + def isMonotonic(self, nums: List[int]) -> bool: + increase, decrease = True, True + + for i in range(len(nums) - 1): + if not (nums[i] <= nums[i + 1]): + increase = False + if not (nums[i] >= nums[i + 1]): + decrease = False + return increase or decrease +``` + +```java +public class Solution { + public boolean isMonotonic(int[] nums) { + boolean increase = true, decrease = true; + + for (int i = 0; i < nums.length - 1; i++) { + if (!(nums[i] <= nums[i + 1])) { + increase = false; + } + if (!(nums[i] >= nums[i + 1])) { + decrease = false; + } + } + return increase || decrease; + } +} +``` + +```cpp +class Solution { +public: + bool isMonotonic(vector& nums) { + bool increase = true, decrease = true; + + for (int i = 0; i < nums.size() - 1; ++i) { + if (!(nums[i] <= nums[i + 1])) { + increase = false; + } + if (!(nums[i] >= nums[i + 1])) { + decrease = false; + } + } + return increase || decrease; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {boolean} + */ + isMonotonic(nums) { + let increase = true, decrease = true; + + for (let i = 0; i < nums.length - 1; i++) { + if (!(nums[i] <= nums[i + 1])) { + increase = false; + } + if (!(nums[i] >= nums[i + 1])) { + decrease = false; + } + } + return increase || decrease; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/number-of-good-pairs.md b/articles/number-of-good-pairs.md new file mode 100644 index 000000000..ea3e819e8 --- /dev/null +++ b/articles/number-of-good-pairs.md @@ -0,0 +1,221 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def numIdenticalPairs(self, nums: List[int]) -> int: + res = 0 + for i in range(len(nums)): + for j in range(i + 1, len(nums)): + if nums[i] == nums[j]: + res += 1 + return res +``` + +```java +public class Solution { + public int numIdenticalPairs(int[] nums) { + int res = 0; + for (int i = 0; i < nums.length; i++) { + for (int j = i + 1; j < nums.length; j++) { + if (nums[i] == nums[j]) { + res++; + } + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int numIdenticalPairs(vector& nums) { + int res = 0; + for (int i = 0; i < nums.size(); i++) { + for (int j = i + 1; j < nums.size(); j++) { + if (nums[i] == nums[j]) { + res++; + } + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + numIdenticalPairs(nums) { + let res = 0; + for (let i = 0; i < nums.length; i++) { + for (let j = i + 1; j < nums.length; j++) { + if (nums[i] == nums[j]) { + res++; + } + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(1)$ + +--- + +## 2. Hash Map (Math) + +::tabs-start + +```python +class Solution: + def numIdenticalPairs(self, nums: List[int]) -> int: + count = Counter(nums) + res = 0 + for num, c in count.items(): + res += c * (c - 1) // 2 + return res +``` + +```java +public class Solution { + public int numIdenticalPairs(int[] nums) { + Map count = new HashMap<>(); + int res = 0; + for (int num : nums) { + count.put(num, count.getOrDefault(num, 0) + 1); + } + for (int c : count.values()) { + res += c * (c - 1) / 2; + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int numIdenticalPairs(vector& nums) { + unordered_map count; + int res = 0; + for (int num : nums) { + count[num]++; + } + for (auto& [num, c] : count) { + res += c * (c - 1) / 2; + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + numIdenticalPairs(nums) { + const count = {}; + let res = 0; + for (const num of nums) { + count[num] = (count[num] || 0) + 1; + } + for (const c of Object.values(count)) { + res += c * (c - 1) / 2; + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Hash Map + +::tabs-start + +```python +class Solution: + def numIdenticalPairs(self, nums: List[int]) -> int: + count = defaultdict(int) + res = 0 + for num in nums: + res += count[num] + count[num] += 1 + return res +``` + +```java +public class Solution { + public int numIdenticalPairs(int[] nums) { + Map count = new HashMap<>(); + int res = 0; + for (int num : nums) { + res += count.getOrDefault(num, 0); + count.put(num, count.getOrDefault(num, 0) + 1); + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int numIdenticalPairs(vector& nums) { + unordered_map count; + int res = 0; + for (int num : nums) { + res += count[num]; + count[num]++; + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + numIdenticalPairs(nums) { + const count = {}; + let res = 0; + for (const num of nums) { + res += count[num] || 0; + count[num] = (count[num] || 0) + 1; + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/number-of-pairs-of-interchangeable-rectangles.md b/articles/number-of-pairs-of-interchangeable-rectangles.md new file mode 100644 index 000000000..bbb73e152 --- /dev/null +++ b/articles/number-of-pairs-of-interchangeable-rectangles.md @@ -0,0 +1,364 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def interchangeableRectangles(self, rectangles: List[List[int]]) -> int: + res = 0 + for i in range(1, len(rectangles)): + for j in range(i): + if rectangles[i][0] / rectangles[i][1] == rectangles[j][0] / rectangles[j][1]: + res += 1 + return res +``` + +```java +public class Solution { + public long interchangeableRectangles(int[][] rectangles) { + long res = 0; + for (int i = 1; i < rectangles.length; i++) { + for (int j = 0; j < i; j++) { + if ((double) rectangles[i][0] / rectangles[i][1] == (double) rectangles[j][0] / rectangles[j][1]) { + res++; + } + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + long long interchangeableRectangles(vector>& rectangles) { + long long res = 0; + for (int i = 1; i < rectangles.size(); i++) { + for (int j = 0; j < i; j++) { + if ((double) rectangles[i][0] / rectangles[i][1] == (double) rectangles[j][0] / rectangles[j][1]) { + res++; + } + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} rectangles + * @return {number} + */ + interchangeableRectangles(rectangles) { + let res = 0; + for (let i = 1; i < rectangles.length; i++) { + for (let j = 0; j < i; j++) { + if (rectangles[i][0] / rectangles[i][1] === rectangles[j][0] / rectangles[j][1]) { + res++; + } + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(1)$ + +--- + +## 2. Hash Map (Two Pass) + +::tabs-start + +```python +class Solution: + def interchangeableRectangles(self, rectangles: List[List[int]]) -> int: + count = {} + for w, h in rectangles: + count[w / h] = 1 + count.get(w / h, 0) + + res = 0 + for c in count.values(): + if c > 1: + res += (c * (c - 1)) // 2 + return res +``` + +```java +public class Solution { + public long interchangeableRectangles(int[][] rectangles) { + HashMap count = new HashMap<>(); + for (int[] rect : rectangles) { + double ratio = (double) rect[0] / rect[1]; + count.put(ratio, count.getOrDefault(ratio, 0) + 1); + } + + long res = 0; + for (int c : count.values()) { + if (c > 1) { + res += (c * 1L * (c - 1)) / 2; + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + long long interchangeableRectangles(vector>& rectangles) { + unordered_map count; + for (const auto& rect : rectangles) { + double ratio = (double) rect[0] / rect[1]; + count[ratio]++; + } + + long long res = 0; + for (const auto& [key, c] : count) { + if (c > 1) { + res += (c * 1LL * (c - 1)) / 2; + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} rectangles + * @return {number} + */ + interchangeableRectangles(rectangles) { + const count = new Map(); + for (const [w, h] of rectangles) { + const ratio = w / h; + count.set(ratio, (count.get(ratio) || 0) + 1); + } + + let res = 0; + for (const c of count.values()) { + if (c > 1) { + res += (c * (c - 1)) / 2; + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Hash Map (One Pass) + +::tabs-start + +```python +class Solution: + def interchangeableRectangles(self, rectangles: List[List[int]]) -> int: + count = {} + res = 0 + for w, h in rectangles: + res += count.get(w / h, 0) + count[w / h] = 1 + count.get(w / h, 0) + return res +``` + +```java +public class Solution { + public long interchangeableRectangles(int[][] rectangles) { + HashMap count = new HashMap<>(); + long res = 0; + for (int[] rect : rectangles) { + double ratio = (double) rect[0] / rect[1]; + res += count.getOrDefault(ratio, 0); + count.put(ratio, count.getOrDefault(ratio, 0) + 1); + } + return res; + } +} +``` + +```cpp +class Solution { +public: + long long interchangeableRectangles(vector>& rectangles) { + unordered_map count; + long long res = 0; + for (const auto& rect : rectangles) { + double ratio = (double) rect[0] / rect[1]; + res += count[ratio]; + count[ratio]++; + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} rectangles + * @return {number} + */ + interchangeableRectangles(rectangles) { + const count = new Map(); + let res = 0; + for (const [w, h] of rectangles) { + const ratio = w / h; + res += count.get(ratio) || 0 + count.set(ratio, (count.get(ratio) || 0) + 1); + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 4. Greatest Common Divisor + +::tabs-start + +```python +class Solution: + def hash(self, a: int, b: int) -> int: + mask = a + mask |= (b << 31) + return mask + + def interchangeableRectangles(self, rectangles: List[List[int]]) -> int: + res = 0 + count = {} + for rect in rectangles: + gcd = math.gcd(rect[0], rect[1]) + key = self.hash(rect[0] // gcd, rect[1] // gcd) + res += count.get(key, 0) + count[key] = count.get(key, 0) + 1 + return res +``` + +```java +public class Solution { + public long hash(int a, int b) { + long mask = a; + mask |= ((long)b << 31); + return mask; + } + + public long interchangeableRectangles(int[][] rectangles) { + long res = 0; + Map count = new HashMap<>(); + for (int[] rect : rectangles) { + int gcd = gcd(rect[0], rect[1]); + long key = hash(rect[0] / gcd, rect[1] / gcd); + res += count.getOrDefault(key, 0); + count.put(key, count.getOrDefault(key, 0) + 1); + } + return res; + } + + private int gcd(int a, int b) { + while (b != 0) { + a %= b; + int temp = a; + a = b; + b = temp; + } + return a; + } +} +``` + +```cpp +class Solution { +public: + long long hash(int a, int b) { + long long mask = a; + mask |= ((long long)b << 31); + return mask; + } + + long long interchangeableRectangles(vector>& rectangles) { + long long res = 0; + unordered_map count; + for (const auto& rect : rectangles) { + int gcd = __gcd(rect[0], rect[1]); + long long key = hash(rect[0] / gcd, rect[1] / gcd); + res += count[key]; + count[key]++; + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} a + * @param {number} b + * @return {string} + */ + hash(a, b) { + return `${a},${b}`; + } + + /** + * @param {number[][]} rectangles + * @return {number} + */ + interchangeableRectangles(rectangles) { + let res = 0; + const count = new Map(); + + const gcd = (a, b) => { + while (b !== 0) { + a %= b; + [a, b] = [b, a] + } + return a; + }; + + for (const rect of rectangles) { + const g = gcd(rect[0], rect[1]); + const key = this.hash(Math.floor(rect[0] / g), Math.floor(rect[1] / g)); + res += count.get(key) || 0; + count.set(key, (count.get(key) || 0) + 1); + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/number-of-students-unable-to-eat-lunch.md b/articles/number-of-students-unable-to-eat-lunch.md new file mode 100644 index 000000000..6ccf1609d --- /dev/null +++ b/articles/number-of-students-unable-to-eat-lunch.md @@ -0,0 +1,348 @@ +## 1. Queue + +::tabs-start + +```python +class Solution: + def countStudents(self, students: List[int], sandwiches: List[int]) -> int: + n = len(students) + q = deque(students) + + res = n + for sandwich in sandwiches: + cnt = 0 + while cnt < n and q[0] != sandwich: + cur = q.popleft() + q.append(cur) + cnt += 1 + + if q[0] == sandwich: + q.popleft() + res -= 1 + else: + break + return res +``` + +```java +public class Solution { + public int countStudents(int[] students, int[] sandwiches) { + int n = students.length; + Queue q = new LinkedList<>(); + for (int student : students) { + q.offer(student); + } + + int res = n; + for (int sandwich : sandwiches) { + int cnt = 0; + while (cnt < n && q.peek() != sandwich) { + q.offer(q.poll()); + cnt++; + } + if (q.peek() == sandwich) { + q.poll(); + res--; + } else { + break; + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int countStudents(vector& students, vector& sandwiches) { + int n = students.size(); + queue q; + + for (int student : students) { + q.push(student); + } + + int res = n; + for (int sandwich : sandwiches) { + int cnt = 0; + while (cnt < n && q.front() != sandwich) { + q.push(q.front()); + q.pop(); + cnt++; + } + if (q.front() == sandwich) { + q.pop(); + res--; + } else { + break; + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} students + * @param {number[]} sandwiches + * @return {number} + */ + countStudents(students, sandwiches) { + let n = students.length; + let q = new Queue(); + for (let student of students) { + q.push(student); + } + + let res = n; + for (let sandwich of sandwiches) { + let cnt = 0; + while (cnt < n && q.front() !== sandwich) { + q.push(q.pop()); + cnt++; + } + + if (q.front() === sandwich) { + q.pop(); + res--; + } else { + break; + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n)$ + +--- + +## 2. Iteration + +::tabs-start + +```python +class Solution: + def countStudents(self, students: List[int], sandwiches: List[int]) -> int: + n = len(students) + idx = 0 + + res = n + for sandwich in sandwiches: + cnt = 0 + while cnt < n and students[idx] != sandwich: + idx += 1 + idx %= n + cnt += 1 + + if students[idx] == sandwich: + students[idx] = -1 + res -= 1 + else: + break + return res +``` + +```java +public class Solution { + public int countStudents(int[] students, int[] sandwiches) { + int n = students.length; + int idx = 0; + + int res = n; + for (int sandwich : sandwiches) { + int cnt = 0; + while (cnt < n && students[idx] != sandwich) { + idx++; + idx %= n; + cnt++; + } + if (students[idx] == sandwich) { + students[idx] = -1; + res--; + } else { + break; + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int countStudents(vector& students, vector& sandwiches) { + int n = students.size(); + int idx = 0; + + int res = n; + for (int sandwich : sandwiches) { + int cnt = 0; + while (cnt < n && students[idx] != sandwich) { + idx++; + idx %= n; + cnt++; + } + if (students[idx] == sandwich) { + students[idx] = -1; + res--; + } else { + break; + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} students + * @param {number[]} sandwiches + * @return {number} + */ + countStudents(students, sandwiches) { + let n = students.length; + let idx = 0; + + let res = n; + for (let sandwich of sandwiches) { + let cnt = 0; + while (cnt < n && students[idx] !== sandwich) { + idx++; + idx %= n; + cnt++; + } + + if (students[idx] === sandwich) { + students[idx] = -1; + res--; + } else { + break; + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(1)$ + +--- + +## 3. Frequency Count + +::tabs-start + +```python +class Solution: + def countStudents(self, students: List[int], sandwiches: List[int]) -> int: + res = len(students) + cnt = Counter(students) + + for s in sandwiches: + if cnt[s] > 0: + res -= 1 + cnt[s] -= 1 + else: + break + + return res +``` + +```java +public class Solution { + public int countStudents(int[] students, int[] sandwiches) { + int n = students.length; + int res = n; + int[] cnt = new int[2]; + for (int i = 0; i < n; i++) { + cnt[students[i]]++; + } + + for (int i = 0; i < n; i++) { + if (cnt[sandwiches[i]] > 0) { + res--; + cnt[sandwiches[i]]--; + } else { + break; + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int countStudents(vector& students, vector& sandwiches) { + int res = students.size(); + vector cnt(2); + for (int& student : students) { + cnt[student]++; + } + + for (int& s : sandwiches) { + if (cnt[s] > 0) { + cnt[s]--; + res--; + } else { + break; + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} students + * @param {number[]} sandwiches + * @return {number} + */ + countStudents(students, sandwiches) { + let res = students.length; + const cnt = new Int32Array(2); + for (let student of students) { + cnt[student]++; + } + + for (let s of sandwiches) { + if (cnt[s] > 0) { + cnt[s]--; + res--; + } else { + break; + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/pascals-triangle-ii.md b/articles/pascals-triangle-ii.md new file mode 100644 index 000000000..5a675a025 --- /dev/null +++ b/articles/pascals-triangle-ii.md @@ -0,0 +1,375 @@ +## 1. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def getRow(self, rowIndex: int) -> List[int]: + if rowIndex == 0: + return [1] + + curRow = [1] + prevRow = self.getRow(rowIndex - 1) + + for i in range(1, rowIndex): + curRow.append(prevRow[i - 1] + prevRow[i]) + + curRow.append(1) + return curRow +``` + +```java +public class Solution { + public List getRow(int rowIndex) { + if (rowIndex == 0) return Arrays.asList(1); + + List curRow = new ArrayList<>(Arrays.asList(1)); + List prevRow = getRow(rowIndex - 1); + + for (int i = 1; i < rowIndex; i++) { + curRow.add(prevRow.get(i - 1) + prevRow.get(i)); + } + + curRow.add(1); + return curRow; + } +} +``` + +```cpp +class Solution { +public: + vector getRow(int rowIndex) { + if (rowIndex == 0) return {1}; + + vector curRow = {1}; + vector prevRow = getRow(rowIndex - 1); + + for (int i = 1; i < rowIndex; i++) { + curRow.push_back(prevRow[i - 1] + prevRow[i]); + } + + curRow.push_back(1); + return curRow; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} rowIndex + * @return {number[]} + */ + getRow(rowIndex) { + if (rowIndex === 0) return [1]; + + let curRow = [1]; + let prevRow = this.getRow(rowIndex - 1); + + for (let i = 1; i < rowIndex; i++) { + curRow.push(prevRow[i - 1] + prevRow[i]); + } + + curRow.push(1); + return curRow; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n)$ + +--- + +## 2. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def getRow(self, rowIndex: int) -> List[int]: + res = [[1] * (i + 1) for i in range(rowIndex + 1)] + for i in range(2, rowIndex + 1): + for j in range(1, i): + res[i][j] = res[i - 1][j - 1] + res[i - 1][j] + return res[rowIndex] +``` + +```java +public class Solution { + public List getRow(int rowIndex) { + List> res = new ArrayList<>(); + for (int i = 0; i <= rowIndex; i++) { + List row = new ArrayList<>(Collections.nCopies(i + 1, 1)); + for (int j = 1; j < i; j++) { + row.set(j, res.get(i - 1).get(j - 1) + res.get(i - 1).get(j)); + } + res.add(row); + } + return res.get(rowIndex); + } +} +``` + +```cpp +class Solution { +public: + vector getRow(int rowIndex) { + vector> res(rowIndex + 1); + for (int i = 0; i <= rowIndex; i++) { + res[i] = vector(i + 1, 1); + for (int j = 1; j < i; j++) { + res[i][j] = res[i - 1][j - 1] + res[i - 1][j]; + } + } + return res[rowIndex]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} rowIndex + * @return {number[]} + */ + getRow(rowIndex) { + const res = Array.from({ length: rowIndex + 1 }, (_, i) => Array(i + 1).fill(1)); + for (let i = 2; i <= rowIndex; i++) { + for (let j = 1; j < i; j++) { + res[i][j] = res[i - 1][j - 1] + res[i - 1][j]; + } + } + return res[rowIndex]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n ^ 2)$ + +--- + +## 3. Dynamic Programming (Space Optimized - I) + +::tabs-start + +```python +class Solution: + def getRow(self, rowIndex: int) -> List[int]: + res = [1] + for i in range(rowIndex): + next_row = [0] * (len(res) + 1) + for j in range(len(res)): + next_row[j] += res[j] + next_row[j + 1] += res[j] + res = next_row + return res +``` + +```java +public class Solution { + public List getRow(int rowIndex) { + List res = new ArrayList<>(); + res.add(1); + for (int i = 0; i < rowIndex; i++) { + List nextRow = new ArrayList<>(Collections.nCopies(res.size() + 1, 0)); + for (int j = 0; j < res.size(); j++) { + nextRow.set(j, nextRow.get(j) + res.get(j)); + nextRow.set(j + 1, nextRow.get(j + 1) + res.get(j)); + } + res = nextRow; + } + return res; + } +} +``` + +```cpp +class Solution { +public: + vector getRow(int rowIndex) { + vector res = {1}; + for (int i = 0; i < rowIndex; i++) { + vector nextRow(res.size() + 1, 0); + for (int j = 0; j < res.size(); j++) { + nextRow[j] += res[j]; + nextRow[j + 1] += res[j]; + } + res = nextRow; + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} rowIndex + * @return {number[]} + */ + getRow(rowIndex) { + let res = [1]; + for (let i = 0; i < rowIndex; i++) { + const nextRow = Array(res.length + 1).fill(0); + for (let j = 0; j < res.length; j++) { + nextRow[j] += res[j]; + nextRow[j + 1] += res[j]; + } + res = nextRow; + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n)$ + +--- + +## 4. Dynamic Programming (Space Optimized - II) + +::tabs-start + +```python +class Solution: + def getRow(self, rowIndex: int) -> List[int]: + row = [1] * (rowIndex + 1) + for i in range(1, rowIndex): + for j in range(i, 0, -1): + row[j] += row[j - 1] + return row +``` + +```java +public class Solution { + public List getRow(int rowIndex) { + List row = new ArrayList<>(Collections.nCopies(rowIndex + 1, 1)); + for (int i = 1; i < rowIndex; i++) { + for (int j = i; j > 0; j--) { + row.set(j, row.get(j) + row.get(j - 1)); + } + } + return row; + } +} +``` + +```cpp +class Solution { +public: + vector getRow(int rowIndex) { + vector row(rowIndex + 1, 1); + for (int i = 1; i < rowIndex; i++) { + for (int j = i; j > 0; j--) { + row[j] += row[j - 1]; + } + } + return row; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} rowIndex + * @return {number[]} + */ + getRow(rowIndex) { + const row = Array(rowIndex + 1).fill(1); + for (let i = 1; i < rowIndex; i++) { + for (let j = i; j > 0; j--) { + row[j] += row[j - 1]; + } + } + return row; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n)$ + +--- + +## 5. Combinatorics + +::tabs-start + +```python +class Solution: + def getRow(self, rowIndex: int) -> List[int]: + row = [1] + for i in range(1, rowIndex + 1): + row.append(row[-1] * (rowIndex - i + 1) // i) + return row +``` + +```java +public class Solution { + public List getRow(int rowIndex) { + List row = new ArrayList<>(); + row.add(1); + for (int i = 1; i <= rowIndex; i++) { + row.add((int)((long)row.get(row.size() - 1) * (rowIndex - i + 1) / i)); + } + return row; + } +} +``` + +```cpp +class Solution { +public: + vector getRow(int rowIndex) { + vector row = {1}; + for (int i = 1; i <= rowIndex; i++) { + row.push_back(int(row.back() * 1LL * (rowIndex - i + 1) / i)); + } + return row; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} rowIndex + * @return {number[]} + */ + getRow(rowIndex) { + const row = [1]; + for (let i = 1; i <= rowIndex; i++) { + row.push(Math.floor(row[row.length - 1] * (rowIndex - i + 1) / i)); + } + return row; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/pascals-triangle.md b/articles/pascals-triangle.md index a3434d283..2254d3bbf 100644 --- a/articles/pascals-triangle.md +++ b/articles/pascals-triangle.md @@ -82,7 +82,7 @@ class Solution { ### Time & Space Complexity * Time complexity: $O(n ^ 2)$ -* Space complexity: $O(1)$ +* Space complexity: $O(n ^ 2)$ --- @@ -176,7 +176,7 @@ class Solution { ### Time & Space Complexity * Time complexity: $O(n ^ 2)$ -* Space complexity: $O(n)$ +* Space complexity: $O(n ^ 2)$ --- @@ -257,4 +257,4 @@ class Solution { ### Time & Space Complexity * Time complexity: $O(n ^ 2)$ -* Space complexity: $O(1)$ \ No newline at end of file +* Space complexity: $O(n ^ 2)$ \ No newline at end of file diff --git a/articles/path-crossing.md b/articles/path-crossing.md new file mode 100644 index 000000000..dd4abbaf2 --- /dev/null +++ b/articles/path-crossing.md @@ -0,0 +1,242 @@ +## 1. Hash Set + +::tabs-start + +```python +class Solution: + def isPathCrossing(self, path: str) -> bool: + dir = { + 'N': [0, 1], + 'S': [0, -1], + 'E': [1, 0], + 'W': [-1, 0] + } + + visit = set() + x, y = 0, 0 + + for c in path: + visit.add((x, y)) + dx, dy = dir[c] + x, y = x + dx, y + dy + if (x, y) in visit: + return True + + return False +``` + +```java +public class Solution { + public boolean isPathCrossing(String path) { + Set visit = new HashSet<>(); + int x = 0, y = 0; + visit.add(x + "," + y); + + for (char c : path.toCharArray()) { + if (c == 'N') y++; + else if (c == 'S') y--; + else if (c == 'E') x++; + else if (c == 'W') x--; + + String pos = x + "," + y; + if (visit.contains(pos)) return true; + visit.add(pos); + } + + return false; + } +} +``` + +```cpp +class Solution { +public: + bool isPathCrossing(string path) { + unordered_set visit; + int x = 0, y = 0; + visit.insert(to_string(x) + "," + to_string(y)); + + for (char c : path) { + if (c == 'N') y++; + else if (c == 'S') y--; + else if (c == 'E') x++; + else if (c == 'W') x--; + + string pos = to_string(x) + "," + to_string(y); + if (visit.count(pos)) return true; + visit.insert(pos); + } + + return false; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} path + * @return {boolean} + */ + isPathCrossing(path) { + const visit = new Set(); + let x = 0, y = 0; + visit.add(`${x},${y}`); + + for (const c of path) { + if (c === 'N') y++; + else if (c === 'S') y--; + else if (c === 'E') x++; + else if (c === 'W') x--; + + const pos = `${x},${y}`; + if (visit.has(pos)) return true; + visit.add(pos); + } + + return false; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Hash Set (Custom Hash) + +::tabs-start + +```python +class Solution: + def isPathCrossing(self, path: str) -> bool: + visit = set() + x, y = 0, 0 + visit.add(self.hash(x, y)) + + for c in path: + if c == 'N': + y += 1 + elif c == 'S': + y -= 1 + elif c == 'E': + x += 1 + elif c == 'W': + x -= 1 + + pos = self.hash(x, y) + if pos in visit: + return True + visit.add(pos) + + return False + + def hash(self, x: int, y: int) -> int: + return (x << 32) + y +``` + +```java +public class Solution { + public boolean isPathCrossing(String path) { + Set visit = new HashSet<>(); + int x = 0, y = 0; + visit.add(hash(x, y)); + + for (char c : path.toCharArray()) { + if (c == 'N') y++; + else if (c == 'S') y--; + else if (c == 'E') x++; + else if (c == 'W') x--; + + long pos = hash(x, y); + if (visit.contains(pos)) return true; + visit.add(pos); + } + + return false; + } + + private long hash(long x, long y) { + return (x << 32) + y; + } +} +``` + +```cpp +class Solution { +public: + bool isPathCrossing(string path) { + unordered_set, pair_hash> visit; + int x = 0, y = 0; + visit.insert({x, y}); + + for (char c : path) { + if (c == 'N') y++; + else if (c == 'S') y--; + else if (c == 'E') x++; + else if (c == 'W') x--; + + if (visit.count({x, y})) return true; + visit.insert({x, y}); + } + + return false; + } + +private: + struct pair_hash { + template + size_t operator()(const pair& p) const { + return (hash()(p.first) << 32) + hash()(p.second); + } + }; +}; +``` + +```javascript +class Solution { + /** + * @param {string} path + * @return {boolean} + */ + isPathCrossing(path) { + const visit = new Set(); + let x = 0, y = 0; + visit.add(this.hash(x, y)); + + for (const c of path) { + if (c === 'N') y++; + else if (c === 'S') y--; + else if (c === 'E') x++; + else if (c === 'W') x--; + + const pos = this.hash(x, y); + if (visit.has(pos)) return true; + visit.add(pos); + } + + return false; + } + + /** + * @param {number} x + * @param {number} x + * @return {number} + */ + hash(x, y) { + return (x << 16) + y; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/products-of-array-discluding-self.md b/articles/products-of-array-discluding-self.md index cafc651ec..1a74a5c68 100644 --- a/articles/products-of-array-discluding-self.md +++ b/articles/products-of-array-discluding-self.md @@ -147,8 +147,7 @@ class Solution { ### Time & Space Complexity * Time complexity: $O(n ^ 2)$ -* Space complexity: $O(1)$ - +* Space complexity: $O(1)$ since the output array is excluded from space analysis. --- ## 2. Division @@ -362,7 +361,7 @@ class Solution { ### Time & Space Complexity * Time complexity: $O(n)$ -* Space complexity: $O(1)$ +* Space complexity: $O(1)$ since the output array is excluded from space analysis. --- @@ -700,4 +699,4 @@ class Solution { ### Time & Space Complexity * Time complexity: $O(n)$ -* Space complexity: $O(1)$ \ No newline at end of file +* Space complexity: $O(1)$ since the output array is excluded from space analysis. \ No newline at end of file diff --git a/articles/push-dominoes.md b/articles/push-dominoes.md new file mode 100644 index 000000000..efe99877f --- /dev/null +++ b/articles/push-dominoes.md @@ -0,0 +1,749 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def pushDominoes(self, dominoes: str) -> str: + n = len(dominoes) + res = list(dominoes) + + for i in range(n): + if dominoes[i] != '.': + continue + + l, r = i - 1, i + 1 + + while l >= 0 and dominoes[l] == '.': + l -= 1 + + while r < n and dominoes[r] == '.': + r += 1 + + left_force = dominoes[l] if l >= 0 else None + right_force = dominoes[r] if r < n else None + + if left_force == 'R' and right_force == 'L': + if (i - l) < (r - i): + res[i] = 'R' + elif (r - i) < (i - l): + res[i] = 'L' + elif left_force == 'R': + res[i] = 'R' + elif right_force == 'L': + res[i] = 'L' + + return "".join(res) +``` + +```java +public class Solution { + public String pushDominoes(String dominoes) { + int n = dominoes.length(); + char[] res = dominoes.toCharArray(); + + for (int i = 0; i < n; i++) { + if (dominoes.charAt(i) != '.') continue; + + int l = i - 1, r = i + 1; + + while (l >= 0 && dominoes.charAt(l) == '.') l--; + while (r < n && dominoes.charAt(r) == '.') r++; + + char leftForce = (l >= 0) ? dominoes.charAt(l) : ' '; + char rightForce = (r < n) ? dominoes.charAt(r) : ' '; + + if (leftForce == 'R' && rightForce == 'L') { + if ((i - l) < (r - i)) res[i] = 'R'; + else if ((r - i) < (i - l)) res[i] = 'L'; + } else if (leftForce == 'R') { + res[i] = 'R'; + } else if (rightForce == 'L') { + res[i] = 'L'; + } + } + + return new String(res); + } +} +``` + +```cpp +class Solution { +public: + string pushDominoes(string dominoes) { + int n = dominoes.size(); + vector res(dominoes.begin(), dominoes.end()); + + for (int i = 0; i < n; i++) { + if (dominoes[i] != '.') continue; + + int l = i - 1, r = i + 1; + + while (l >= 0 && dominoes[l] == '.') l--; + while (r < n && dominoes[r] == '.') r++; + + char leftForce = (l >= 0) ? dominoes[l] : ' '; + char rightForce = (r < n) ? dominoes[r] : ' '; + + if (leftForce == 'R' && rightForce == 'L') { + if ((i - l) < (r - i)) res[i] = 'R'; + else if ((r - i) < (i - l)) res[i] = 'L'; + } else if (leftForce == 'R') { + res[i] = 'R'; + } else if (rightForce == 'L') { + res[i] = 'L'; + } + } + + return string(res.begin(), res.end()); + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} dominoes + * @return {string} + */ + pushDominoes(dominoes) { + const n = dominoes.length; + const res = dominoes.split(""); + + for (let i = 0; i < n; i++) { + if (dominoes[i] !== ".") continue; + + let l = i - 1, r = i + 1; + + while (l >= 0 && dominoes[l] === ".") l--; + while (r < n && dominoes[r] === ".") r++; + + const leftForce = l >= 0 ? dominoes[l] : null; + const rightForce = r < n ? dominoes[r] : null; + + if (leftForce === "R" && rightForce === "L") { + if ((i - l) < (r - i)) res[i] = "R"; + else if ((r - i) < (i - l)) res[i] = "L"; + } else if (leftForce === "R") { + res[i] = "R"; + } else if (rightForce === "L") { + res[i] = "L"; + } + } + + return res.join(""); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n)$ for only the output string. + +--- + +## 2. Force From Left & Right + +::tabs-start + +```python +class Solution: + def pushDominoes(self, dominoes: str) -> str: + n = len(dominoes) + left = [float('inf')] * n + right = [float('inf')] * n + res = list(dominoes) + + force = float('inf') + for i in range(n): + if dominoes[i] == 'R': + force = 0 + elif dominoes[i] == 'L': + force = float('inf') + else: + force += 1 + right[i] = force + + force = float('inf') + for i in range(n - 1, -1, -1): + if dominoes[i] == 'L': + force = 0 + elif dominoes[i] == 'R': + force = float('inf') + else: + force += 1 + left[i] = force + + for i in range(n): + if left[i] < right[i]: + res[i] = 'L' + elif right[i] < left[i]: + res[i] = 'R' + + return "".join(res) +``` + +```java +public class Solution { + public String pushDominoes(String dominoes) { + int n = dominoes.length(); + int[] left = new int[n]; + int[] right = new int[n]; + char[] res = dominoes.toCharArray(); + + int force = Integer.MAX_VALUE; + for (int i = 0; i < n; i++) { + if (dominoes.charAt(i) == 'R') { + force = 0; + } else if (dominoes.charAt(i) == 'L') { + force = Integer.MAX_VALUE; + } else { + force = force == Integer.MAX_VALUE ? Integer.MAX_VALUE : force + 1; + } + right[i] = force; + } + + force = Integer.MAX_VALUE; + for (int i = n - 1; i >= 0; i--) { + if (dominoes.charAt(i) == 'L') { + force = 0; + } else if (dominoes.charAt(i) == 'R') { + force = Integer.MAX_VALUE; + } else { + force = force == Integer.MAX_VALUE ? Integer.MAX_VALUE : force + 1; + } + left[i] = force; + } + + for (int i = 0; i < n; i++) { + if (left[i] < right[i]) { + res[i] = 'L'; + } else if (right[i] < left[i]) { + res[i] = 'R'; + } + } + + return new String(res); + } +} +``` + +```cpp +class Solution { +public: + string pushDominoes(string dominoes) { + int n = dominoes.size(); + vector left(n, INT_MAX); + vector right(n, INT_MAX); + vector res(dominoes.begin(), dominoes.end()); + + int force = INT_MAX; + for (int i = 0; i < n; i++) { + if (dominoes[i] == 'R') { + force = 0; + } else if (dominoes[i] == 'L') { + force = INT_MAX; + } else { + force = (force == INT_MAX) ? INT_MAX : force + 1; + } + right[i] = force; + } + + force = INT_MAX; + for (int i = n - 1; i >= 0; i--) { + if (dominoes[i] == 'L') { + force = 0; + } else if (dominoes[i] == 'R') { + force = INT_MAX; + } else { + force = (force == INT_MAX) ? INT_MAX : force + 1; + } + left[i] = force; + } + + for (int i = 0; i < n; i++) { + if (left[i] < right[i]) { + res[i] = 'L'; + } else if (right[i] < left[i]) { + res[i] = 'R'; + } + } + + return string(res.begin(), res.end()); + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} dominoes + * @return {string} + */ + pushDominoes(dominoes) { + const n = dominoes.length; + const left = new Array(n).fill(Infinity); + const right = new Array(n).fill(Infinity); + const res = dominoes.split(""); + + let force = Infinity; + for (let i = 0; i < n; i++) { + if (dominoes[i] === "R") { + force = 0; + } else if (dominoes[i] === "L") { + force = Infinity; + } else { + force = force === Infinity ? Infinity : force + 1; + } + right[i] = force; + } + + force = Infinity; + for (let i = n - 1; i >= 0; i--) { + if (dominoes[i] === "L") { + force = 0; + } else if (dominoes[i] === "R") { + force = Infinity; + } else { + force = force === Infinity ? Infinity : force + 1; + } + left[i] = force; + } + + for (let i = 0; i < n; i++) { + if (left[i] < right[i]) { + res[i] = "L"; + } else if (right[i] < left[i]) { + res[i] = "R"; + } + } + + return res.join(""); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Simulation (Queue) + +::tabs-start + +```python +class Solution: + def pushDominoes(self, dominoes: str) -> str: + dom = list(dominoes) + q = deque() + + for i, d in enumerate(dom): + if d != ".": + q.append((i, d)) + + while q: + i, d = q.popleft() + + if d == "L" and i > 0 and dom[i - 1] == ".": + q.append((i - 1, "L")) + dom[i - 1] = "L" + elif d == "R": + if i + 1 < len(dom) and dom[i + 1] == ".": + if i + 2 < len(dom) and dom[i + 2] == "L": + q.popleft() + else: + q.append((i + 1, "R")) + dom[i + 1] = "R" + + return "".join(dom) +``` + +```java +public class Solution { + public String pushDominoes(String dominoes) { + char[] dom = dominoes.toCharArray(); + Queue q = new LinkedList<>(); + + for (int i = 0; i < dom.length; i++) { + if (dom[i] != '.') { + q.add(new int[] {i, dom[i]}); + } + } + + while (!q.isEmpty()) { + int[] curr = q.poll(); + int i = curr[0]; + char d = (char) curr[1]; + + if (d == 'L' && i > 0 && dom[i - 1] == '.') { + q.add(new int[] {i - 1, 'L'}); + dom[i - 1] = 'L'; + } else if (d == 'R') { + if (i + 1 < dom.length && dom[i + 1] == '.') { + if (i + 2 < dom.length && dom[i + 2] == 'L') { + q.poll(); + } else { + q.add(new int[] {i + 1, 'R'}); + dom[i + 1] = 'R'; + } + } + } + } + + return new String(dom); + } +} +``` + +```cpp +class Solution { +public: + string pushDominoes(string dominoes) { + vector dom(dominoes.begin(), dominoes.end()); + queue> q; + + for (int i = 0; i < dom.size(); i++) { + if (dom[i] != '.') { + q.push({i, dom[i]}); + } + } + + while (!q.empty()) { + auto [i, d] = q.front(); + q.pop(); + + if (d == 'L' && i > 0 && dom[i - 1] == '.') { + q.push({i - 1, 'L'}); + dom[i - 1] = 'L'; + } else if (d == 'R') { + if (i + 1 < dom.size() && dom[i + 1] == '.') { + if (i + 2 < dom.size() && dom[i + 2] == 'L') { + q.pop(); + } else { + q.push({i + 1, 'R'}); + dom[i + 1] = 'R'; + } + } + } + } + + return string(dom.begin(), dom.end()); + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} dominoes + * @return {string} + */ + pushDominoes(dominoes) { + const dom = dominoes.split(""); + const q = new Queue(); + + for (let i = 0; i < dom.length; i++) { + if (dom[i] !== ".") { + q.push([i, dom[i]]); + } + } + + while (!q.isEmpty()) { + const [i, d] = q.pop(); + + if (d === "L" && i > 0 && dom[i - 1] === ".") { + q.push([i - 1, "L"]); + dom[i - 1] = "L"; + } else if (d === "R") { + if (i + 1 < dom.length && dom[i + 1] === ".") { + if (i + 2 < dom.length && dom[i + 2] === "L") { + q.pop(); + } else { + q.push([i + 1, "R"]); + dom[i + 1] = "R"; + } + } + } + } + + return dom.join(""); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 4. Iteration (Greedy) + +::tabs-start + +```python +class Solution: + def pushDominoes(self, dominoes: str) -> str: + res = [] + dots = 0 + R = False + + for d in dominoes: + if d == '.': + dots += 1 + elif d == 'R': + if R: + # Previous was 'R', and current is also 'R'. + # Append 'R' for all the dots between them. + res.append('R' * (dots + 1)) + elif dots > 0: + # Previous was not 'R'. The previous dots remain unchanged. + res.append('.' * dots) + dots = 0 + R = True # Current is 'R' + else: + if R: + # Append the previous 'R'. + res.append('R') + if dots > 0: + # Half the dots are affected by the previous 'R'. + res.append('R' * (dots // 2)) + + # Add a '.' if there's an odd number of dots. + if (dots % 2) != 0: + res.append('.') + + # Append half the dots as 'L'. + res.append('L' * (dots // 2)) + + # Append the current 'L'. + res.append('L') + R, dots = False, 0 + else: + # There is no 'R' on the left. + # Append 'L' for all the dots and the current 'L'. + res.append('L' * (dots + 1)) + dots = 0 + + if R: + # Trailing dots are affected by the last 'R'. + res.append('R' * (dots + 1)) + else: + # Trailing dots remain unchanged as there is no previous 'R'. + res.append('.' * dots) + + return ''.join(res) +``` + +```java +public class Solution { + public String pushDominoes(String dominoes) { + StringBuilder res = new StringBuilder(); + int dots = 0; + boolean R = false; + + for (char d : dominoes.toCharArray()) { + if (d == '.') { + dots++; + } else if (d == 'R') { + if (R) { + // Previous was 'R', and current is also 'R'. + // Append 'R' for all the dots between them. + res.append("R".repeat(dots + 1)); + } else if (dots > 0) { + // Previous was not 'R'. The previous dots remain unchanged. + res.append(".".repeat(dots)); + } + dots = 0; + R = true; // Current is 'R'. + } else { + if (R) { + // Append the previous 'R'. + res.append("R"); + if (dots > 0) { + // Half the dots are affected by the previous 'R'. + res.append("R".repeat(dots / 2)); + + // Add a '.' if there's an odd number of dots. + if (dots % 2 != 0) { + res.append("."); + } + + // Append half the dots as 'L'. + res.append("L".repeat(dots / 2)); + } + // Append the current 'L'. + res.append("L"); + R = false; + dots = 0; + } else { + // There is no 'R' on the left. + // Append 'L' for all the dots and the current 'L'. + res.append("L".repeat(dots + 1)); + dots = 0; + } + } + } + + if (R) { + // Trailing dots are affected by the last 'R'. + res.append("R".repeat(dots + 1)); + } else { + // Trailing dots remain unchanged as there is no previous 'R'. + res.append(".".repeat(dots)); + } + + return res.toString(); + } +} +``` + +```cpp +class Solution { +public: + string pushDominoes(string dominoes) { + string res = ""; + int dots = 0; + bool R = false; + + for (char d : dominoes) { + if (d == '.') { + dots++; + } else if (d == 'R') { + if (R) { + // Previous was 'R', and current is also 'R'. + // Append 'R' for all the dots between them. + res += string(dots + 1, 'R'); + } else if (dots > 0) { + // Previous was not 'R'. The previous dots remain unchanged. + res += string(dots, '.'); + } + dots = 0; + R = true; // Current is 'R'. + } else { + if (R) { + // Append the previous 'R'. + res += 'R'; + if (dots > 0) { + // Half the dots are affected by the previous 'R'. + res += string(dots / 2, 'R'); + + // Add a '.' if there's an odd number of dots. + if (dots % 2 != 0) { + res += '.'; + } + + // Append half the dots as 'L'. + res += string(dots / 2, 'L'); + } + // Append the current 'L'. + res += 'L'; + R = false; + dots = 0; + } else { + // There is no 'R' on the left. + // Append 'L' for all the dots and the current 'L'. + res += string(dots + 1, 'L'); + dots = 0; + } + } + } + + if (R) { + // Trailing dots are affected by the last 'R'. + res += string(dots + 1, 'R'); + } else { + // Trailing dots remain unchanged as there is no previous 'R'. + res += string(dots, '.'); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} dominoes + * @return {string} + */ + pushDominoes(dominoes) { + let res = []; + let dots = 0; + let R = false; + + for (let d of dominoes) { + if (d === '.') { + dots++; + } else if (d === 'R') { + if (R) { + // Previous was 'R', and current is also 'R'. + // Append 'R' for all the dots between them. + res.push('R'.repeat(dots + 1)); + } else if (dots > 0) { + // Previous was not 'R'. The previous dots remain unchanged. + res.push('.'.repeat(dots)); + } + dots = 0; + R = true; // Current is 'R'. + } else { + if (R) { + // Append the previous 'R'. + res.push('R'); + if (dots > 0) { + // Half the dots are affected by the previous 'R'. + res.push('R'.repeat(Math.floor(dots / 2))); + + // Add a '.' if there's an odd number of dots. + if (dots % 2 !== 0) { + res.push('.'); + } + + // Append half the dots as 'L'. + res.push('L'.repeat(Math.floor(dots / 2))); + } + // Append the current 'L'. + res.push('L'); + R = false; + dots = 0; + } else { + // There is no 'R' on the left. + // Append 'L' for all the dots and the current 'L'. + res.push('L'.repeat(dots + 1)); + dots = 0; + } + } + } + + if (R) { + // Trailing dots are affected by the last 'R'. + res.push('R'.repeat(dots + 1)); + } else { + // Trailing dots remain unchanged as there is no previous 'R'. + res.push('.'.repeat(dots)); + } + + return res.join(''); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ for only the output string. \ No newline at end of file diff --git a/articles/range-sum-query-immutable.md b/articles/range-sum-query-immutable.md new file mode 100644 index 000000000..f53012caa --- /dev/null +++ b/articles/range-sum-query-immutable.md @@ -0,0 +1,452 @@ +## 1. Brute Force + +::tabs-start + +```python +class NumArray: + def __init__(self, nums): + self.nums = nums + + def sumRange(self, left, right): + res = 0 + for i in range(left, right + 1): + res += self.nums[i] + return res +``` + +```java +public class NumArray { + private final int[] nums; + + public NumArray(int[] nums) { + this.nums = nums; + } + + public int sumRange(int left, int right) { + int res = 0; + for (int i = left; i <= right; i++) { + res += nums[i]; + } + return res; + } +} +``` + +```cpp +class NumArray { +private: + const vector& nums; + +public: + NumArray(const vector& nums) : nums(nums) {} + + int sumRange(int left, int right) { + int res = 0; + for (int i = left; i <= right; i++) { + res += nums[i]; + } + return res; + } +}; +``` + +```javascript +class NumArray { + /** + * @param {number[]} nums + */ + constructor(nums) { + this.nums = nums; + } + + /** + * @param {number} left + * @param {number} right + * @return {number} + */ + sumRange(left, right) { + let res = 0; + for (let i = left; i <= right; i++) { + res += this.nums[i]; + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ for each $sumRange()$ query. +* Space complexity: $O(1)$ since we only make a reference to the input array. + +--- + +## 2. Prefix Sum - I + +::tabs-start + +```python +class NumArray: + def __init__(self, nums): + self.prefix = [] + cur = 0 + for num in nums: + cur += num + self.prefix.append(cur) + + def sumRange(self, left, right): + rightSum = self.prefix[right] + leftSum = self.prefix[left - 1] if left > 0 else 0 + return rightSum - leftSum +``` + +```java +public class NumArray { + private int[] prefix; + + public NumArray(int[] nums) { + prefix = new int[nums.length]; + int cur = 0; + for (int i = 0; i < nums.length; i++) { + cur += nums[i]; + prefix[i] = cur; + } + } + + public int sumRange(int left, int right) { + int rightSum = prefix[right]; + int leftSum = left > 0 ? prefix[left - 1] : 0; + return rightSum - leftSum; + } +} +``` + +```cpp +class NumArray { +private: + vector prefix; + +public: + NumArray(const vector& nums) { + int cur = 0; + for (int num : nums) { + cur += num; + prefix.push_back(cur); + } + } + + int sumRange(int left, int right) { + int rightSum = prefix[right]; + int leftSum = left > 0 ? prefix[left - 1] : 0; + return rightSum - leftSum; + } +}; +``` + +```javascript +class NumArray { + /** + * @param {number[]} nums + */ + constructor(nums) { + this.prefix = []; + let cur = 0; + for (let num of nums) { + cur += num; + this.prefix.push(cur); + } + } + + /** + * @param {number} left + * @param {number} right + * @return {number} + */ + sumRange(left, right) { + const rightSum = this.prefix[right]; + const leftSum = left > 0 ? this.prefix[left - 1] : 0; + return rightSum - leftSum; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(1)$ for each $sumRange()$ query, $O(n)$ for building the prefix sum array. +* Space complexity: $O(n)$ + +--- + +## 3. Prefix Sum - II + +::tabs-start + +```python +class NumArray: + def __init__(self, nums): + self.prefix = [0] * (len(nums) + 1) + for i in range(len(nums)): + self.prefix[i + 1] = self.prefix[i] + nums[i] + + + def sumRange(self, left, right): + return self.prefix[right + 1] - self.prefix[left] +``` + +```java +public class NumArray { + private int[] prefix; + + public NumArray(int[] nums) { + prefix = new int[nums.length + 1]; + for (int i = 0; i < nums.length; i++) { + prefix[i + 1] = prefix[i] + nums[i]; + } + } + + public int sumRange(int left, int right) { + return prefix[right + 1] - prefix[left]; + } +} +``` + +```cpp +class NumArray { +private: + vector prefix; + +public: + NumArray(const vector& nums) { + prefix = vector(nums.size() + 1, 0); + for (int i = 0; i < nums.size(); i++) { + prefix[i + 1] = prefix[i] + nums[i]; + } + } + + int sumRange(int left, int right) { + return prefix[right + 1] - prefix[left]; + } +}; +``` + +```javascript +class NumArray { + /** + * @param {number[]} nums + */ + constructor(nums) { + this.prefix = new Array(nums.length + 1).fill(0); + for (let i = 0; i < nums.length; i++) { + this.prefix[i + 1] = this.prefix[i] + nums[i]; + } + } + + /** + * @param {number} left + * @param {number} right + * @return {number} + */ + sumRange(left, right) { + return this.prefix[right + 1] - this.prefix[left]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(1)$ for each $sumRange()$ query, $O(n)$ for building the prefix sum array. +* Space complexity: $O(n)$ + +--- + +## 4. Segment Tree + +::tabs-start + +```python +class SegmentTree: + def __init__(self, nums): + self.n = len(nums) + self.tree = [0] * (2 * self.n) + for i in range(self.n): + self.tree[self.n + i] = nums[i] + for i in range(self.n - 1, 0, -1): + self.tree[i] = self.tree[2 * i] + self.tree[2 * i + 1] + + def query(self, left, right): + left += self.n + right += self.n + 1 + res = 0 + while left < right: + if left % 2 == 1: + res += self.tree[left] + left += 1 + if right % 2 == 1: + right -= 1 + res += self.tree[right] + left //= 2 + right //= 2 + return res + +class NumArray: + def __init__(self, nums): + self.segTree = SegmentTree(nums) + + def sumRange(self, left, right): + return self.segTree.query(left, right) +``` + +```java +class SegmentTree { + private int[] tree; + private int n; + + public SegmentTree(int[] nums) { + n = nums.length; + tree = new int[2 * n]; + for (int i = 0; i < n; i++) { + tree[n + i] = nums[i]; + } + for (int i = n - 1; i > 0; i--) { + tree[i] = tree[2 * i] + tree[2 * i + 1]; + } + } + + public int query(int left, int right) { + int sum = 0; + left += n; + right += n + 1; + while (left < right) { + if (left % 2 == 1) sum += tree[left++]; + if (right % 2 == 1) sum += tree[--right]; + left /= 2; + right /= 2; + } + return sum; + } +} + +public class NumArray { + private SegmentTree segTree; + + public NumArray(int[] nums) { + segTree = new SegmentTree(nums); + } + + public int sumRange(int left, int right) { + return segTree.query(left, right); + } +} +``` + +```cpp +class SegmentTree { +private: + vector tree; + int n; + +public: + SegmentTree(const vector& nums) { + n = nums.size(); + tree.resize(2 * n); + for (int i = 0; i < n; i++) { + tree[n + i] = nums[i]; + } + for (int i = n - 1; i > 0; i--) { + tree[i] = tree[2 * i] + tree[2 * i + 1]; + } + } + + int query(int left, int right) { + int sum = 0; + left += n; + right += n + 1; + while (left < right) { + if (left % 2 == 1) sum += tree[left++]; + if (right % 2 == 1) sum += tree[--right]; + left /= 2; + right /= 2; + } + return sum; + } +}; + +class NumArray { +private: + SegmentTree segTree; + +public: + NumArray(const vector& nums) : segTree(nums) {} + + int sumRange(int left, int right) { + return segTree.query(left, right); + } +}; +``` + +```javascript +class SegmentTree { + /** + * @param {number[]} nums + */ + constructor(nums) { + this.n = nums.length; + this.tree = Array(this.n * 2).fill(0); + for (let i = 0; i < this.n; i++) { + this.tree[this.n + i] = nums[i]; + } + for (let i = this.n - 1; i > 0; i--) { + this.tree[i] = this.tree[2 * i] + this.tree[2 * i + 1]; + } + } + + /** + * @param {number} left + * @param {number} right + * @return {number} + */ + query(left, right) { + let sum = 0; + left += this.n; + right += this.n + 1; + while (left < right) { + if (left % 2 === 1) sum += this.tree[left++]; + if (right % 2 === 1) sum += this.tree[--right]; + left = Math.floor(left / 2); + right = Math.floor(right / 2); + } + return sum; + } +} + +class NumArray { + /** + * @param {number[]} nums + */ + constructor(nums) { + this.segTree = new SegmentTree(nums); + } + + /** + * @param {number} left + * @param {number} right + * @return {number} + */ + sumRange(left, right) { + return this.segTree.query(left, right); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(\log n)$ for each $sumRange()$ query, $O(n)$ for building the Segment Tree. +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/reconstruct-flight-path.md b/articles/reconstruct-flight-path.md index 634aa641c..b76aff7a7 100644 --- a/articles/reconstruct-flight-path.md +++ b/articles/reconstruct-flight-path.md @@ -290,7 +290,7 @@ class Solution { * Time complexity: $O(E * V)$ * Space complexity: $O(E * V)$ -> Where $E$ is the number of tickets (edges) and $V$ is the number of airpots (vertices). +> Where $E$ is the number of tickets (edges) and $V$ is the number of airports (vertices). --- @@ -509,7 +509,7 @@ class Solution { * Time complexity: $O(E\log E)$ * Space complexity: $O(E)$ -> Where $E$ is the number of tickets (edges) and $V$ is the number of airpots (vertices). +> Where $E$ is the number of tickets (edges) and $V$ is the number of airports (vertices). --- @@ -725,4 +725,4 @@ class Solution { * Time complexity: $O(E\log E)$ * Space complexity: $O(E)$ -> Where $E$ is the number of tickets (edges) and $V$ is the number of airpots (vertices). \ No newline at end of file +> Where $E$ is the number of tickets (edges) and $V$ is the number of airports (vertices). \ No newline at end of file diff --git a/articles/redistribute-characters-to-make-all-strings-equal.md b/articles/redistribute-characters-to-make-all-strings-equal.md new file mode 100644 index 000000000..b825a3786 --- /dev/null +++ b/articles/redistribute-characters-to-make-all-strings-equal.md @@ -0,0 +1,228 @@ +## 1. Frequency Count (Hash Map) + +::tabs-start + +```python +class Solution: + def makeEqual(self, words: List[str]) -> bool: + char_cnt = defaultdict(int) + + for w in words: + for c in w: + char_cnt[c] += 1 + + for c in char_cnt: + if char_cnt[c] % len(words): + return False + return True +``` + +```java +public class Solution { + public boolean makeEqual(String[] words) { + Map charCnt = new HashMap<>(); + + for (String w : words) { + for (char c : w.toCharArray()) { + charCnt.put(c, charCnt.getOrDefault(c, 0) + 1); + } + } + + for (int count : charCnt.values()) { + if (count % words.length != 0) { + return false; + } + } + return true; + } +} +``` + +```cpp +class Solution { +public: + bool makeEqual(vector& words) { + unordered_map charCnt; + + for (const string& w : words) { + for (char c : w) { + charCnt[c]++; + } + } + + for (const auto& entry : charCnt) { + if (entry.second % words.size() != 0) { + return false; + } + } + return true; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} words + * @return {boolean} + */ + makeEqual(words) { + const charCnt = {}; + + for (let w of words) { + for (let c of w) { + charCnt[c] = (charCnt[c] || 0) + 1; + } + } + + for (let count of Object.values(charCnt)) { + if (count % words.length !== 0) { + return false; + } + } + return true; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * m)$ +* Space complexity: $O(1)$ since we have at most $26$ different characters. + +> Where $n$ is the number of words and $m$ is the average length of each word. + +--- + +## 2. Frequency Count (Array) + +::tabs-start + +```python +class Solution: + def makeEqual(self, words: List[str]) -> bool: + freq = [0] * 26 + flag = 0 + n = len(words) + + for w in words: + for c in w: + i = ord(c) - ord('a') + if freq[i] != 0: + freq[i] += 1 + if freq[i] % n == 0: + flag += 1 + else: + freq[i] += 1 + if freq[i] % n != 0: + flag -= 1 + freq[i] %= n + + return flag == 0 +``` + +```java +public class Solution { + public boolean makeEqual(String[] words) { + int[] freq = new int[26]; + int flag = 0; + int n = words.length; + + for (String w : words) { + for (char c : w.toCharArray()) { + int i = c - 'a'; + if (freq[i] != 0) { + freq[i]++; + if (freq[i] % n == 0) { + flag++; + } + } else { + freq[i]++; + if (freq[i] % n != 0) { + flag--; + } + } + freq[i] %= n; + } + } + + return flag == 0; + } +} +``` + +```cpp +class Solution { +public: + bool makeEqual(vector& words) { + vector freq(26, 0); + int flag = 0; + int n = words.size(); + + for (const string& w : words) { + for (char c : w) { + int i = c - 'a'; + if (freq[i] != 0) { + freq[i]++; + if (freq[i] % n == 0) { + flag++; + } + } else { + freq[i]++; + if (freq[i] % n != 0) { + flag--; + } + } + freq[i] %= n; + } + } + + return flag == 0; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} words + * @return {boolean} + */ + makeEqual(words) { + const freq = Array(26).fill(0); + let flag = 0; + const n = words.length; + + for (let w of words) { + for (let c of w) { + const i = c.charCodeAt(0) - 'a'.charCodeAt(0); + if (freq[i] !== 0) { + freq[i]++; + if (freq[i] % n === 0) { + flag++; + } + } else { + freq[i]++; + if (freq[i] % n !== 0) { + flag--; + } + } + freq[i] %= n; + } + } + + return flag === 0; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * m)$ +* Space complexity: $O(1)$ since we have at most $26$ different characters. + +> Where $n$ is the number of words and $m$ is the average length of each word. \ No newline at end of file diff --git a/articles/redundant-connection.md b/articles/redundant-connection.md index 6d54e1e95..4a51a9aa3 100644 --- a/articles/redundant-connection.md +++ b/articles/redundant-connection.md @@ -617,7 +617,7 @@ class Solution { --- -## 3. Kanh's Algorithm (BFS) +## 3. Topological Sort (Kahn's Algorithm) ::tabs-start diff --git a/articles/set-mismatch.md b/articles/set-mismatch.md new file mode 100644 index 000000000..f29e54444 --- /dev/null +++ b/articles/set-mismatch.md @@ -0,0 +1,721 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def findErrorNums(self, nums: List[int]) -> List[int]: + res = [0, 0] + n = len(nums) + + for i in range(1, n + 1): + cnt = 0 + for num in nums: + if num == i: + cnt += 1 + + if cnt == 0: + res[1] = i + elif cnt == 2: + res[0] = i + + return res +``` + +```java +public class Solution { + public int[] findErrorNums(int[] nums) { + int[] res = new int[2]; + int n = nums.length; + + for (int i = 1; i <= n; i++) { + int cnt = 0; + for (int num : nums) { + if (num == i) { + cnt++; + } + } + + if (cnt == 0) { + res[1] = i; + } else if (cnt == 2) { + res[0] = i; + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector findErrorNums(vector& nums) { + vector res(2, 0); + int n = nums.size(); + + for (int i = 1; i <= n; i++) { + int cnt = 0; + for (int num : nums) { + if (num == i) { + cnt++; + } + } + + if (cnt == 0) { + res[1] = i; + } else if (cnt == 2) { + res[0] = i; + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number[]} + */ + findErrorNums(nums) { + const res = [0, 0]; + const n = nums.length; + + for (let i = 1; i <= n; i++) { + let cnt = 0; + for (const num of nums) { + if (num === i) { + cnt++; + } + } + + if (cnt === 0) { + res[1] = i; + } else if (cnt === 2) { + res[0] = i; + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(1)$ + +--- + +## 2. Sorting + +::tabs-start + +```python +class Solution: + def findErrorNums(self, nums: List[int]) -> List[int]: + res = [0, 1] + nums.sort() + + for i in range(1, len(nums)): + if nums[i] == nums[i - 1]: + res[0] = nums[i] + elif nums[i] - nums[i - 1] == 2: + res[1] = nums[i] - 1 + + if nums[-1] != len(nums): + res[1] = len(nums) + return res +``` + +```java +public class Solution { + public int[] findErrorNums(int[] nums) { + int[] res = {0, 1}; + Arrays.sort(nums); + + for (int i = 1; i < nums.length; i++) { + if (nums[i] == nums[i - 1]) { + res[0] = nums[i]; + } else if (nums[i] - nums[i - 1] == 2) { + res[1] = nums[i] - 1; + } + } + + if (nums[nums.length - 1] != nums.length) { + res[1] = nums.length; + } + return res; + } +} +``` + +```cpp +class Solution { +public: + vector findErrorNums(vector& nums) { + vector res = {0, 1}; + sort(nums.begin(), nums.end()); + + for (int i = 1; i < nums.size(); i++) { + if (nums[i] == nums[i - 1]) { + res[0] = nums[i]; + } else if (nums[i] - nums[i - 1] == 2) { + res[1] = nums[i] - 1; + } + } + + if (nums.back() != nums.size()) { + res[1] = nums.size(); + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number[]} + */ + findErrorNums(nums) { + const res = [0, 1]; + nums.sort((a, b) => a - b); + + for (let i = 1; i < nums.length; i++) { + if (nums[i] === nums[i - 1]) { + res[0] = nums[i]; + } else if (nums[i] - nums[i - 1] === 2) { + res[1] = nums[i] - 1; + } + } + + if (nums[nums.length - 1] !== nums.length) { + res[1] = nums.length; + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(1)$ or $O(n)$ depending on the sorting algorithm. + +--- + +## 3. Frequency Count (Hash Table) + +::tabs-start + +```python +class Solution: + def findErrorNums(self, nums: List[int]) -> List[int]: + res = [0, 0] # [duplicate, missing] + count = Counter(nums) + + for i in range(1, len(nums) + 1): + if count[i] == 0: + res[1] = i + if count[i] == 2: + res[0] = i + + return res +``` + +```java +public class Solution { + public int[] findErrorNums(int[] nums) { + int n = nums.length; + int[] count = new int[n + 1]; + int[] res = new int[2]; + + for (int num : nums) { + count[num]++; + } + + for (int i = 1; i <= n; i++) { + if (count[i] == 0) { + res[1] = i; + } + if (count[i] == 2) { + res[0] = i; + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector findErrorNums(vector& nums) { + int n = nums.size(); + vector count(n + 1, 0); + vector res(2, 0); + + for (int num : nums) { + count[num]++; + } + + for (int i = 1; i <= n; i++) { + if (count[i] == 0) { + res[1] = i; + } + if (count[i] == 2) { + res[0] = i; + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number[]} + */ + findErrorNums(nums) { + const n = nums.length; + const count = Array(n + 1).fill(0); + const res = [0, 0]; + + for (const num of nums) { + count[num]++; + } + + for (let i = 1; i <= n; i++) { + if (count[i] === 0) { + res[1] = i; + } + if (count[i] === 2) { + res[0] = i; + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 4. Negative Marking + +::tabs-start + +```python +class Solution: + def findErrorNums(self, nums: List[int]) -> List[int]: + res = [0, 0] + + for num in nums: + num = abs(num) + nums[num - 1] *= -1 + if nums[num - 1] > 0: + res[0] = num + + for i, num in enumerate(nums): + if num > 0 and i + 1 != res[0]: + res[1] = i + 1 + return res +``` + +```java +public class Solution { + public int[] findErrorNums(int[] nums) { + int[] res = new int[2]; + + for (int num : nums) { + int absNum = Math.abs(num); + if (nums[absNum - 1] < 0) { + res[0] = absNum; + } else { + nums[absNum - 1] *= -1; + } + } + + for (int i = 0; i < nums.length; i++) { + if (nums[i] > 0 && i + 1 != res[0]) { + res[1] = i + 1; + return res; + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector findErrorNums(vector& nums) { + vector res(2); + + for (int num : nums) { + int absNum = abs(num); + if (nums[absNum - 1] < 0) { + res[0] = absNum; + } else { + nums[absNum - 1] *= -1; + } + } + + for (int i = 0; i < nums.size(); i++) { + if (nums[i] > 0 && i + 1 != res[0]) { + res[1] = i + 1; + return res; + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number[]} + */ + findErrorNums(nums) { + const res = [0, 0]; + + for (let num of nums) { + const absNum = Math.abs(num); + if (nums[absNum - 1] < 0) { + res[0] = absNum; + } else { + nums[absNum - 1] *= -1; + } + } + + for (let i = 0; i < nums.length; i++) { + if (nums[i] > 0 && i + 1 !== res[0]) { + res[1] = i + 1; + return res; + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ + +--- + +## 5. Math + +::tabs-start + +```python +class Solution: + def findErrorNums(self, nums: List[int]) -> List[int]: + N = len(nums) + x = 0 # duplicate - missing + y = 0 # duplicate^2 - missing^2 + + for i in range(1, N + 1): + x += nums[i - 1] - i + y += nums[i - 1]**2 - i**2 + + missing = (y - x**2) // (2 * x) + duplicate = missing + x + return [duplicate, missing] +``` + +```java +public class Solution { + public int[] findErrorNums(int[] nums) { + int N = nums.length; + int x = 0; // duplicate - missing + int y = 0; // duplicate^2 - missing^2 + + for (int i = 1; i <= N; i++) { + x += nums[i - 1] - i; + y += nums[i - 1] * nums[i - 1] - i * i; + } + + int missing = (y - x * x) / (2 * x); + int duplicate = missing + x; + return new int[]{duplicate, missing}; + } +} +``` + +```cpp +class Solution { +public: + vector findErrorNums(vector& nums) { + int N = nums.size(); + long long x = 0; // duplicate - missing + long long y = 0; // duplicate^2 - missing^2 + + for (int i = 1; i <= N; i++) { + x += nums[i - 1] - i; + y += (long long)nums[i - 1] * nums[i - 1] - (long long)i * i; + } + + int missing = (y - x * x) / (2 * x); + int duplicate = missing + x; + return {duplicate, missing}; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number[]} + */ + findErrorNums(nums) { + const N = nums.length; + let x = 0; // duplicate - missing + let y = 0; // duplicate^2 - missing^2 + + for (let i = 1; i <= N; i++) { + x += nums[i - 1] - i; + y += nums[i - 1] ** 2 - i ** 2; + } + + const missing = (y - x ** 2) / (2 * x); + const duplicate = missing + x; + return [duplicate, missing]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ + + +--- + +## 6. Bitwise XOR + +::tabs-start + +```python +class Solution: + def findErrorNums(self, nums: List[int]) -> List[int]: + N = len(nums) + # a ^ a = 0 + # xorr = (1 ^ 2 ^ ... N) ^ (nums[0] ^ nums[1] ^ ... nums[N - 1]) + # xorr = missing ^ duplicate + xorr = 0 + for i in range(1, N + 1): + xorr ^= i + xorr ^= nums[i - 1] + + # bit that is set in only one number among (duplicate, missing), + # will be set in (duplicate ^ missing) + # take rightMost set bit for simplicity + rightMostBit = xorr & ~(xorr - 1) + + # divide numbers (from nums, from [1, N]) into two sets w.r.t the rightMostBit + # xorr the numbers of these sets independently + x = y = 0 + for i in range(1, N + 1): + if i & rightMostBit: + x ^= i + else: + y ^= i + + if nums[i - 1] & rightMostBit: + x ^= nums[i - 1] + else: + y ^= nums[i - 1] + + # identify the duplicate number from x and y + for num in nums: + if num == x: + return [x, y] + return [y, x] +``` + +```java +public class Solution { + public int[] findErrorNums(int[] nums) { + int N = nums.length; + // a ^ a = 0 + // xorr = (1 ^ 2 ^ ... N) ^ (nums[0] ^ nums[1] ^ ... nums[N - 1]) + // xorr = missing ^ duplicate + int xorr = 0; + for (int i = 1; i <= N; i++) { + xorr ^= i; + xorr ^= nums[i - 1]; + } + + // bit that is set in only one number among (duplicate, missing), + // will be set in (duplicate ^ missing) + // take rightMost set bit for simplicity + int rightMostBit = xorr & ~(xorr - 1); + + // divide numbers (from nums, from [1, N]) into two sets w.r.t the rightMostBit + // xorr the numbers of these sets independently + int x = 0, y = 0; + for (int i = 1; i <= N; i++) { + if ((i & rightMostBit) != 0) { + x ^= i; + } else { + y ^= i; + } + + if ((nums[i - 1] & rightMostBit) != 0) { + x ^= nums[i - 1]; + } else { + y ^= nums[i - 1]; + } + } + + // identify the duplicate number from x and y + for (int num : nums) { + if (num == x) { + return new int[]{x, y}; + } + } + return new int[]{y, x}; + } +} +``` + +```cpp +class Solution { +public: + vector findErrorNums(vector& nums) { + int N = nums.size(); + // a ^ a = 0 + // xorr = (1 ^ 2 ^ ... N) ^ (nums[0] ^ nums[1] ^ ... nums[N - 1]) + // xorr = missing ^ duplicate + int xorr = 0; + for (int i = 1; i <= N; i++) { + xorr ^= i; + xorr ^= nums[i - 1]; + } + + // bit that is set in only one number among (duplicate, missing), + // will be set in (duplicate ^ missing) + // take rightMost set bit for simplicity + int rightMostBit = xorr & ~(xorr - 1); + + // divide numbers (from nums, from [1, N]) into two sets w.r.t the rightMostBit + // xorr the numbers of these sets independently + int x = 0, y = 0; + for (int i = 1; i <= N; i++) { + if (i & rightMostBit) { + x ^= i; + } else { + y ^= i; + } + + if (nums[i - 1] & rightMostBit) { + x ^= nums[i - 1]; + } else { + y ^= nums[i - 1]; + } + } + + // identify the duplicate number from x and y + for (int num : nums) { + if (num == x) { + return {x, y}; + } + } + return {y, x}; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number[]} + */ + findErrorNums(nums) { + const N = nums.length; + // a ^ a = 0 + // xorr = (1 ^ 2 ^ ... N) ^ (nums[0] ^ nums[1] ^ ... nums[N - 1]) + // xorr = missing ^ duplicate + let xorr = 0; + for (let i = 1; i <= N; i++) { + xorr ^= i; + xorr ^= nums[i - 1]; + } + + // bit that is set in only one number among (duplicate, missing), + // will be set in (duplicate ^ missing) + // take rightMost set bit for simplicity + const rightMostBit = xorr & ~(xorr - 1); + + // divide numbers (from nums, from [1, N]) into two sets w.r.t the rightMostBit + // xorr the numbers of these sets independently + let x = 0, y = 0; + for (let i = 1; i <= N; i++) { + if (i & rightMostBit) { + x ^= i; + } else { + y ^= i; + } + + if (nums[i - 1] & rightMostBit) { + x ^= nums[i - 1]; + } else { + y ^= nums[i - 1]; + } + } + + // identify the duplicate number from x and y + for (let num of nums) { + if (num === x) { + return [x, y]; + } + } + return [y, x]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/sort-an-array.md b/articles/sort-an-array.md new file mode 100644 index 000000000..7551c767e --- /dev/null +++ b/articles/sort-an-array.md @@ -0,0 +1,1105 @@ +## 1. Quick Sort + +::tabs-start + +```python +class Solution: + def partition(self, nums: List[int], left: int, right: int) -> int: + mid = (left + right) >> 1 + nums[mid], nums[left + 1] = nums[left + 1], nums[mid] + + if nums[left] > nums[right]: + nums[left], nums[right] = nums[right], nums[left] + if nums[left + 1] > nums[right]: + nums[left + 1], nums[right] = nums[right], nums[left + 1] + if nums[left] > nums[left + 1]: + nums[left], nums[left + 1] = nums[left + 1], nums[left] + + pivot = nums[left + 1] + i = left + 1 + j = right + + while True: + while True: + i += 1 + if not nums[i] < pivot: + break + while True: + j -= 1 + if not nums[j] > pivot: + break + if i > j: + break + nums[i], nums[j] = nums[j], nums[i] + + nums[left + 1], nums[j] = nums[j], nums[left + 1] + return j + + def quickSort(self, nums: List[int], left: int, right: int) -> None: + if right <= left + 1: + if right == left + 1 and nums[right] < nums[left]: + nums[left], nums[right] = nums[right], nums[left] + return + + j = self.partition(nums, left, right) + self.quickSort(nums, left, j - 1) + self.quickSort(nums, j + 1, right) + + def sortArray(self, nums: List[int]) -> List[int]: + self.quickSort(nums, 0, len(nums) - 1) + return nums +``` + +```java +public class Solution { + private int partition(int[] nums, int left, int right) { + int mid = (left + right) >> 1; + swap(nums, mid, left + 1); + + if (nums[left] > nums[right]) + swap(nums, left, right); + if (nums[left + 1] > nums[right]) + swap(nums, left + 1, right); + if (nums[left] > nums[left + 1]) + swap(nums, left, left + 1); + + int pivot = nums[left + 1]; + int i = left + 1; + int j = right; + + while (true) { + while (nums[++i] < pivot); + while (nums[--j] > pivot); + if (i > j) break; + swap(nums, i, j); + } + + nums[left + 1] = nums[j]; + nums[j] = pivot; + return j; + } + + private void swap(int[] nums, int i, int j) { + int temp = nums[i]; + nums[i] = nums[j]; + nums[j] = temp; + } + + private void quickSort(int[] nums, int left, int right) { + if (right <= left + 1) { + if (right == left + 1 && nums[right] < nums[left]) + swap(nums, left, right); + return; + } + + int j = partition(nums, left, right); + quickSort(nums, left, j - 1); + quickSort(nums, j + 1, right); + } + + public int[] sortArray(int[] nums) { + quickSort(nums, 0, nums.length - 1); + return nums; + } +} +``` + +```cpp +class Solution { +public: +int partition(vector& nums, int left, int right) { + int mid = (left + right) >> 1; + swap(nums[mid], nums[left + 1]); + + if (nums[left] > nums[right]) + swap(nums[left], nums[right]); + if (nums[left + 1] > nums[right]) + swap(nums[left + 1], nums[right]); + if (nums[left] > nums[left + 1]) + swap(nums[left], nums[left + 1]); + + int pivot = nums[left + 1]; + int i = left + 1; + int j = right; + + while (true) { + while (nums[++i] < pivot); + while (nums[--j] > pivot); + if (i > j) break; + swap(nums[i], nums[j]); + } + + nums[left + 1] = nums[j]; + nums[j] = pivot; + return j; + } + + void quickSort(vector& nums, int left, int right) { + if (right <= left + 1) { + if (right == left + 1 && nums[right] < nums[left]) + swap(nums[left], nums[right]); + return; + } + + int j = partition(nums, left, right); + quickSort(nums, left, j - 1); + quickSort(nums, j + 1, right); + } + + vector sortArray(vector& nums) { + quickSort(nums, 0, nums.size() - 1); + return nums; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number[]} + */ + sortArray(nums) { + function partition(left, right) { + const mid = (left + right) >> 1; + [nums[mid], nums[left + 1]] = [nums[left + 1], nums[mid]]; + + if (nums[left] > nums[right]) + [nums[left], nums[right]] = [nums[right], nums[left]]; + if (nums[left + 1] > nums[right]) + [nums[left + 1], nums[right]] = [nums[right], nums[left + 1]]; + if (nums[left] > nums[left + 1]) + [nums[left], nums[left + 1]] = [nums[left + 1], nums[left]]; + + const pivot = nums[left + 1]; + let i = left + 1; + let j = right; + + while (true) { + while (nums[++i] < pivot); + while (nums[--j] > pivot); + if (i > j) break; + [nums[i], nums[j]] = [nums[j], nums[i]]; + } + + nums[left + 1] = nums[j]; + nums[j] = pivot; + return j; + } + + function quickSort(left, right) { + if (right <= left + 1) { + if (right == left + 1 && nums[right] < nums[left]) + [nums[left], nums[right]] = [nums[right], nums[left]]; + return; + } + + const j = partition(left, right); + quickSort(left, j - 1); + quickSort(j + 1, right); + } + + quickSort(0, nums.length - 1); + return nums; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ in average case, O(n ^ 2) in worst case. +* Space complexity: $O(\log n)$ for recursive stack. + +--- + +## 2. Merge Sort + +::tabs-start + +```python +class Solution: + def sortArray(self, nums: List[int]) -> List[int]: + def merge(arr, L, M, R): + left, right = arr[L : M + 1], arr[M + 1 : R + 1] + i, j, k = L, 0, 0 + + while j < len(left) and k < len(right): + if left[j] <= right[k]: + arr[i] = left[j] + j += 1 + else: + arr[i] = right[k] + k += 1 + i += 1 + while j < len(left): + nums[i] = left[j] + j += 1 + i += 1 + while k < len(right): + nums[i] = right[k] + k += 1 + i += 1 + + def mergeSort(arr, l, r): + if l == r: + return + + m = (l + r) // 2 + mergeSort(arr, l, m) + mergeSort(arr, m + 1, r) + merge(arr, l, m, r) + return + + mergeSort(nums, 0, len(nums)) + return nums +``` + +```java +public class Solution { + public int[] sortArray(int[] nums) { + mergeSort(nums, 0, nums.length - 1); + return nums; + } + + private void mergeSort(int[] arr, int l, int r) { + if (l >= r) return; + int m = (l + r) / 2; + mergeSort(arr, l, m); + mergeSort(arr, m + 1, r); + merge(arr, l, m, r); + } + + private void merge(int[] arr, int l, int m, int r) { + ArrayList temp = new ArrayList<>(); + int i = l; + int j = m + 1; + + while (i <= m && j <= r) { + if (arr[i] <= arr[j]) { + temp.add(arr[i]); + i++; + } else { + temp.add(arr[j]); + j++; + } + } + + + while (i <= m) { + temp.add(arr[i]); + i++; + } + + while (j <= r) { + temp.add(arr[j]); + j++; + } + + for (i = l; i <= r; i++) { + arr[i] = temp.get(i - l); + } + } +} +``` + +```cpp +class Solution { +public: + vector sortArray(vector& nums) { + mergeSort(nums, 0, nums.size() - 1); + return nums; + } + +private: + void mergeSort(vector& arr, int l, int r) { + if (l >= r) return; + int m = (l + r) / 2; + mergeSort(arr, l, m); + mergeSort(arr, m + 1, r); + merge(arr, l, m, r); + } + + void merge(vector& arr, int l, int m, int r) { + vector temp; + int i = l, j = m + 1; + + while (i <= m && j <= r) { + if (arr[i] <= arr[j]) { + temp.push_back(arr[i++]); + } else { + temp.push_back(arr[j++]); + } + } + + while (i <= m) temp.push_back(arr[i++]); + while (j <= r) temp.push_back(arr[j++]); + + for (int i = l; i <= r; i++) { + arr[i] = temp[i - l]; + } + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number[]} + */ + sortArray(nums) { + this.mergeSort(nums, 0, nums.length - 1); + return nums; + } + + /** + * @param {number[]} arr + * @param {number} l + * @param {number} r + * @return {void} + */ + mergeSort(arr, l, r) { + if (l >= r) return; + let m = Math.floor((l + r) / 2); + this.mergeSort(arr, l, m); + this.mergeSort(arr, m + 1, r); + this.merge(arr, l, m, r); + } + + /** + * @param {number[]} arr + * @param {number} l + * @param {number} m + * @param {number} r + * @return {void} + */ + merge(arr, l, m, r) { + let temp = []; + let i = l, j = m + 1; + + while (i <= m && j <= r) { + if (arr[i] <= arr[j]) { + temp.push(arr[i++]); + } else { + temp.push(arr[j++]); + } + } + + while (i <= m) temp.push(arr[i++]); + while (j <= r) temp.push(arr[j++]); + + for (let i = l; i <= r; i++) { + arr[i] = temp[i - l]; + } + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Heap Sort + +::tabs-start + +```python +class Solution: + def sortArray(self, nums: List[int]) -> List[int]: + self.heapSort(nums) + return nums + + def heapify(self, arr, n, i): + l = (i << 1) + 1 + r = (i << 1) + 2 + largestNode = i + + if l < n and arr[l] > arr[largestNode]: + largestNode = l + + if r < n and arr[r] > arr[largestNode]: + largestNode = r + + if largestNode != i: + arr[i], arr[largestNode] = arr[largestNode], arr[i] + self.heapify(arr, n, largestNode) + + def heapSort(self, arr): + n = len(arr) + for i in range(n // 2 - 1, -1, -1): + self.heapify(arr, n, i) + + for i in range(n - 1, 0, -1): + arr[0], arr[i] = arr[i], arr[0] + self.heapify(arr, i, 0) +``` + +```java +public class Solution { + public int[] sortArray(int[] nums) { + heapSort(nums); + return nums; + } + + private void heapify(int[] arr, int n, int i) { + int l = (i << 1) + 1; + int r = (i << 1) + 2; + int largestNode = i; + + if (l < n && arr[l] > arr[largestNode]) { + largestNode = l; + } + + if (r < n && arr[r] > arr[largestNode]) { + largestNode = r; + } + + if (largestNode != i) { + int temp = arr[i]; + arr[i] = arr[largestNode]; + arr[largestNode] = temp; + heapify(arr, n, largestNode); + } + } + + private void heapSort(int[] arr) { + int n = arr.length; + for (int i = n / 2 - 1; i >= 0; i--) { + heapify(arr, n, i); + } + + for (int i = n - 1; i > 0; i--) { + int temp = arr[0]; + arr[0] = arr[i]; + arr[i] = temp; + heapify(arr, i, 0); + } + } +} +``` + +```cpp +class Solution { +public: + vector sortArray(vector& nums) { + heapSort(nums); + return nums; + } +private: + void heapify(vector& arr, int n, int i) { + int l = (i << 1) + 1; + int r = (i << 1) + 2; + int largestNode = i; + + if (l < n && arr[l] > arr[largestNode]) { + largestNode = l; + } + + if (r < n && arr[r] > arr[largestNode]) { + largestNode = r; + } + + if (largestNode != i) { + swap(arr[i], arr[largestNode]); + heapify(arr, n, largestNode); + } + } + + void heapSort(vector& arr) { + int n = arr.size(); + for (int i = n / 2 - 1; i >= 0; i--) { + heapify(arr, n, i); + } + + for (int i = n - 1; i > 0; i--) { + swap(arr[0], arr[i]); + heapify(arr, i, 0); + } + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number[]} + */ + sortArray(nums) { + this.heapSort(nums); + return nums; + } + + /** + * @param {number[]} arr + * @param {number} n + * @param {number} i + * @return {void} + */ + heapify(arr, n, i) { + let l = (i << 1) + 1; + let r = (i << 1) + 2; + let largestNode = i; + + if (l < n && arr[l] > arr[largestNode]) { + largestNode = l; + } + + if (r < n && arr[r] > arr[largestNode]) { + largestNode = r; + } + + if (largestNode !== i) { + [arr[i], arr[largestNode]] = [arr[largestNode], arr[i]]; + this.heapify(arr, n, largestNode); + } + } + + /** + * @param {number[]} arr + * @return {void} + */ + heapSort(arr) { + let n = arr.length; + for (let i = Math.floor(n / 2) - 1; i >= 0; i--) { + this.heapify(arr, n, i); + } + + for (let i = n - 1; i > 0; i--) { + [arr[0], arr[i]] = [arr[i], arr[0]]; + this.heapify(arr, i, 0); + } + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(\log n)$ for recursive stack. + +--- + +## 4. Counting Sort + +::tabs-start + +```python +class Solution: + def sortArray(self, nums: List[int]) -> List[int]: + def counting_sort(): + count = defaultdict(int) + minVal, maxVal = min(nums), max(nums) + for val in nums: + count[val] += 1 + + index = 0 + for val in range(minVal, maxVal + 1): + while count[val] > 0: + nums[index] = val + index += 1 + count[val] -= 1 + + counting_sort() + return nums +``` + +```java +public class Solution { + private void countingSort(int[] arr) { + HashMap count = new HashMap<>(); + int minVal = arr[0], maxVal = arr[0]; + + for (int i = 0; i < arr.length; i++) { + minVal = Math.min(minVal, arr[i]); + maxVal = Math.max(maxVal, arr[i]); + count.put(arr[i], count.getOrDefault(arr[i], 0) + 1); + } + + int index = 0; + for (int val = minVal; val <= maxVal; ++val) { + while (count.getOrDefault(val, 0) > 0) { + arr[index] = val; + index += 1; + count.put(val, count.get(val) - 1); + } + } + } + + public int[] sortArray(int[] nums) { + countingSort(nums); + return nums; + } +} +``` + +```cpp +class Solution { +private: + void countingSort(vector &arr) { + unordered_map count; + int minVal = *min_element(arr.begin(), arr.end()); + int maxVal = *max_element(arr.begin(), arr.end()); + + for (auto& val : arr) { + count[val]++; + } + + int index = 0; + for (int val = minVal; val <= maxVal; ++val) { + while (count[val] > 0) { + arr[index] = val; + index += 1; + count[val] -= 1; + } + } + } + +public: + vector sortArray(vector& nums) { + countingSort(nums); + return nums; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number[]} + */ + sortArray(nums) { + this.countingSort(nums); + return nums; + } + + /** + * @param {number[]} arr + * @return {void} + */ + countingSort(arr) { + let count = new Map(); + let minVal = Math.min(...nums); + let maxVal = Math.max(...nums); + + nums.forEach(val => { + if (!count.has(val)) { + count.set(val, 0); + } + count.set(val, count.get(val) + 1); + }); + + let index = 0; + for (let val = minVal; val <= maxVal; val += 1) { + while (count.get(val) > 0) { + nums[index] = val; + index += 1; + count.set(val, count.get(val) - 1); + } + } + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n + k)$ +* Space complexity: $O(n)$ + +> Where $n$ is the size of the array $nums$ and $k$ is the range between the minimum and maximum values in the array. + +--- + +## 5. Radix Sort + +::tabs-start + +```python +class Solution: + def sortArray(self, nums: List[int]) -> List[int]: + def countSort(arr, n, d): + count = [0] * 10 + for num in arr: + count[(num // d) % 10] += 1 + for i in range(1, 10): + count[i] += count[i - 1] + + res = [0] * n + for i in range(n - 1, -1, -1): + idx = (arr[i] // d) % 10 + res[count[idx] - 1] = arr[i] + count[idx] -= 1 + + for i in range(n): + arr[i] = res[i] + + def radixSort(arr): + n = len(arr) + max_element = max(arr) + d = 1 + while max_element // d > 0: + countSort(arr, n, d) + d *= 10 + + negatives = [-num for num in nums if num < 0] + positives = [num for num in nums if num >= 0] + + if negatives: + radixSort(negatives) + negatives = [-num for num in reversed(negatives)] + + if positives: + radixSort(positives) + + return negatives + positives +``` + +```java +public class Solution { + public int[] sortArray(int[] nums) { + ArrayList negatives = new ArrayList<>(); + ArrayList positives = new ArrayList<>(); + + for (int num : nums) { + if (num < 0) { + negatives.add(-num); + } else { + positives.add(num); + } + } + + if (!negatives.isEmpty()) { + radixSort(negatives); + Collections.reverse(negatives); + for (int i = 0; i < negatives.size(); i++) { + negatives.set(i, -negatives.get(i)); + } + } + + if (!positives.isEmpty()) { + radixSort(positives); + } + + int index = 0; + for (int num : negatives) { + nums[index++] = num; + } + for (int num : positives) { + nums[index++] = num; + } + return nums; + } + + private void countSort(ArrayList arr, int n, int d) { + int[] count = new int[10]; + for (int num : arr) { + count[(num / d) % 10]++; + } + for (int i = 1; i < 10; i++) { + count[i] += count[i - 1]; + } + + ArrayList res = new ArrayList<>(Collections.nCopies(n, 0)); + for (int i = n - 1; i >= 0; i--) { + int idx = (arr.get(i) / d) % 10; + res.set(count[idx] - 1, arr.get(i)); + count[idx]--; + } + + for (int i = 0; i < n; i++) { + arr.set(i, res.get(i)); + } + } + + private void radixSort(ArrayList arr) { + int n = arr.size(); + int maxElement = Collections.max(arr); + int d = 1; + + while (maxElement / d > 0) { + countSort(arr, n, d); + d *= 10; + } + } +} +``` + +```cpp +class Solution { +public: + vector sortArray(vector& nums) { + vector negatives, positives; + + for (int num : nums) { + if (num < 0) { + negatives.push_back(-num); + } else { + positives.push_back(num); + } + } + + if (!negatives.empty()) { + radixSort(negatives); + reverse(negatives.begin(), negatives.end()); + for (int& num : negatives) { + num = -num; + } + } + + if (!positives.empty()) { + radixSort(positives); + } + + int index = 0; + for (int& num : negatives) { + nums[index++] = num; + } + for (int& num : positives) { + nums[index++] = num; + } + return nums; + } + +private: + void countSort(vector& arr, int n, int d) { + vector count(10, 0); + for (int num : arr) { + count[(num / d) % 10]++; + } + for (int i = 1; i < 10; i++) { + count[i] += count[i - 1]; + } + + vector res(n); + for (int i = n - 1; i >= 0; i--) { + int idx = (arr[i] / d) % 10; + res[count[idx] - 1] = arr[i]; + count[idx]--; + } + + for (int i = 0; i < n; i++) { + arr[i] = res[i]; + } + } + + void radixSort(vector& arr) { + int n = arr.size(); + int maxElement = *max_element(arr.begin(), arr.end()); + int d = 1; + + while (maxElement / d > 0) { + countSort(arr, n, d); + d *= 10; + } + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number[]} + */ + sortArray(nums) { + const negatives = nums.filter(num => num < 0).map(num => -num); + const positives = nums.filter(num => num >= 0); + + if (negatives.length > 0) { + this.radixSort(negatives); + negatives.reverse(); + for (let i = 0; i < negatives.length; i++) { + negatives[i] = -negatives[i]; + } + } + + if (positives.length > 0) { + this.radixSort(positives); + } + + return [...negatives, ...positives]; + } + + /** + * @param {number[]} arr + * @return {void} + */ + radixSort(arr) { + const maxElement = Math.max(...arr); + let d = 1; + + while (Math.floor(maxElement / d) > 0) { + this.countSort(arr, d); + d *= 10; + } + } + + /** + * @param {number[]} arr + * @param {number} d + * @return {void} + */ + countSort(arr, d) { + const count = Array(10).fill(0); + for (const num of arr) { + count[Math.floor(num / d) % 10]++; + } + for (let i = 1; i < 10; i++) { + count[i] += count[i - 1]; + } + + const res = Array(arr.length); + for (let i = arr.length - 1; i >= 0; i--) { + const idx = Math.floor(arr[i] / d) % 10; + res[count[idx] - 1] = arr[i]; + count[idx]--; + } + + for (let i = 0; i < arr.length; i++) { + arr[i] = res[i]; + } + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(d * n)$ +* Space complexity: $O(n)$ + +> Where $n$ is the size of the array $nums$ and $d$ is the number of digits in the maximum element of the array. + +--- + +## 6. Shell Sort + +::tabs-start + +```python +class Solution: + def sortArray(self, nums: List[int]) -> List[int]: + def shell_sort(nums, n): + gap = n // 2 + while gap >= 1: + for i in range(gap, n): + tmp = nums[i] + j = i - gap + while j >= 0 and nums[j] > tmp: + nums[j + gap] = nums[j] + j -= gap + nums[j + gap] = tmp + gap //= 2 + + n = len(nums) + if n == 1: + return nums + shell_sort(nums, n) + return nums +``` + +```java +public class Solution { + private void shellSort(int[] nums, int n) { + int gap = n / 2; + while (gap >= 1) { + for (int i = gap; i < n; i++) { + int tmp = nums[i]; + int j = i - gap; + while (j >= 0 && nums[j] > tmp) { + nums[j + gap] = nums[j]; + j -= gap; + } + nums[j + gap] = tmp; + } + gap /= 2; + } + } + + public int[] sortArray(int[] nums) { + int n = nums.length; + if (n == 1) return nums; + + shellSort(nums, n); + return nums; + } +} +``` + +```cpp +class Solution { +private: + void shellSort(vector& nums, int n) { + for (int gap = n / 2; gap >= 1; gap /= 2) { + for (int i = gap; i < n; i++) { + int tmp = nums[i]; + int j = i - gap; + while (j >= 0 && nums[j] > tmp) { + nums[j + gap] = nums[j]; + j -= gap; + } + nums[j + gap] = tmp; + } + } + } + +public: + vector sortArray(vector& nums) { + if (nums.size() == 1) return nums; + shellSort(nums, nums.size()); + return nums; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number[]} + */ + sortArray(nums) { + const n = nums.length; + if (n === 1) return nums; + + const shellSort = () => { + let gap = Math.floor(n / 2); + while (gap >= 1) { + for (let i = gap; i < n; i++) { + let key = nums[i]; + let j = i - gap; + while (j >= 0 && nums[j] > key) { + nums[j + gap] = nums[j]; + j -= gap; + } + nums[j + gap] = key; + } + gap = Math.floor(gap / 2); + } + } + + shellSort(); + return nums; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ in average case, $O(n ^ 2)$ in worst case. +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/sort-colors.md b/articles/sort-colors.md new file mode 100644 index 000000000..c94416b39 --- /dev/null +++ b/articles/sort-colors.md @@ -0,0 +1,437 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def sortColors(self, nums: List[int]) -> None: + """ + Do not return anything, modify nums in-place instead. + """ + nums.sort() +``` + +```java +public class Solution { + public void sortColors(int[] nums) { + Arrays.sort(nums); + } +} +``` + +```cpp +class Solution { +public: + void sortColors(vector& nums) { + sort(nums.begin(), nums.end()); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {void} Do not return anything, modify nums in-place instead. + */ + sortColors(nums) { + nums.sort((a, b) => a - b); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(1)$ or $O(n)$ depending on the sorting algorithm. + +--- + +## 2. Counting Sort + +::tabs-start + +```python +class Solution: + def sortColors(self, nums: List[int]) -> None: + """ + Do not return anything, modify nums in-place instead. + """ + count = [0] * 3 + for num in nums: + count[num] += 1 + + index = 0 + for i in range(3): + while count[i]: + count[i] -= 1 + nums[index] = i + index += 1 +``` + +```java +public class Solution { + public void sortColors(int[] nums) { + int[] count = new int[3]; + for (int num : nums) { + count[num]++; + } + + int index = 0; + for (int i = 0; i < 3; i++) { + while (count[i]-- > 0) { + nums[index++] = i; + } + } + } +} +``` + +```cpp +class Solution { +public: + void sortColors(vector& nums) { + vector count(3); + for (int& num : nums) { + count[num]++; + } + + int index = 0; + for (int i = 0; i < 3; i++) { + while (count[i]-- > 0) { + nums[index++] = i; + } + } + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {void} Do not return anything, modify nums in-place instead. + */ + sortColors(nums) { + const count = new Int32Array(3); + for (let num of nums) { + count[num]++; + } + + let index = 0; + for (let i = 0; i < 3; i++) { + while (count[i]-- > 0) { + nums[index++] = i; + } + } + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ + +--- + +## 3. Three Pointers - I + +::tabs-start + +```python +class Solution: + def sortColors(self, nums: List[int]) -> None: + """ + Do not return anything, modify nums in-place instead. + """ + l, r = 0, len(nums) - 1 + i = 0 + + def swap(i, j): + temp = nums[i] + nums[i] = nums[j] + nums[j] = temp + + while i <= r: + if nums[i] == 0: + swap(l, i) + l += 1 + elif nums[i] == 2: + swap(i, r) + r -= 1 + i -= 1 + i += 1 +``` + +```java +public class Solution { + public void sortColors(int[] nums) { + int i = 0, l = 0, r = nums.length - 1; + while (i <= r) { + if (nums[i] == 0) { + swap(nums, l, i); + l++; + } else if (nums[i] == 2) { + swap(nums, i, r); + r--; + i--; + } + i++; + } + } + + private void swap(int[] nums, int i, int j) { + int temp = nums[i]; + nums[i] = nums[j]; + nums[j] = temp; + } +} +``` + +```cpp +class Solution { +public: + void sortColors(vector& nums) { + int i = 0, l = 0, r = nums.size() - 1; + while (i <= r) { + if (nums[i] == 0) { + swap(nums[l], nums[i]); + l++; + } else if (nums[i] == 2) { + swap(nums[i], nums[r]); + r--; + i--; + } + i++; + } + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {void} Do not return anything, modify nums in-place instead. + */ + sortColors(nums) { + let i = 0, l = 0, r = nums.length - 1; + while (i <= r) { + if (nums[i] == 0) { + [nums[l], nums[i]] = [nums[i], nums[l]]; + l++; + } else if (nums[i] == 2) { + [nums[i], nums[r]] = [nums[r], nums[i]]; + r--; + i--; + } + i++; + } + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ + +--- + +## 4. Three Pointers - II + +::tabs-start + +```python +class Solution: + def sortColors(self, nums: List[int]) -> None: + """ + Do not return anything, modify nums in-place instead. + """ + zero = one = two = 0 + for i in range(len(nums)): + if nums[i] == 0: + nums[two] = 2 + nums[one] = 1 + nums[zero] = 0 + two += 1 + one += 1 + zero += 1 + elif nums[i] == 1: + nums[two] = 2 + nums[one] = 1 + two += 1 + one += 1 + else: + nums[two] = 2 + two += 1 +``` + +```java +public class Solution { + public void sortColors(int[] nums) { + int zero = 0, one = 0, two = 0; + for (int i = 0; i < nums.length; i++) { + if (nums[i] == 0) { + nums[two++] = 2; + nums[one++] = 1; + nums[zero++] = 0; + } else if (nums[i] == 1) { + nums[two++] = 2; + nums[one++] = 1; + } else { + nums[two++] = 2; + } + } + } +} +``` + +```cpp +class Solution { +public: + void sortColors(vector& nums) { + int zero = 0, one = 0, two = 0; + for (int i = 0; i < nums.size(); i++) { + if (nums[i] == 0) { + nums[two++] = 2; + nums[one++] = 1; + nums[zero++] = 0; + } else if (nums[i] == 1) { + nums[two++] = 2; + nums[one++] = 1; + } else { + nums[two++] = 2; + } + } + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {void} Do not return anything, modify nums in-place instead. + */ + sortColors(nums) { + let zero = 0, one = 0, two = 0; + for (let i = 0; i < nums.length; i++) { + if (nums[i] == 0) { + nums[two++] = 2; + nums[one++] = 1; + nums[zero++] = 0; + } else if (nums[i] == 1) { + nums[two++] = 2; + nums[one++] = 1; + } else { + nums[two++] = 2; + } + } + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ + +--- + +## 5. Three Pointers - III + +::tabs-start + +```python +class Solution: + def sortColors(self, nums: List[int]) -> None: + """ + Do not return anything, modify nums in-place instead. + """ + zero = one = 0 + for two in range(len(nums)): + tmp = nums[two] + nums[two] = 2 + if tmp < 2: + nums[one] = 1 + one += 1 + if tmp < 1: + nums[zero] = 0 + zero += 1 +``` + +```java +public class Solution { + public void sortColors(int[] nums) { + int zero = 0, one = 0; + for (int two = 0; two < nums.length; two++) { + int tmp = nums[two]; + nums[two] = 2; + if (tmp < 2) { + nums[one++] = 1; + } + if (tmp < 1) { + nums[zero++] = 0; + } + } + } +} +``` + +```cpp +class Solution { +public: + void sortColors(vector& nums) { + int zero = 0, one = 0; + for (int two = 0; two < nums.size(); two++) { + int tmp = nums[two]; + nums[two] = 2; + if (tmp < 2) { + nums[one++] = 1; + } + if (tmp < 1) { + nums[zero++] = 0; + } + } + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {void} Do not return anything, modify nums in-place instead. + */ + sortColors(nums) { + let zero = 0, one = 0; + for (let two = 0; two < nums.length; two++) { + let tmp = nums[two]; + nums[two] = 2; + if (tmp < 2) { + nums[one++] = 1; + } + if (tmp < 1) { + nums[zero++] = 0; + } + } + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/special-array-with-x-elements-greater-than-or-equal-x.md b/articles/special-array-with-x-elements-greater-than-or-equal-x.md new file mode 100644 index 000000000..3ba445d99 --- /dev/null +++ b/articles/special-array-with-x-elements-greater-than-or-equal-x.md @@ -0,0 +1,506 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def specialArray(self, nums: List[int]) -> int: + for i in range(1, len(nums) + 1): + cnt = 0 + for num in nums: + if num >= i: + cnt += 1 + + if cnt == i: + return i + + return -1 +``` + +```java +public class Solution { + public int specialArray(int[] nums) { + for (int i = 1; i <= nums.length; i++) { + int count = 0; + for (int num : nums) { + if (num >= i) { + count++; + } + } + if (count == i) { + return i; + } + } + return -1; + } +} +``` + +```cpp +class Solution { +public: + int specialArray(vector& nums) { + for (int i = 1; i <= nums.size(); i++) { + int count = 0; + for (int num : nums) { + if (num >= i) { + count++; + } + } + if (count == i) { + return i; + } + } + return -1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + specialArray(nums) { + for (let i = 1; i <= nums.length; i++) { + let count = 0; + for (let num of nums) { + if (num >= i) { + count++; + } + } + if (count === i) { + return i; + } + } + return -1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(1)$ + +--- + +## 2. Binary Search + +::tabs-start + +```python +class Solution: + def specialArray(self, nums: List[int]) -> int: + l, r = 1, len(nums) + while l <= r: + mid = (l + r) >> 1 + cnt = sum(1 for num in nums if num >= mid) + + if cnt == mid: + return mid + + if cnt < mid: + r = mid - 1 + else: + l = mid + 1 + + return -1 +``` + +```java +public class Solution { + public int specialArray(int[] nums) { + int l = 1, r = nums.length; + while (l <= r) { + int mid = (l + r) / 2; + int cnt = 0; + for (int num : nums) { + if (num >= mid) cnt++; + } + + if (cnt == mid) return mid; + + if (cnt < mid) { + r = mid - 1; + } else { + l = mid + 1; + } + } + return -1; + } +} +``` + +```cpp +class Solution { +public: + int specialArray(vector& nums) { + int l = 1, r = nums.size(); + while (l <= r) { + int mid = (l + r) / 2; + int cnt = 0; + for (int num : nums) { + if (num >= mid) cnt++; + } + + if (cnt == mid) return mid; + + if (cnt < mid) { + r = mid - 1; + } else { + l = mid + 1; + } + } + return -1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + specialArray(nums) { + let l = 1, r = nums.length; + while (l <= r) { + const mid = Math.floor((l + r) / 2); + const cnt = nums.filter(num => num >= mid).length; + + if (cnt === mid) return mid; + + if (cnt < mid) { + r = mid - 1; + } else { + l = mid + 1; + } + } + return -1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(1)$ + +--- + +## 3. Sorting + +::tabs-start + +```python +class Solution: + def specialArray(self, nums: List[int]) -> int: + nums.sort() + i = 0 + prev = -1 + total_right = len(nums) + while i < len(nums): + if nums[i] == total_right or (prev < total_right < nums[i]): + return total_right + + while i + 1 < len(nums) and nums[i] == nums[i + 1]: + i += 1 + + prev = nums[i] + i += 1 + total_right = len(nums) - i + + return -1 +``` + +```java +public class Solution { + public int specialArray(int[] nums) { + Arrays.sort(nums); + int i = 0, prev = -1, totalRight = nums.length; + + while (i < nums.length) { + if (nums[i] == totalRight || + (prev < totalRight && totalRight < nums[i])) { + return totalRight; + } + + while (i + 1 < nums.length && nums[i] == nums[i + 1]) { + i++; + } + + prev = nums[i]; + i++; + totalRight = nums.length - i; + } + + return -1; + } +} +``` + +```cpp +class Solution { +public: + int specialArray(vector& nums) { + sort(nums.begin(), nums.end()); + int i = 0, prev = -1, totalRight = nums.size(); + + while (i < nums.size()) { + if (nums[i] == totalRight || + (prev < totalRight && totalRight < nums[i])) { + return totalRight; + } + + while (i + 1 < nums.size() && nums[i] == nums[i + 1]) { + i++; + } + + prev = nums[i]; + i++; + totalRight = nums.size() - i; + } + + return -1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + specialArray(nums) { + nums.sort((a, b) => a - b); + let i = 0, prev = -1, totalRight = nums.length; + + while (i < nums.length) { + if (nums[i] === totalRight || + (prev < totalRight && totalRight < nums[i])) { + return totalRight; + } + + while (i + 1 < nums.length && nums[i] === nums[i + 1]) { + i++; + } + + prev = nums[i]; + i++; + totalRight = nums.length - i; + } + + return -1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(1)$ or $O(n)$ depending on the sorting algorithm. + +--- + +## 4. Sorting + Two Pointers + +::tabs-start + +```python +class Solution: + def specialArray(self, nums: List[int]) -> int: + nums.sort() + n = len(nums) + i, j = 0, 1 + + while i < n and j <= n: + while i < n and j > nums[i]: + i += 1 + + if j == n - i: + return j + j += 1 + + return -1 +``` + +```java +public class Solution { + public int specialArray(int[] nums) { + Arrays.sort(nums); + int n = nums.length; + int i = 0, j = 1; + + while (i < n && j <= n) { + while (i < n && j > nums[i]) i++; + + if (j == n - i) { + return j; + } + j++; + } + + return -1; + } +} +``` + +```cpp +class Solution { +public: + int specialArray(vector& nums) { + sort(nums.begin(), nums.end()); + int n = nums.size(); + int i = 0, j = 1; + + while (i < n && j <= n) { + while (i < n && j > nums[i]) i++; + + if (j == n - i) { + return j; + } + j++; + } + + return -1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + specialArray(nums) { + nums.sort((a, b) => a - b); + const n = nums.length; + let i = 0, j = 1; + + while (i < n && j <= n) { + while (i < n && j > nums[i]) i++; + + if (j == n - i) { + return j; + } + j++; + } + + return -1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(1)$ or $O(n)$ depending on the sorting algorithm. + +--- + +## 5. Counting Sort + +::tabs-start + +```python +class Solution: + def specialArray(self, nums: List[int]) -> int: + count = [0] * (len(nums) + 1) + for num in nums: + index = min(num, len(nums)) + count[index] += 1 + + total_right = 0 + for i in range(len(nums), -1, -1): + total_right += count[i] + if i == total_right: + return total_right + return -1 +``` + +```java +public class Solution { + public int specialArray(int[] nums) { + int[] count = new int[nums.length + 1]; + for (int num : nums) { + int index = Math.min(num, nums.length); + count[index]++; + } + + int totalRight = 0; + for (int i = nums.length; i >= 0; i--) { + totalRight += count[i]; + if (i == totalRight) { + return totalRight; + } + } + return -1; + } +} +``` + +```cpp +class Solution { +public: + int specialArray(vector& nums) { + vector count(nums.size() + 1, 0); + for (int num : nums) { + int index = min(num, (int)nums.size()); + count[index]++; + } + + int totalRight = 0; + for (int i = nums.size(); i >= 0; --i) { + totalRight += count[i]; + if (i == totalRight) { + return totalRight; + } + } + return -1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + specialArray(nums) { + const count = new Array(nums.length + 1).fill(0); + for (const num of nums) { + const index = Math.min(num, nums.length); + count[index]++; + } + + let totalRight = 0; + for (let i = nums.length; i >= 0; i--) { + totalRight += count[i]; + if (i === totalRight) { + return totalRight; + } + } + return -1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/subarray-sum-equals-k.md b/articles/subarray-sum-equals-k.md new file mode 100644 index 000000000..5afa95bd3 --- /dev/null +++ b/articles/subarray-sum-equals-k.md @@ -0,0 +1,169 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def subarraySum(self, nums: List[int], k: int) -> int: + res = 0 + for i in range(len(nums)): + sum = 0 + for j in range(i, len(nums)): + sum += nums[j] + if sum == k: + res += 1 + return res +``` + +```java +public class Solution { + public int subarraySum(int[] nums, int k) { + int res = 0; + for (int i = 0; i < nums.length; i++) { + int sum = 0; + for (int j = i; j < nums.length; j++) { + sum += nums[j]; + if (sum == k) res++; + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int subarraySum(vector& nums, int k) { + int res = 0; + for (int i = 0; i < nums.size(); i++) { + int sum = 0; + for (int j = i; j < nums.size(); j++) { + sum += nums[j]; + if (sum == k) res++; + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {number} + */ + subarraySum(nums, k) { + let res = 0; + for (let i = 0; i < nums.length; i++) { + let sum = 0; + for (let j = i; j < nums.length; j++) { + sum += nums[j]; + if (sum == k) res++; + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(1)$ + +--- + +## 2. Hash Map + +::tabs-start + +```python +class Solution: + def subarraySum(self, nums: List[int], k: int) -> int: + res = curSum = 0 + prefixSums = { 0 : 1 } + + for num in nums: + curSum += num + diff = curSum - k + + res += prefixSums.get(diff, 0) + prefixSums[curSum] = 1 + prefixSums.get(curSum, 0) + + return res +``` + +```java +public class Solution { + public int subarraySum(int[] nums, int k) { + int res = 0, curSum = 0; + Map prefixSums = new HashMap<>(); + prefixSums.put(0, 1); + + for (int num : nums) { + curSum += num; + int diff = curSum - k; + res += prefixSums.getOrDefault(diff, 0); + prefixSums.put(curSum, prefixSums.getOrDefault(curSum, 0) + 1); + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int subarraySum(vector& nums, int k) { + int res = 0, curSum = 0; + unordered_map prefixSums; + prefixSums[0] = 1; + + for (int num : nums) { + curSum += num; + int diff = curSum - k; + res += prefixSums[diff]; + prefixSums[curSum]++; + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {number} + */ + subarraySum(nums, k) { + let res = 0, curSum = 0; + const prefixSums = new Map(); + prefixSums.set(0, 1); + + for (let num of nums) { + curSum += num; + let diff = curSum - k; + res += prefixSums.get(diff) || 0; + prefixSums.set(curSum, (prefixSums.get(curSum) || 0) + 1); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/time-needed-to-buy-tickets.md b/articles/time-needed-to-buy-tickets.md new file mode 100644 index 000000000..b7422f54c --- /dev/null +++ b/articles/time-needed-to-buy-tickets.md @@ -0,0 +1,329 @@ +## 1. Queue + +::tabs-start + +```python +class Solution: + def timeRequiredToBuy(self, tickets: List[int], k: int) -> int: + n = len(tickets) + q = deque() + + for i in range(n): + q.append(i) + + time = 0 + while q: + time += 1 + cur = q.popleft() + tickets[cur] -= 1 + if tickets[cur] == 0: + if cur == k: + return time + else: + q.append(cur) + return time +``` + +```java +public class Solution { + public int timeRequiredToBuy(int[] tickets, int k) { + int n = tickets.length; + Queue queue = new LinkedList<>(); + + for (int i = 0; i < n; i++) { + queue.add(i); + } + + int time = 0; + while (!queue.isEmpty()) { + time++; + int cur = queue.poll(); + tickets[cur]--; + if (tickets[cur] == 0) { + if (cur == k) { + return time; + } + } else { + queue.add(cur); + } + } + return time; + } +} +``` + +```cpp +class Solution { +public: + int timeRequiredToBuy(vector& tickets, int k) { + int n = tickets.size(); + queue q; + + for (int i = 0; i < n; i++) { + q.push(i); + } + + int time = 0; + while (!q.empty()) { + time++; + int cur = q.front(); + q.pop(); + tickets[cur]--; + if (tickets[cur] == 0) { + if (cur == k) { + return time; + } + } else { + q.push(cur); + } + } + return time; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} tickets + * @param {number} k + * @return {number} + */ + timeRequiredToBuy(tickets, k) { + let n = tickets.length; + let queue = new Queue(); + + for (let i = 0; i < n; i++) { + queue.push(i); + } + + let time = 0; + while (queue.size() > 0) { + time++; + let cur = queue.pop(); + tickets[cur]--; + if (tickets[cur] === 0) { + if (cur === k) { + return time; + } + } else { + queue.push(cur); + } + } + return time; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * m)$ +* Space complexity: $O(n)$ + +> Where $n$ is the size of the input array and $m$ is the maximum value in the input array. + +--- + +## 2. Iteration + +::tabs-start + +```python +class Solution: + def timeRequiredToBuy(self, tickets: List[int], k: int) -> int: + n = len(tickets) + idx = 0 + + time = 0 + while True: + time += 1 + tickets[idx] -= 1 + if tickets[idx] == 0: + if idx == k: + return time + idx = (idx + 1) % n + while tickets[idx] == 0: + idx = (idx + 1) % n + + return time +``` + +```java +public class Solution { + public int timeRequiredToBuy(int[] tickets, int k) { + int n = tickets.length; + int idx = 0; + + int time = 0; + while (true) { + time++; + tickets[idx]--; + if (tickets[idx] == 0) { + if (idx == k) { + return time; + } + } + idx = (idx + 1) % n; + while (tickets[idx] == 0) { + idx = (idx + 1) % n; + } + } + } +} +``` + +```cpp +class Solution { +public: + int timeRequiredToBuy(vector& tickets, int k) { + int n = tickets.size(); + int idx = 0; + + int time = 0; + while (true) { + time++; + tickets[idx]--; + if (tickets[idx] == 0) { + if (idx == k) { + return time; + } + } + idx = (idx + 1) % n; + while (tickets[idx] == 0) { + idx = (idx + 1) % n; + } + } + + return time; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} tickets + * @param {number} k + * @return {number} + */ + timeRequiredToBuy(tickets, k) { + let n = tickets.length; + let idx = 0; + + let time = 0; + while (true) { + time++; + tickets[idx]--; + if (tickets[idx] === 0) { + if (idx === k) { + return time; + } + } + idx = (idx + 1) % n; + while (tickets[idx] === 0) { + idx = (idx + 1) % n; + } + } + + return time; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * m)$ +* Space complexity: $O(1)$ + +> Where $n$ is the size of the input array and $m$ is the maximum value in the input array. + +--- + +## 3. Iteration (One Pass) + +::tabs-start + +```python +class Solution: + def timeRequiredToBuy(self, tickets: List[int], k: int) -> int: + res = 0 + + for i in range(len(tickets)): + if i <= k: + res += min(tickets[i], tickets[k]) + else: + res += min(tickets[i], tickets[k] - 1) + + return res +``` + +```java +public class Solution { + public int timeRequiredToBuy(int[] tickets, int k) { + int res = 0; + + for (int i = 0; i < tickets.length; i++) { + if (i <= k) { + res += Math.min(tickets[i], tickets[k]); + } else { + res += Math.min(tickets[i], tickets[k] - 1); + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int timeRequiredToBuy(vector& tickets, int k) { + int res = 0; + + for (int i = 0; i < tickets.size(); i++) { + if (i <= k) { + res += min(tickets[i], tickets[k]); + } else { + res += min(tickets[i], tickets[k] - 1); + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} tickets + * @param {number} k + * @return {number} + */ + timeRequiredToBuy(tickets, k) { + let res = 0; + + for (let i = 0; i < tickets.length; i++) { + if (i <= k) { + res += Math.min(tickets[i], tickets[k]); + } else { + res += Math.min(tickets[i], tickets[k] - 1); + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/unique-length-3-palindromic-subsequences.md b/articles/unique-length-3-palindromic-subsequences.md new file mode 100644 index 000000000..642af0a41 --- /dev/null +++ b/articles/unique-length-3-palindromic-subsequences.md @@ -0,0 +1,863 @@ +## 1. Brute Force (Recursion) + +::tabs-start + +```python +class Solution: + def countPalindromicSubsequence(self, s: str) -> int: + res = set() + + def rec(i, cur): + if len(cur) == 3: + if cur[0] == cur[2]: + res.add(cur) + return + if i == len(s): + return + rec(i + 1, cur) + rec(i + 1, cur + s[i]) + + rec(0, "") + return len(res) +``` + +```java +public class Solution { + public int countPalindromicSubsequence(String s) { + Set res = new HashSet<>(); + rec(s, 0, "", res); + return res.size(); + } + + private void rec(String s, int i, String cur, Set res) { + if (cur.length() == 3) { + if (cur.charAt(0) == cur.charAt(2)) { + res.add(cur); + } + return; + } + if (i == s.length()) { + return; + } + rec(s, i + 1, cur, res); + rec(s, i + 1, cur + s.charAt(i), res); + } +} +``` + +```cpp +class Solution { +public: + int countPalindromicSubsequence(string s) { + unordered_set res; + rec(s, 0, "", res); + return res.size(); + } + +private: + void rec(const string& s, int i, string cur, unordered_set& res) { + if (cur.length() == 3) { + if (cur[0] == cur[2]) { + res.insert(cur); + } + return; + } + if (i == s.length()) { + return; + } + rec(s, i + 1, cur, res); + rec(s, i + 1, cur + s[i], res); + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {number} + */ + countPalindromicSubsequence(s) { + const res = new Set(); + + const rec = (i, cur) => { + if (cur.length === 3) { + if (cur[0] === cur[2]) { + res.add(cur); + } + return; + } + if (i === s.length) { + return; + } + rec(i + 1, cur); + rec(i + 1, cur + s[i]); + }; + + rec(0, ""); + return res.size; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(2 ^ n)$ +* Space complexity: $O(n + m)$ + +> Where $n$ is the length of the string $s$ and $m$ is the number of unique three length pallindromic subsequences (26 * 26 = 676). + +--- + +## 2. Brute Force + +::tabs-start + +```python +class Solution: + def countPalindromicSubsequence(self, s: str) -> int: + res = set() + + for i in range(len(s) - 2): + for j in range(i + 1, len(s) - 1): + for k in range(j + 1, len(s)): + if s[i] != s[k]: + continue + res.add(s[i] + s[j] + s[k]) + return len(res) +``` + +```java +public class Solution { + public int countPalindromicSubsequence(String s) { + Set res = new HashSet<>(); + + for (int i = 0; i < s.length() - 2; i++) { + for (int j = i + 1; j < s.length() - 1; j++) { + for (int k = j + 1; k < s.length(); k++) { + if (s.charAt(i) != s.charAt(k)) { + continue; + } + res.add("" + s.charAt(i) + s.charAt(j) + s.charAt(k)); + } + } + } + return res.size(); + } +} +``` + +```cpp +class Solution { +public: + int countPalindromicSubsequence(string s) { + unordered_set res; + + for (int i = 0; i < s.length() - 2; i++) { + for (int j = i + 1; j < s.length() - 1; j++) { + for (int k = j + 1; k < s.length(); k++) { + if (s[i] != s[k]) { + continue; + } + res.insert(string() + s[i] + s[j] + s[k]); + } + } + } + return res.size(); + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {number} + */ + countPalindromicSubsequence(s) { + const res = new Set(); + + for (let i = 0; i < s.length - 2; i++) { + for (let j = i + 1; j < s.length - 1; j++) { + for (let k = j + 1; k < s.length; k++) { + if (s[i] !== s[k]) { + continue; + } + res.add(s[i] + s[j] + s[k]); + } + } + } + return res.size; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 3)$ +* Space complexity: $O(m)$ + +> Where $n$ is the length of the string $s$ and $m$ is the number of unique three length pallindromic subsequences (26 * 26 = 676). + +--- + +## 3. Sequential Matching for Each Pallindrome + +::tabs-start + +```python +class Solution: + def countPalindromicSubsequence(self, s: str) -> int: + res = 0 + for ends in range(ord('a'), ord('z') + 1): + for mid in range(ord('a'), ord('z') + 1): + seq = chr(ends) + chr(mid) + chr(ends) + idx, found = 0, 0 + for c in s: + if seq[idx] == c: + idx += 1 + if idx == 3: + found = 1 + break + res += found + return res +``` + +```java +public class Solution { + public int countPalindromicSubsequence(String s) { + int res = 0; + for (char ends = 'a'; ends <= 'z'; ends++) { + for (char mid = 'a'; mid <= 'z'; mid++) { + String seq = "" + ends + mid + ends; + int idx = 0, found = 0; + for (char c : s.toCharArray()) { + if (seq.charAt(idx) == c) { + idx++; + if (idx == 3) { + found = 1; + break; + } + } + } + res += found; + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int countPalindromicSubsequence(string s) { + int res = 0; + for (char ends = 'a'; ends <= 'z'; ends++) { + for (char mid = 'a'; mid <= 'z'; mid++) { + string seq = string() + ends + mid + ends; + int idx = 0, found = 0; + for (char& c : s) { + if (seq[idx] == c) { + idx++; + if (idx == 3) { + found = 1; + break; + } + } + } + res += found; + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {number} + */ + countPalindromicSubsequence(s) { + let res = 0; + for (let ends = 'a'.charCodeAt(0); ends <= 'z'.charCodeAt(0); ends++) { + for (let mid = 'a'.charCodeAt(0); mid <= 'z'.charCodeAt(0); mid++) { + const seq = String.fromCharCode(ends) + + String.fromCharCode(mid) + + String.fromCharCode(ends); + let idx = 0, found = 0; + for (const c of s) { + if (seq[idx] === c) { + idx++; + if (idx === 3) { + found = 1; + break; + } + } + } + res += found; + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * m)$ +* Space complexity: $O(1)$ + +> Where $n$ is the length of the string $s$ and $m$ is the number of unique three length pallindromic subsequences (26 * 26 = 676). + +--- + +## 4. Iterate On Middle Characters + +::tabs-start + +```python +class Solution: + def countPalindromicSubsequence(self, s: str) -> int: + res = set() + left = set() + right = collections.Counter(s) + + for i in range(len(s)): + right[s[i]] -= 1 + if right[s[i]] == 0: + right.pop(s[i]) + + for j in range(26): + c = chr(ord('a') + j) + if c in left and c in right: + res.add((s[i], c)) + left.add(s[i]) + + return len(res) +``` + +```java +public class Solution { + public int countPalindromicSubsequence(String s) { + Set res = new HashSet<>(); + Set left = new HashSet<>(); + int[] right = new int[26]; + + for (char c : s.toCharArray()) { + right[c - 'a']++; + } + + for (int i = 0; i < s.length(); i++) { + right[s.charAt(i) - 'a']--; + if (right[s.charAt(i) - 'a'] == 0) { + right[s.charAt(i) - 'a'] = -1; + } + + for (int j = 0; j < 26; j++) { + char c = (char) (j + 'a'); + if (left.contains(c) && right[j] > 0) { + res.add("" + s.charAt(i) + c); + } + } + left.add(s.charAt(i)); + } + + return res.size(); + } +} +``` + +```cpp +class Solution { +public: + int countPalindromicSubsequence(string s) { + unordered_set res; + unordered_set left; + vector right(26, 0); + + for (char c : s) { + right[c - 'a']++; + } + + for (int i = 0; i < s.length(); i++) { + right[s[i] - 'a']--; + if (right[s[i] - 'a'] == 0) { + right[s[i] - 'a'] = -1; + } + + for (int j = 0; j < 26; j++) { + char c = 'a' + j; + if (left.count(c) && right[j] > 0) { + res.insert(string() + s[i] + c); + } + } + left.insert(s[i]); + } + + return res.size(); + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {number} + */ + countPalindromicSubsequence(s) { + const res = new Set(); + const left = new Set(); + const right = Array(26).fill(0); + + for (const c of s) { + right[c.charCodeAt(0) - 'a'.charCodeAt(0)]++; + } + + for (let i = 0; i < s.length; i++) { + right[s.charCodeAt(i) - 'a'.charCodeAt(0)]--; + if (right[s.charCodeAt(i) - 'a'.charCodeAt(0)] === 0) { + right[s.charCodeAt(i) - 'a'.charCodeAt(0)] = -1; + } + + for (let j = 0; j < 26; j++) { + const c = String.fromCharCode('a'.charCodeAt(0) + j); + if (left.has(c) && right[j] > 0) { + res.add(s[i] + c); + } + } + left.add(s[i]); + } + + return res.size; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(26 * n)$ +* Space complexity: $O(m)$ + +> Where $n$ is the length of the string $s$ and $m$ is the number of unique three length pallindromic subsequences (26 * 26 = 676). + +--- + +## 5. Prefix Count + +::tabs-start + +```python +class Solution: + def countPalindromicSubsequence(self, s: str) -> int: + n = len(s) + prefix = [[0] * 26 for _ in range(n + 1)] + firstIndex = [-1] * 26 + lastIndex = [-1] * 26 + + for i in range(n): + j = ord(s[i]) - ord('a') + if firstIndex[j] == -1: + firstIndex[j] = i + lastIndex[j] = i + prefix[i + 1] = prefix[i][:] + prefix[i + 1][j] += 1 + + res = 0 + for ends in range(26): + if firstIndex[ends] == -1 or firstIndex[ends] == lastIndex[ends]: + continue + l, r = firstIndex[ends], lastIndex[ends] + for mid in range(26): + if prefix[r][mid] - prefix[l + 1][mid] > 0: + res += 1 + return res +``` + +```java +public class Solution { + public int countPalindromicSubsequence(String s) { + int n = s.length(); + int[][] prefix = new int[n + 1][26]; + int[] firstIndex = new int[26]; + int[] lastIndex = new int[26]; + Arrays.fill(firstIndex, -1); + Arrays.fill(lastIndex, -1); + + for (int i = 0; i < n; i++) { + int j = s.charAt(i) - 'a'; + if (firstIndex[j] == -1) { + firstIndex[j] = i; + } + lastIndex[j] = i; + for (int k = 0; k < 26; k++) { + prefix[i + 1][k] = prefix[i][k]; + } + prefix[i + 1][j]++; + } + + int res = 0; + for (int ends = 0; ends < 26; ends++) { + if (firstIndex[ends] == -1 || firstIndex[ends] == lastIndex[ends]) { + continue; + } + int l = firstIndex[ends], r = lastIndex[ends]; + for (int mid = 0; mid < 26; mid++) { + if (prefix[r][mid] - prefix[l + 1][mid] > 0) { + res++; + } + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int countPalindromicSubsequence(string s) { + int n = s.length(); + vector> prefix(n + 1, vector(26)); + vector firstIndex(26, -1); + vector lastIndex(26, -1); + + for (int i = 0; i < n; i++) { + int j = s[i] - 'a'; + if (firstIndex[j] == -1) { + firstIndex[j] = i; + } + lastIndex[j] = i; + prefix[i + 1] = prefix[i]; + prefix[i + 1][j]++; + } + + int res = 0; + for (int ends = 0; ends < 26; ends++) { + if (firstIndex[ends] == -1 || firstIndex[ends] == lastIndex[ends]) { + continue; + } + int l = firstIndex[ends], r = lastIndex[ends]; + for (int mid = 0; mid < 26; mid++) { + if (prefix[r][mid] - prefix[l + 1][mid] > 0) { + res++; + } + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {number} + */ + countPalindromicSubsequence(s) { + const n = s.length; + const prefix = Array.from({ length: n + 1 }, () => Array(26).fill(0)); + const firstIndex = Array(26).fill(-1); + const lastIndex = Array(26).fill(-1); + + for (let i = 0; i < n; i++) { + const j = s.charCodeAt(i) - 'a'.charCodeAt(0); + if (firstIndex[j] === -1) { + firstIndex[j] = i; + } + lastIndex[j] = i; + for (let k = 0; k < 26; k++) { + prefix[i + 1][k] = prefix[i][k]; + } + prefix[i + 1][j]++; + } + + let res = 0; + for (let ends = 0; ends < 26; ends++) { + if (firstIndex[ends] === -1 || firstIndex[ends] === lastIndex[ends]) { + continue; + } + const l = firstIndex[ends], r = lastIndex[ends]; + for (let mid = 0; mid < 26; mid++) { + if (prefix[r][mid] - prefix[l + 1][mid] > 0) { + res++; + } + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(26 * n)$ +* Space complexity: $O(26 * n)$ + +--- + +## 6. First And Last Index + +::tabs-start + +```python +class Solution: + def countPalindromicSubsequence(self, s: str) -> int: + res = 0 + + for i in range(26): + c = chr(ord('a') + i) + l, r = s.find(c), s.rfind(c) + if l == -1 or l == r: + continue + + mids = set() + for j in range(l + 1, r): + mids.add(s[j]) + res += len(mids) + + return res +``` + +```java +public class Solution { + public int countPalindromicSubsequence(String s) { + int res = 0; + + for (char c = 'a'; c <= 'z'; c++) { + int l = s.indexOf(c), r = s.lastIndexOf(c); + if (l == -1 || l == r) continue; + + Set mids = new HashSet<>(); + for (int j = l + 1; j < r; j++) { + mids.add(s.charAt(j)); + } + res += mids.size(); + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int countPalindromicSubsequence(string s) { + int res = 0; + + for (char c = 'a'; c <= 'z'; c++) { + int l = s.find(c), r = s.rfind(c); + if (l == -1 || l == r) continue; + + unordered_set mids; + for (int j = l + 1; j < r; j++) { + mids.insert(s[j]); + } + res += mids.size(); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {number} + */ + countPalindromicSubsequence(s) { + let res = 0; + + for (let i = 0; i < 26; i++) { + const c = String.fromCharCode('a'.charCodeAt(0) + i); + const l = s.indexOf(c), r = s.lastIndexOf(c); + if (l === -1 || l === r) continue; + + const mids = new Set(); + for (let j = l + 1; j < r; j++) { + mids.add(s[j]); + } + res += mids.size; + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(26 * n)$ +* Space complexity: $O(1)$ since we have at most $26$ different characters. + +--- + +## 7. First And Last Index (Optimal) + +::tabs-start + +```python +class Solution: + def countPalindromicSubsequence(self, s: str) -> int: + firstIndex = [-1] * 26 + lastIndex = [-1] * 26 + + for i in range(len(s)): + j = ord(s[i]) - ord('a') + if firstIndex[j] == -1: + firstIndex[j] = i + lastIndex[j] = i + + res = 0 + for ends in range(26): + if firstIndex[ends] == -1 or firstIndex[ends] == lastIndex[ends]: + continue + l, r = firstIndex[ends], lastIndex[ends] + mask = 0 + for i in range(l + 1, r): + c = ord(s[i]) - ord('a') + if mask & (1 << c): + continue + mask |= (1 << c) + res += 1 + + return res +``` + +```java +public class Solution { + public int countPalindromicSubsequence(String s) { + int[] firstIndex = new int[26]; + int[] lastIndex = new int[26]; + Arrays.fill(firstIndex, -1); + Arrays.fill(lastIndex, -1); + + for (int i = 0; i < s.length(); i++) { + int j = s.charAt(i) - 'a'; + if (firstIndex[j] == -1) { + firstIndex[j] = i; + } + lastIndex[j] = i; + } + + int res = 0; + for (int ends = 0; ends < 26; ends++) { + if (firstIndex[ends] == -1 || firstIndex[ends] == lastIndex[ends]) { + continue; + } + int l = firstIndex[ends], r = lastIndex[ends]; + int mask = 0; + for (int i = l + 1; i < r; i++) { + int c = s.charAt(i) - 'a'; + if ((mask & (1 << c)) != 0) { + continue; + } + mask |= (1 << c); + res++; + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int countPalindromicSubsequence(string s) { + vector firstIndex(26, -1); + vector lastIndex(26, -1); + + for (int i = 0; i < s.size(); i++) { + int j = s[i] - 'a'; + if (firstIndex[j] == -1) { + firstIndex[j] = i; + } + lastIndex[j] = i; + } + + int res = 0; + for (int ends = 0; ends < 26; ends++) { + if (firstIndex[ends] == -1 || firstIndex[ends] == lastIndex[ends]) { + continue; + } + int l = firstIndex[ends], r = lastIndex[ends]; + int mask = 0; + for (int i = l + 1; i < r; i++) { + int c = s[i] - 'a'; + if (mask & (1 << c)) { + continue; + } + mask |= (1 << c); + res++; + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {number} + */ + countPalindromicSubsequence(s) { + const firstIndex = Array(26).fill(-1); + const lastIndex = Array(26).fill(-1); + + for (let i = 0; i < s.length; i++) { + const j = s.charCodeAt(i) - 'a'.charCodeAt(0); + if (firstIndex[j] === -1) { + firstIndex[j] = i; + } + lastIndex[j] = i; + } + + let res = 0; + for (let ends = 0; ends < 26; ends++) { + if (firstIndex[ends] === -1 || firstIndex[ends] === lastIndex[ends]) { + continue; + } + const l = firstIndex[ends], r = lastIndex[ends]; + let mask = 0; + for (let i = l + 1; i < r; i++) { + const c = s.charCodeAt(i) - 'a'.charCodeAt(0); + if (mask & (1 << c)) { + continue; + } + mask |= (1 << c); + res++; + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(26 * n)$ +* Space complexity: $O(1)$ since we have at most $26$ different characters. \ No newline at end of file diff --git a/articles/word-pattern.md b/articles/word-pattern.md new file mode 100644 index 000000000..c09b50aa1 --- /dev/null +++ b/articles/word-pattern.md @@ -0,0 +1,533 @@ +## 1. Two Hash Maps + +::tabs-start + +```python +class Solution: + def wordPattern(self, pattern: str, s: str) -> bool: + words = s.split(" ") + if len(pattern) != len(words): + return False + + charToWord = {} + wordToChar = {} + + for c, w in zip(pattern, words): + if c in charToWord and charToWord[c] != w: + return False + if w in wordToChar and wordToChar[w] != c: + return False + charToWord[c] = w + wordToChar[w] = c + return True +``` + +```java +public class Solution { + public boolean wordPattern(String pattern, String s) { + String[] words = s.split(" "); + if (pattern.length() != words.length) { + return false; + } + + Map charToWord = new HashMap<>(); + Map wordToChar = new HashMap<>(); + + for (int i = 0; i < pattern.length(); i++) { + char c = pattern.charAt(i); + String w = words[i]; + + if (charToWord.containsKey(c) && !charToWord.get(c).equals(w)) { + return false; + } + if (wordToChar.containsKey(w) && wordToChar.get(w) != c) { + return false; + } + + charToWord.put(c, w); + wordToChar.put(w, c); + } + return true; + } +} +``` + +```cpp +class Solution { +public: + bool wordPattern(string pattern, string s) { + vector words; + string word; + stringstream ss(s); + while (ss >> word) { + words.push_back(word); + } + + if (pattern.length() != words.size()) { + return false; + } + + unordered_map charToWord; + unordered_map wordToChar; + + for (int i = 0; i < pattern.length(); i++) { + char c = pattern[i]; + string& w = words[i]; + + if (charToWord.count(c) && charToWord[c] != w) { + return false; + } + if (wordToChar.count(w) && wordToChar[w] != c) { + return false; + } + + charToWord[c] = w; + wordToChar[w] = c; + } + return true; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} pattern + * @param {string} s + * @return {boolean} + */ + wordPattern(pattern, s) { + const words = s.split(" "); + if (pattern.length !== words.length) { + return false; + } + + const charToWord = new Map(); + const wordToChar = new Map(); + + for (let i = 0; i < pattern.length; i++) { + const c = pattern[i]; + const w = words[i]; + + if (charToWord.has(c) && charToWord.get(c) !== w) { + return false; + } + if (wordToChar.has(w) && wordToChar.get(w) !== c) { + return false; + } + + charToWord.set(c, w); + wordToChar.set(w, c); + } + return true; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n + m)$ +* Space complexity: $O(m)$ + +> Where $n$ is the length of the string $pattern$ and $m$ is the length of the string $s$. + +--- + +## 2. Two Hash Maps (Optimal) + +::tabs-start + +```python +class Solution: + def wordPattern(self, pattern: str, s: str) -> bool: + charToWord = {} + wordToChar = {} + words = s.split() + + if len(pattern) != len(words): + return False + + for i, (c, word) in enumerate(zip(pattern, words)): + if charToWord.get(c, 0) != wordToChar.get(word, 0): + return False + charToWord[c] = i + 1 + wordToChar[word] = i + 1 + + return True +``` + +```java +public class Solution { + public boolean wordPattern(String pattern, String s) { + Map charToWord = new HashMap<>(); + Map wordToChar = new HashMap<>(); + String[] words = s.split(" "); + + if (words.length != pattern.length()) return false; + + for (int i = 0; i < pattern.length(); i++) { + if (charToWord.containsKey(pattern.charAt(i)) && + !words[charToWord.get(pattern.charAt(i))].equals(words[i])) { + return false; + } + + if (wordToChar.containsKey(words[i]) && + pattern.charAt(wordToChar.get(words[i])) != pattern.charAt(i)) { + return false; + } + + charToWord.put(pattern.charAt(i), i); + wordToChar.put(words[i], i); + } + + return true; + } +} +``` + +```cpp +class Solution { +public: + bool wordPattern(string pattern, string s) { + unordered_map charToWord; + unordered_map wordToChar; + istringstream in(s); + int i = 0, n = pattern.size(); + for (string word; in >> word; ++i) { + if (i == n || charToWord[pattern[i]] != wordToChar[word]) { + return false; + } + charToWord[pattern[i]] = wordToChar[word] = i + 1; + } + return i == n; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} pattern + * @param {string} s + * @return {boolean} + */ + wordPattern(pattern, s) { + const charToWord = new Map(); + const wordToChar = new Map(); + const words = s.split(" "); + + if (pattern.length !== words.length) { + return false; + } + + for (let i = 0; i < words.length; i++) { + const c = pattern[i]; + const word = words[i]; + + if ((charToWord.get(c) || 0) !== (wordToChar.get(word) || 0)) { + return false; + } + + charToWord.set(c, i + 1); + wordToChar.set(word, i + 1); + } + + return true; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n + m)$ +* Space complexity: $O(m)$ + +> Where $n$ is the length of the string $pattern$ and $m$ is the length of the string $s$. + +--- + +## 3. Hash Set + +::tabs-start + +```python +class Solution: + def wordPattern(self, pattern: str, s: str) -> bool: + words = s.split() + if len(pattern) != len(words): + return False + + charToWord = {} + store = set() + + for i, (c, w) in enumerate(zip(pattern, words)): + if c in charToWord: + if words[charToWord[c]] != w: + return False + else: + if w in store: + return False + charToWord[c] = i + store.add(w) + + return True +``` + +```java +public class Solution { + public boolean wordPattern(String pattern, String s) { + String[] words = s.split(" "); + if (pattern.length() != words.length) return false; + + Map charToWord = new HashMap<>(); + Set store = new HashSet<>(); + + for (int i = 0; i < pattern.length(); i++) { + char c = pattern.charAt(i); + + if (charToWord.containsKey(c)) { + if (!words[charToWord.get(c)].equals(words[i])) { + return false; + } + } else { + if (store.contains(words[i])) { + return false; + } + charToWord.put(c, i); + store.add(words[i]); + } + } + + return true; + } +} +``` + +```cpp +class Solution { +public: + bool wordPattern(string pattern, string s) { + stringstream ss(s); + string word; + vector words; + + while (ss >> word) { + words.push_back(word); + } + + if (pattern.length() != words.size()) return false; + + unordered_map charToWord; + set store; + + for (int i = 0; i < pattern.length(); i++) { + char c = pattern[i]; + + if (charToWord.count(c)) { + if (words[charToWord[c]] != words[i]) { + return false; + } + } else { + if (store.count(words[i])) { + return false; + } + charToWord[c] = i; + store.insert(words[i]); + } + } + + return true; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} pattern + * @param {string} s + * @return {boolean} + */ + wordPattern(pattern, s) { + const charToWord = new Map(); + const wordToChar = new Map(); + const words = s.split(" "); + + if (pattern.length !== words.length) { + return false; + } + + for (let i = 0; i < words.length; i++) { + const c = pattern[i]; + const word = words[i]; + + if ((charToWord.get(c) || 0) !== (wordToChar.get(word) || 0)) { + return false; + } + + charToWord.set(c, i + 1); + wordToChar.set(word, i + 1); + } + + return true; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n + m)$ +* Space complexity: $O(m)$ + +> Where $n$ is the length of the string $pattern$ and $m$ is the length of the string $s$. + +--- + +## 4. Single Hash Map + +::tabs-start + +```python +class Solution: + def wordPattern(self, pattern: str, s: str) -> bool: + words = s.split() + if len(pattern) != len(words): + return False + + charToWord = {} + + for i, (c, w) in enumerate(zip(pattern, words)): + if c in charToWord: + if words[charToWord[c]] != w: + return False + else: + # iterates atmost 26 times (a - z) + for k in charToWord: + if words[charToWord[k]] == w: + return False + charToWord[c] = i + + return True +``` + +```java +public class Solution { + public boolean wordPattern(String pattern, String s) { + String[] words = s.split(" "); + if (pattern.length() != words.length) { + return false; + } + + Map charToWord = new HashMap<>(); + for (int i = 0; i < pattern.length(); i++) { + char c = pattern.charAt(i); + String w = words[i]; + + if (charToWord.containsKey(c)) { + if (!words[charToWord.get(c)].equals(w)) { + return false; + } + } else { + for (Map.Entry entry : charToWord.entrySet()) { + if (words[entry.getValue()].equals(w)) { + return false; + } + } + charToWord.put(c, i); + } + } + return true; + } +} +``` + +```cpp +class Solution { +public: + bool wordPattern(string pattern, string s) { + vector words; + string word; + stringstream ss(s); + while (ss >> word) { + words.push_back(word); + } + + if (pattern.size() != words.size()) { + return false; + } + + unordered_map charToWord; + for (int i = 0; i < pattern.size(); ++i) { + char c = pattern[i]; + const string& w = words[i]; + + if (charToWord.count(c)) { + if (words[charToWord[c]] != w) { + return false; + } + } else { + for (const auto& [key, val] : charToWord) { + if (words[val] == w) { + return false; + } + } + charToWord[c] = i; + } + } + + return true; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} pattern + * @param {string} s + * @return {boolean} + */ + wordPattern(pattern, s) { + const words = s.split(" "); + if (pattern.length !== words.length) { + return false; + } + + const charToWord = new Map(); + for (let i = 0; i < pattern.length; i++) { + const c = pattern[i]; + const w = words[i]; + + if (charToWord.has(c)) { + if (words[charToWord.get(c)] !== w) { + return false; + } + } else { + for (const [key, index] of charToWord.entries()) { + if (words[index] === w) { + return false; + } + } + charToWord.set(c, i); + } + } + + return true; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n + m)$ +* Space complexity: $O(m)$ + +> Where $n$ is the length of the string $pattern$ and $m$ is the length of the string $s$. \ No newline at end of file From f201a57b8286a9bdd4212d12950fdd7d3f14b3f3 Mon Sep 17 00:00:00 2001 From: rey Date: Sat, 7 Dec 2024 09:55:57 -0800 Subject: [PATCH 12/45] clarify space complexity for dfs trees (#3761) --- articles/balanced-binary-tree.md | 6 +++++- articles/binary-tree-diameter.md | 6 +++++- articles/depth-of-binary-tree.md | 6 +++++- articles/same-binary-tree.md | 6 +++++- 4 files changed, 20 insertions(+), 4 deletions(-) diff --git a/articles/balanced-binary-tree.md b/articles/balanced-binary-tree.md index 65a8aece1..c9c407ed0 100644 --- a/articles/balanced-binary-tree.md +++ b/articles/balanced-binary-tree.md @@ -526,7 +526,11 @@ class Solution { ### Time & Space Complexity * Time complexity: $O(n)$ -* Space complexity: $O(n)$ +* Space complexity: $O(h)$ + * Best Case ([balanced tree](https://www.geeksforgeeks.org/balanced-binary-tree/)): $O(log(n))$ + * Worst Case ([degenerate tree](https://www.geeksforgeeks.org/introduction-to-degenerate-binary-tree/)): $O(n)$ + +> Where $n$ is the number of nodes in the tree and $h$ is the height of the tree. --- diff --git a/articles/binary-tree-diameter.md b/articles/binary-tree-diameter.md index d6c7ab0fe..3c73fbe92 100644 --- a/articles/binary-tree-diameter.md +++ b/articles/binary-tree-diameter.md @@ -520,7 +520,11 @@ class Solution { ### Time & Space Complexity * Time complexity: $O(n)$ -* Space complexity: $O(n)$ +* Space complexity: $O(h)$ + * Best Case ([balanced tree](https://www.geeksforgeeks.org/balanced-binary-tree/)): $O(log(n))$ + * Worst Case ([degenerate tree](https://www.geeksforgeeks.org/introduction-to-degenerate-binary-tree/)): $O(n)$ + +> Where $n$ is the number of nodes in the tree and $h$ is the height of the tree. --- diff --git a/articles/depth-of-binary-tree.md b/articles/depth-of-binary-tree.md index d14965dba..efd76b64d 100644 --- a/articles/depth-of-binary-tree.md +++ b/articles/depth-of-binary-tree.md @@ -176,7 +176,11 @@ class Solution { ### Time & Space Complexity * Time complexity: $O(n)$ -* Space complexity: $O(n)$ +* Space complexity: $O(h)$ + * Best Case ([balanced tree](https://www.geeksforgeeks.org/balanced-binary-tree/)): $O(log(n))$ + * Worst Case ([degenerate tree](https://www.geeksforgeeks.org/introduction-to-degenerate-binary-tree/)): $O(n)$ + +> Where $n$ is the number of nodes in the tree and $h$ is the height of the tree. --- diff --git a/articles/same-binary-tree.md b/articles/same-binary-tree.md index 05876d926..6cbfc12aa 100644 --- a/articles/same-binary-tree.md +++ b/articles/same-binary-tree.md @@ -191,7 +191,11 @@ class Solution { ### Time & Space Complexity * Time complexity: $O(n)$ -* Space complexity: $O(n)$ +* Space complexity: $O(h)$ + * Best Case ([balanced tree](https://www.geeksforgeeks.org/balanced-binary-tree/)): $O(log(n))$ + * Worst Case ([degenerate tree](https://www.geeksforgeeks.org/introduction-to-degenerate-binary-tree/)): $O(n)$ + +> Where $n$ is the number of nodes in the tree and $h$ is the height of the tree. --- From 894f2bb32ee832faf31b4d7699cb33295eedfaf6 Mon Sep 17 00:00:00 2001 From: Anton Dryakhlykh Date: Sun, 8 Dec 2024 17:16:36 +0300 Subject: [PATCH 13/45] Create 0228-summary-ranges.swift --- swift/0228-summary-ranges.swift | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 swift/0228-summary-ranges.swift diff --git a/swift/0228-summary-ranges.swift b/swift/0228-summary-ranges.swift new file mode 100644 index 000000000..cb0736d83 --- /dev/null +++ b/swift/0228-summary-ranges.swift @@ -0,0 +1,27 @@ +class Solution { + func summaryRanges(_ nums: [Int]) -> [String] { + if nums.isEmpty { + return [] + } + var l = 0 + var res = [String]() + for r in 1..\(nums[r - 1])") + } + l = r + } + } + + if l == nums.count - 1 { + res.append("\(nums[l])") + } else { + res.append("\(nums[l])->\(nums[nums.count - 1])") + } + + return res + } +} \ No newline at end of file From 6611623cb7a13bf7f4e19734ad2b689be14a25c0 Mon Sep 17 00:00:00 2001 From: Anton Dryakhlykh Date: Mon, 9 Dec 2024 17:43:26 +0300 Subject: [PATCH 14/45] Create 1493-longest-subarray-of-1s-after-deleting-one-element.swift --- ...ray-of-1s-after-deleting-one-element.swift | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 swift/1493-longest-subarray-of-1s-after-deleting-one-element.swift diff --git a/swift/1493-longest-subarray-of-1s-after-deleting-one-element.swift b/swift/1493-longest-subarray-of-1s-after-deleting-one-element.swift new file mode 100644 index 000000000..a1a712d3f --- /dev/null +++ b/swift/1493-longest-subarray-of-1s-after-deleting-one-element.swift @@ -0,0 +1,22 @@ +class Solution { + func longestSubarray(_ nums: [Int]) -> Int { + var zeroCount = 0 + var res = 0 + var l = 0 + + for r in 0.. 1 { + if nums[l] == 0 { + zeroCount -= 1 + } + l += 1 + } + res = max(res, r - l) + } + + return res + } +} \ No newline at end of file From fdaf14b34e2bea8a7358ecb31b2a29b7f2f9c706 Mon Sep 17 00:00:00 2001 From: Sri Hari <94112314+Srihari2222@users.noreply.github.com> Date: Wed, 11 Dec 2024 01:29:28 +0530 Subject: [PATCH 15/45] Batch-3/Neetcode-All/Added-articles (#3765) --- articles/buy-and-sell-crypto-with-cooldown.md | 6 +- articles/champagne-tower.md | 562 +++++++++++++++++ ...ing-contains-all-binary-codes-of-size-k.md | 497 +++++++++++++++ articles/contiguous-array.md | 341 ++++++++++ ...n-array-into-a-2d-array-with-conditions.md | 317 ++++++++++ ...e-array-into-arrays-with-max-difference.md | 238 +++++++ articles/encode-and-decode-tinyurl.md | 312 +++++++++ articles/find-the-difference-of-two-arrays.md | 467 ++++++++++++++ articles/first-missing-positive.md | 536 ++++++++++++++++ articles/insert-delete-getrandom-o1.md | 300 +++++++++ articles/majority-element-ii.md | 597 ++++++++++++++++++ ...umber-of-operations-to-make-array-empty.md | 563 +++++++++++++++++ articles/minimum-penalty-for-a-shop.md | 497 +++++++++++++++ articles/non-decreasing-array.md | 105 +++ articles/number-of-zero-filled-subarrays.md | 338 ++++++++++ articles/optimal-partition-of-string.md | 250 ++++++++ articles/range-sum-query-2d-immutable.md | 369 +++++++++++ articles/repeated-dna-sequences.md | 470 ++++++++++++++ articles/sign-of-the-product-of-an-array.md | 158 +++++ articles/sort-an-array.md | 2 +- articles/sort-characters-by-frequency.md | 340 ++++++++++ ...-absolute-differences-in-a-sorted-array.md | 430 +++++++++++++ articles/wiggle-sort.md | 293 +++++++++ 23 files changed, 7982 insertions(+), 6 deletions(-) create mode 100644 articles/champagne-tower.md create mode 100644 articles/check-if-a-string-contains-all-binary-codes-of-size-k.md create mode 100644 articles/contiguous-array.md create mode 100644 articles/convert-an-array-into-a-2d-array-with-conditions.md create mode 100644 articles/divide-array-into-arrays-with-max-difference.md create mode 100644 articles/encode-and-decode-tinyurl.md create mode 100644 articles/find-the-difference-of-two-arrays.md create mode 100644 articles/first-missing-positive.md create mode 100644 articles/insert-delete-getrandom-o1.md create mode 100644 articles/majority-element-ii.md create mode 100644 articles/minimum-number-of-operations-to-make-array-empty.md create mode 100644 articles/minimum-penalty-for-a-shop.md create mode 100644 articles/non-decreasing-array.md create mode 100644 articles/number-of-zero-filled-subarrays.md create mode 100644 articles/optimal-partition-of-string.md create mode 100644 articles/range-sum-query-2d-immutable.md create mode 100644 articles/repeated-dna-sequences.md create mode 100644 articles/sign-of-the-product-of-an-array.md create mode 100644 articles/sort-characters-by-frequency.md create mode 100644 articles/sum-of-absolute-differences-in-a-sorted-array.md create mode 100644 articles/wiggle-sort.md diff --git a/articles/buy-and-sell-crypto-with-cooldown.md b/articles/buy-and-sell-crypto-with-cooldown.md index 3c4ff367b..366028454 100644 --- a/articles/buy-and-sell-crypto-with-cooldown.md +++ b/articles/buy-and-sell-crypto-with-cooldown.md @@ -634,7 +634,7 @@ class Solution: for i in range(n - 1, -1, -1): dp_buy = max(dp1_sell - prices[i], dp1_buy) dp_sell = max(dp2_buy + prices[i], dp1_sell) - dp2_buy, dp1_sell = dp1_buy, dp1_sell + dp2_buy = dp1_buy dp1_buy, dp1_sell = dp_buy, dp_sell return dp1_buy @@ -651,7 +651,6 @@ public class Solution { int dp_buy = Math.max(dp1_sell - prices[i], dp1_buy); int dp_sell = Math.max(dp2_buy + prices[i], dp1_sell); dp2_buy = dp1_buy; - dp1_sell = dp1_sell; dp1_buy = dp_buy; dp1_sell = dp_sell; } @@ -673,7 +672,6 @@ public: int dp_buy = max(dp1_sell - prices[i], dp1_buy); int dp_sell = max(dp2_buy + prices[i], dp1_sell); dp2_buy = dp1_buy; - dp1_sell = dp1_sell; dp1_buy = dp_buy; dp1_sell = dp_sell; } @@ -698,7 +696,6 @@ class Solution { let dp_buy = Math.max(dp1_sell - prices[i], dp1_buy); let dp_sell = Math.max(dp2_buy + prices[i], dp1_sell); dp2_buy = dp1_buy; - dp1_sell = dp1_sell; dp1_buy = dp_buy; dp1_sell = dp_sell; } @@ -719,7 +716,6 @@ public class Solution { int dp_buy = Math.Max(dp1_sell - prices[i], dp1_buy); int dp_sell = Math.Max(dp2_buy + prices[i], dp1_sell); dp2_buy = dp1_buy; - dp1_sell = dp1_sell; dp1_buy = dp_buy; dp1_sell = dp_sell; } diff --git a/articles/champagne-tower.md b/articles/champagne-tower.md new file mode 100644 index 000000000..d2f36a28e --- /dev/null +++ b/articles/champagne-tower.md @@ -0,0 +1,562 @@ +## 1. Recursion + +::tabs-start + +```python +class Solution: + def champagneTower(self, poured: int, query_row: int, query_glass: int) -> float: + def rec(row, glass): + if row < 0 or glass < 0 or glass > row: + return 0 + + if row == 0 and glass == 0: + return poured + + left_parent = max(0, rec(row - 1, glass - 1) - 1) + right_parent = max(0, rec(row - 1, glass) - 1) + + return (left_parent + right_parent) / 2 + + return min(1, rec(query_row, query_glass)) +``` + +```java +public class Solution { + public double champagneTower(int poured, int query_row, int query_glass) { + return Math.min(1, rec(poured, query_row, query_glass)); + } + + private double rec(int poured, int row, int glass) { + if (row < 0 || glass < 0 || glass > row) { + return 0; + } + + if (row == 0 && glass == 0) { + return poured; + } + + double leftParent = Math.max(0, rec(poured, row - 1, glass - 1) - 1); + double rightParent = Math.max(0, rec(poured, row - 1, glass) - 1); + + return (leftParent + rightParent) / 2; + } +} +``` + +```cpp +class Solution { +public: + double champagneTower(int poured, int query_row, int query_glass) { + return min(1.0, rec(poured, query_row, query_glass)); + } + +private: + double rec(int poured, int row, int glass) { + if (row < 0 || glass < 0 || glass > row) { + return 0; + } + + if (row == 0 && glass == 0) { + return poured; + } + + double leftParent = max(0.0, rec(poured, row - 1, glass - 1) - 1); + double rightParent = max(0.0, rec(poured, row - 1, glass) - 1); + + return (leftParent + rightParent) / 2; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} poured + * @param {number} query_row + * @param {number} query_glass + * @return {number} + */ + champagneTower(poured, query_row, query_glass) { + const rec = (row, glass) => { + if (row < 0 || glass < 0 || glass > row) { + return 0; + } + + if (row === 0 && glass === 0) { + return poured; + } + + const leftParent = Math.max(0, rec(row - 1, glass - 1) - 1); + const rightParent = Math.max(0, rec(row - 1, glass) - 1); + + return (leftParent + rightParent) / 2; + }; + + return Math.min(1, rec(query_row, query_glass)); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(2 ^ n)$ +* Space complexity: $O(n)$ + +> Where $n$ is the given $queryRow$. + +--- + +## 2. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def champagneTower(self, poured: int, query_row: int, query_glass: int) -> float: + memo = { (0, 0) : poured } + + def rec(row, glass): + if row < 0 or glass < 0 or glass > row: + return 0 + if (row, glass) in memo: + return memo[(row, glass)] + + left_parent = max(0, rec(row - 1, glass - 1) - 1) + right_parent = max(0, rec(row - 1, glass) - 1) + + memo[(row, glass)] = (left_parent + right_parent) / 2 + return memo[(row, glass)] + + return min(1, rec(query_row, query_glass)) +``` + +```java +public class Solution { + public double champagneTower(int poured, int query_row, int query_glass) { + double[][] memo = new double[query_row + 5][]; + for (int i = 0; i < query_row + 5; i++) { + memo[i] = new double[i + 1]; + Arrays.fill(memo[i], -1); + } + memo[0][0] = poured; + + return Math.min(1, rec(memo, query_row, query_glass)); + } + + private double rec(double[][] memo, int row, int glass) { + if (row < 0 || glass < 0 || glass > row) { + return 0; + } + + if (memo[row][glass] != -1) { + return memo[row][glass]; + } + + double leftParent = Math.max(0, rec(memo, row - 1, glass - 1) - 1); + double rightParent = Math.max(0, rec(memo, row - 1, glass) - 1); + + memo[row][glass] = (leftParent + rightParent) / 2; + return memo[row][glass]; + } +} +``` + +```cpp +class Solution { +public: + double champagneTower(int poured, int query_row, int query_glass) { + vector> memo(query_row + 5); + for (int i = 0; i <= query_row + 4; i++) { + memo[i].resize(i + 1, -1); + } + + memo[0][0] = poured; + return min(1.0, rec(memo, query_row, query_glass)); + } + +private: + double rec(vector>& memo, int row, int glass) { + if (row < 0 || glass < 0 || glass > row) { + return 0; + } + + if (memo[row][glass] != -1) { + return memo[row][glass]; + } + + double leftParent = max(0.0, rec(memo, row - 1, glass - 1) - 1); + double rightParent = max(0.0, rec(memo, row - 1, glass) - 1); + + memo[row][glass] = (leftParent + rightParent) / 2; + return memo[row][glass]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} poured + * @param {number} query_row + * @param {number} query_glass + * @return {number} + */ + champagneTower(poured, query_row, query_glass) { + const memo = Array.from({ length: query_row + 5 }, (_, i) => Array(i + 1).fill(-1)); + memo[0][0] = poured; + + const rec = (row, glass) => { + if (row < 0 || glass < 0 || glass > row) { + return 0; + } + + if (memo[row][glass] != -1) { + return memo[row][glass]; + } + + const leftParent = Math.max(0, rec(row - 1, glass - 1) - 1); + const rightParent = Math.max(0, rec(row - 1, glass) - 1); + + memo[row][glass] = (leftParent + rightParent) / 2; + return memo[row][glass]; + + return (leftParent + rightParent) / 2; + }; + + return Math.min(1, rec(query_row, query_glass)); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * m)$ +* Space complexity: $O(n * m)$ + +> Where $n$ is the given $queryRow$ and $m$ is the given $queryGlass$. + +--- + +## 3. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def champagneTower(self, poured: int, query_row: int, query_glass: int) -> float: + dp = [[0] * (i + 1) for i in range(query_row + 5)] + dp[0][0] += poured + + for row in range(min(99, query_row + 1)): + for glass in range(row + 1): + excess = (dp[row][glass] - 1.0) / 2.0 + if excess > 0: + dp[row + 1][glass] += excess + dp[row + 1][glass + 1] += excess + + return min(1.0, dp[query_row][query_glass]) +``` + +```java +public class Solution { + public double champagneTower(int poured, int query_row, int query_glass) { + double[][] dp = new double[query_row + 5][]; + for (int i = 0; i < query_row + 5; i++) { + dp[i] = new double[i + 1]; + } + + dp[0][0] += poured; + + for (int row = 0; row < Math.min(99, query_row + 1); row++) { + for (int glass = 0; glass <= row; glass++) { + double excess = (dp[row][glass] - 1.0) / 2.0; + if (excess > 0) { + dp[row + 1][glass] += excess; + dp[row + 1][glass + 1] += excess; + } + } + } + + return Math.min(1.0, dp[query_row][query_glass]); + } +} +``` + +```cpp +class Solution { +public: + double champagneTower(int poured, int query_row, int query_glass) { + vector> dp(query_row + 5); + for (int i = 0; i <= query_row + 4; i++) { + dp[i].resize(i + 1, 0); + } + + dp[0][0] += poured; + + for (int row = 0; row < min(99, query_row + 1); row++) { + for (int glass = 0; glass <= row; glass++) { + double excess = (dp[row][glass] - 1.0) / 2.0; + if (excess > 0) { + dp[row + 1][glass] += excess; + dp[row + 1][glass + 1] += excess; + } + } + } + + return min(1.0, dp[query_row][query_glass]); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} poured + * @param {number} query_row + * @param {number} query_glass + * @return {number} + */ + champagneTower(poured, query_row, query_glass) { + const dp = Array.from({ length: query_row + 5 }, (_, i) => Array(i + 1).fill(0)); + dp[0][0] += poured; + + for (let row = 0; row < Math.min(99, query_row + 1); row++) { + for (let glass = 0; glass <= row; glass++) { + const excess = (dp[row][glass] - 1.0) / 2.0; + if (excess > 0) { + dp[row + 1][glass] += excess; + dp[row + 1][glass + 1] += excess; + } + } + } + + return Math.min(1.0, dp[query_row][query_glass]); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * m)$ +* Space complexity: $O(n * m)$ + +> Where $n$ is the given $queryRow$ and $m$ is the given $queryGlass$. + +--- + +## 4. Dynamic Programming (Space Optimized) + +::tabs-start + +```python +class Solution: + def champagneTower(self, poured: int, query_row: int, query_glass: int) -> float: + prev_row = [poured] # Flow + + for row in range(1, query_row + 1): + cur_row = [0] * (row + 1) + for i in range(row): + extra = prev_row[i] - 1 + if extra > 0: + cur_row[i] += 0.5 * extra + cur_row[i + 1] += 0.5 * extra + prev_row = cur_row + + return min(1, prev_row[query_glass]) +``` + +```java +public class Solution { + public double champagneTower(int poured, int query_row, int query_glass) { + double[] prev_row = new double[] { poured }; // Flow + + for (int row = 1; row <= query_row; row++) { + double[] cur_row = new double[row + 1]; + for (int i = 0; i < row; i++) { + double extra = prev_row[i] - 1; + if (extra > 0) { + cur_row[i] += 0.5 * extra; + cur_row[i + 1] += 0.5 * extra; + } + } + prev_row = cur_row; + } + + return Math.min(1.0, prev_row[query_glass]); + } +} +``` + +```cpp +class Solution { +public: + double champagneTower(int poured, int query_row, int query_glass) { + vector prev_row = {double(poured)}; // Flow + + for (int row = 1; row <= query_row; row++) { + vector cur_row(row + 1, 0); + for (int i = 0; i < row; i++) { + double extra = prev_row[i] - 1; + if (extra > 0) { + cur_row[i] += 0.5 * extra; + cur_row[i + 1] += 0.5 * extra; + } + } + prev_row = cur_row; + } + + return min(1.0, prev_row[query_glass]); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} poured + * @param {number} query_row + * @param {number} query_glass + * @return {number} + */ + champagneTower(poured, query_row, query_glass) { + let prev_row = [poured]; // Flow + + for (let row = 1; row <= query_row; row++) { + let cur_row = new Array(row + 1).fill(0); + for (let i = 0; i < row; i++) { + let extra = prev_row[i] - 1; + if (extra > 0) { + cur_row[i] += 0.5 * extra; + cur_row[i + 1] += 0.5 * extra; + } + } + prev_row = cur_row; + } + + return Math.min(1, prev_row[query_glass]); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * m)$ +* Space complexity: $O(n)$ + +> Where $n$ is the given $queryRow$ and $m$ is the given $queryGlass$. + +--- + +## 5. Dynamic Programming (Optimal) + +::tabs-start + +```python +class Solution: + def champagneTower(self, poured: int, query_row: int, query_glass: int) -> float: + dp = [poured] + [0] * query_row + + for row in range(1, query_row + 1): + for i in range(row - 1, -1, -1): + extra = dp[i] - 1 + if extra > 0: + dp[i] = 0.5 * extra + dp[i + 1] += 0.5 * extra + else: + dp[i] = 0 + + return min(1, dp[query_glass]) +``` + +```java +public class Solution { + public double champagneTower(int poured, int query_row, int query_glass) { + double[] dp = new double[query_row + 1]; + dp[0] = poured; + + for (int row = 1; row <= query_row; row++) { + for (int i = row - 1; i >= 0; i--) { + double extra = dp[i] - 1; + if (extra > 0) { + dp[i] = 0.5 * extra; + dp[i + 1] += 0.5 * extra; + } else { + dp[i] = 0; + } + } + } + + return Math.min(1, dp[query_glass]); + } +} +``` + +```cpp +class Solution { +public: + double champagneTower(int poured, int query_row, int query_glass) { + vector dp(query_row + 1, 0); + dp[0] = poured; + + for (int row = 1; row <= query_row; row++) { + for (int i = row - 1; i >= 0; i--) { + double extra = dp[i] - 1; + if (extra > 0) { + dp[i] = 0.5 * extra; + dp[i + 1] += 0.5 * extra; + } else { + dp[i] = 0; + } + } + } + + return min(1.0, dp[query_glass]); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} poured + * @param {number} query_row + * @param {number} query_glass + * @return {number} + */ + champagneTower(poured, query_row, query_glass) { + let dp = new Array(query_row + 1).fill(0); + dp[0] = poured; + + for (let row = 1; row <= query_row; row++) { + for (let i = row - 1; i >= 0; i--) { + let extra = dp[i] - 1; + if (extra > 0) { + dp[i] = 0.5 * extra; + dp[i + 1] += 0.5 * extra; + } else { + dp[i] = 0; + } + } + } + + return Math.min(1, dp[query_glass]); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * m)$ +* Space complexity: $O(n)$ + +> Where $n$ is the given $queryRow$ and $m$ is the given $queryGlass$. \ No newline at end of file diff --git a/articles/check-if-a-string-contains-all-binary-codes-of-size-k.md b/articles/check-if-a-string-contains-all-binary-codes-of-size-k.md new file mode 100644 index 000000000..917412238 --- /dev/null +++ b/articles/check-if-a-string-contains-all-binary-codes-of-size-k.md @@ -0,0 +1,497 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def hasAllCodes(self, s: str, k: int) -> bool: + n = len(s) + if n < (1 << k): + return False + + for num in range(1 << k): + binaryCode = format(num, f'0{k}b') + if binaryCode not in s: + return False + + return True +``` + +```java +public class Solution { + public boolean hasAllCodes(String s, int k) { + int n = s.length(); + if (n < (1 << k)) { + return false; + } + + for (int num = 0; num < (1 << k); num++) { + String binaryCode = String.format("%" + k + "s", Integer.toBinaryString(num)).replace(' ', '0'); + if (!s.contains(binaryCode)) { + return false; + } + } + + return true; + } +} +``` + +```cpp +class Solution { +public: + bool hasAllCodes(string s, int k) { + int n = s.size(); + if (n < (1 << k)) { + return false; + } + + for (int num = 0; num < (1 << k); num++) { + string binaryCode = bitset<20>(num).to_string().substr(20 - k); + if (s.find(binaryCode) == string::npos) { + return false; + } + } + + return true; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @param {number} k + * @return {boolean} + */ + hasAllCodes(s, k) { + const n = s.length; + if (n < (1 << k)) { + return false; + } + + for (let num = 0; num < (1 << k); num++) { + const binaryCode = num.toString(2).padStart(k, '0'); + if (!s.includes(binaryCode)) { + return false; + } + } + + return true; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * 2 ^ k)$ +* Space complexity: $O(k)$ + +> Where $n$ is the length of the string $s$ and $k$ is the length of the binary code. + +--- + +## 2. Hash Set + +::tabs-start + +```python +class Solution: + def hasAllCodes(self, s: str, k: int) -> bool: + if len(s) < 2 ** k: + return False + + codeSet = set() + for i in range(len(s) - k + 1): + codeSet.add(s[i:i + k]) + + return len(codeSet) == 2 ** k +``` + +```java +public class Solution { + public boolean hasAllCodes(String s, int k) { + if (s.length() < (1 << k)) { + return false; + } + + HashSet codeSet = new HashSet<>(); + for (int i = 0; i <= s.length() - k; i++) { + codeSet.add(s.substring(i, i + k)); + } + + return codeSet.size() == (1 << k); + } +} +``` + +```cpp +class Solution { +public: + bool hasAllCodes(std::string s, int k) { + if (s.size() < (1 << k)) { + return false; + } + + std::unordered_set codeSet; + for (int i = 0; i <= s.size() - k; i++) { + codeSet.insert(s.substr(i, k)); + } + + return codeSet.size() == (1 << k); + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @param {number} k + * @return {boolean} + */ + hasAllCodes(s, k) { + if (s.length < (1 << k)) { + return false; + } + + const codeSet = new Set(); + for (let i = 0; i <= s.length - k; i++) { + codeSet.add(s.substring(i, i + k)); + } + + return codeSet.size === (1 << k); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * k)$ +* Space complexity: $O(2 ^ k)$ + +> Where $n$ is the length of the string $s$ and $k$ is the length of the binary code. + +--- + +## 3. Sliding Window + +::tabs-start + +```python +class Solution: + def hasAllCodes(self, s: str, k: int) -> bool: + n = len(s) + if n < (1 << k): + return False + + codeSet = [False] * (1 << k) + cur = 0 + i = j = 0 + bit = k - 1 + while j < k: + if s[j] == '1': + cur |= (1 << bit) + bit -= 1 + j += 1 + + have = 1 + codeSet[cur] = True + while j < n: + if s[i] == '1': + cur ^= (1 << (k - 1)) + i += 1 + + cur <<= 1 + if s[j] == '1': + cur |= 1 + j += 1 + + if not codeSet[cur]: + have += 1 + codeSet[cur] = True + + return have == (1 << k) +``` + +```java +public class Solution { + public boolean hasAllCodes(String s, int k) { + int n = s.length(); + if (n < (1 << k)) { + return false; + } + + boolean[] codeSet = new boolean[1 << k]; + int cur = 0; + int i = 0, j = 0, bit = k - 1; + + while (j < k) { + if (s.charAt(j) == '1') { + cur |= (1 << bit); + } + bit--; + j++; + } + + int have = 1; + codeSet[cur] = true; + + while (j < n) { + if (s.charAt(i) == '1') { + cur ^= (1 << (k - 1)); + } + i++; + + cur <<= 1; + if (s.charAt(j) == '1') { + cur |= 1; + } + j++; + + if (!codeSet[cur]) { + have++; + codeSet[cur] = true; + } + } + + return have == (1 << k); + } +} +``` + +```cpp +class Solution { +public: + bool hasAllCodes(string s, int k) { + int n = s.size(); + if (n < (1 << k)) { + return false; + } + + vector codeSet(1 << k, false); + int cur = 0; + int i = 0, j = 0, bit = k - 1; + + while (j < k) { + if (s[j] == '1') { + cur |= (1 << bit); + } + bit--; + j++; + } + + int have = 1; + codeSet[cur] = true; + + while (j < n) { + if (s[i] == '1') { + cur ^= (1 << (k - 1)); + } + i++; + + cur <<= 1; + if (s[j] == '1') { + cur |= 1; + } + j++; + + if (!codeSet[cur]) { + have++; + codeSet[cur] = true; + } + } + + return have == (1 << k); + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @param {number} k + * @return {boolean} + */ + hasAllCodes(s, k) { + const n = s.length; + if (n < (1 << k)) { + return false; + } + + const codeSet = new Array(1 << k).fill(false); + let cur = 0; + let i = 0, j = 0, bit = k - 1; + + while (j < k) { + if (s[j] === '1') { + cur |= (1 << bit); + } + bit--; + j++; + } + + let have = 1; + codeSet[cur] = true; + + while (j < n) { + if (s[i] === '1') { + cur ^= (1 << (k - 1)); + } + i++; + + cur <<= 1; + if (s[j] === '1') { + cur |= 1; + } + j++; + + if (!codeSet[cur]) { + have++; + codeSet[cur] = true; + } + } + + return have === (1 << k); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(2 ^ k)$ + +> Where $n$ is the length of the string $s$ and $k$ is the length of the binary code. + +--- + +## 4. Sliding Window (Optimal) + +::tabs-start + +```python +class Solution: + def hasAllCodes(self, s: str, k: int) -> bool: + n = len(s) + if n < (1 << k): + return False + + codeSet = [False] * (1 << k) + cur = 0 + have = 0 + + for i in range(n): + cur = ((cur << 1) & ((1 << k) - 1)) | (ord(s[i]) - ord('0')) + + if i >= k - 1: + if not codeSet[cur]: + codeSet[cur] = True + have += 1 + + return have == (1 << k) +``` + +```java +public class Solution { + public boolean hasAllCodes(String s, int k) { + int n = s.length(); + if (n < (1 << k)) { + return false; + } + + boolean[] codeSet = new boolean[1 << k]; + int cur = 0, have = 0; + + for (int i = 0; i < n; i++) { + cur = ((cur << 1) & ((1 << k) - 1)) | (s.charAt(i) - '0'); + + if (i >= k - 1) { + if (!codeSet[cur]) { + codeSet[cur] = true; + have++; + } + } + } + + return have == (1 << k); + } +} +``` + +```cpp +class Solution { +public: + bool hasAllCodes(string s, int k) { + int n = s.size(); + if (n < (1 << k)) { + return false; + } + + vector codeSet(1 << k, false); + int cur = 0, have = 0; + + for (int i = 0; i < n; i++) { + cur = ((cur << 1) & ((1 << k) - 1)) | (s[i] - '0'); + + if (i >= k - 1) { + if (!codeSet[cur]) { + codeSet[cur] = true; + have++; + } + } + } + + return have == (1 << k); + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @param {number} k + * @return {boolean} + */ + hasAllCodes(s, k) { + const n = s.length; + if (n < (1 << k)) { + return false; + } + + const codeSet = new Array(1 << k).fill(false); + let cur = 0, have = 0; + + for (let i = 0; i < n; i++) { + cur = ((cur << 1) & ((1 << k) - 1)) | (s[i] - '0'); + + if (i >= k - 1) { + if (!codeSet[cur]) { + codeSet[cur] = true; + have++; + } + } + } + + return have === (1 << k); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(2 ^ k)$ + +> Where $n$ is the length of the string $s$ and $k$ is the length of the binary code. \ No newline at end of file diff --git a/articles/contiguous-array.md b/articles/contiguous-array.md new file mode 100644 index 000000000..f13825744 --- /dev/null +++ b/articles/contiguous-array.md @@ -0,0 +1,341 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def findMaxLength(self, nums: List[int]) -> int: + n, res = len(nums), 0 + + for i in range(n): + zeros = ones = 0 + for j in range(i, n): + if nums[j] == 1: + ones += 1 + else: + zeros += 1 + + if ones == zeros and res < (j - i + 1): + res = j - i + 1 + + return res +``` + +```java +public class Solution { + public int findMaxLength(int[] nums) { + int n = nums.length, res = 0; + + for (int i = 0; i < n; i++) { + int zeros = 0, ones = 0; + for (int j = i; j < n; j++) { + if (nums[j] == 1) { + ones++; + } else { + zeros++; + } + if (ones == zeros && res < (j - i + 1)) { + res = j - i + 1; + } + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int findMaxLength(vector& nums) { + int n = nums.size(), res = 0; + + for (int i = 0; i < n; i++) { + int zeros = 0, ones = 0; + for (int j = i; j < n; j++) { + if (nums[j] == 1) { + ones++; + } else { + zeros++; + } + if (ones == zeros && res < (j - i + 1)) { + res = j - i + 1; + } + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + findMaxLength(nums) { + let n = nums.length, res = 0; + + for (let i = 0; i < n; i++) { + let zeros = 0, ones = 0; + for (let j = i; j < n; j++) { + if (nums[j] === 1) { + ones++; + } else { + zeros++; + } + if (ones === zeros && res < (j - i + 1)) { + res = j - i + 1; + } + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(1)$ + +--- + +## 2. Array + +::tabs-start + +```python +class Solution: + def findMaxLength(self, nums: List[int]) -> int: + n = len(nums) + res = 0 + diff_index = [None] * (2 * n + 1) + count = 0 + + for i in range(n): + count += 1 if nums[i] == 1 else -1 + if count == 0: + res = i + 1 + if diff_index[count + n] is not None: + res = max(res, i - diff_index[count + n]) + else: + diff_index[count + n] = i + + return res +``` + +```java +public class Solution { + public int findMaxLength(int[] nums) { + int n = nums.length, res = 0, count = 0; + int[] diffIndex = new int[2 * n + 1]; + Arrays.fill(diffIndex, -2); + diffIndex[n] = -1; + + for (int i = 0; i < n; i++) { + count += nums[i] == 1 ? 1 : -1; + if (diffIndex[count + n] != -2) { + res = Math.max(res, i - diffIndex[count + n]); + } else { + diffIndex[count + n] = i; + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int findMaxLength(vector& nums) { + int n = nums.size(), res = 0, count = 0; + vector diffIndex(2 * n + 1, -2); + diffIndex[n] = -1; + + for (int i = 0; i < n; i++) { + count += nums[i] == 1 ? 1 : -1; + if (diffIndex[count + n] != -2) { + res = max(res, i - diffIndex[count + n]); + } else { + diffIndex[count + n] = i; + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + findMaxLength(nums) { + const n = nums.length; + let res = 0, count = 0; + const diffIndex = new Array(2 * n + 1).fill(-2); + diffIndex[n] = -1; + + for (let i = 0; i < n; i++) { + count += nums[i] === 1 ? 1 : -1; + if (diffIndex[count + n] !== -2) { + res = Math.max(res, i - diffIndex[count + n]); + } else { + diffIndex[count + n] = i; + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Hash Map + +::tabs-start + +```python +class Solution: + def findMaxLength(self, nums: List[int]) -> int: + zero = one = res = 0 + diff_index = {} + + for i, n in enumerate(nums): + if n == 0: + zero += 1 + else: + one += 1 + + if one - zero not in diff_index: + diff_index[one - zero] = i + + if one == zero: + res = one + zero + else: + idx = diff_index[one - zero] + res = max(res, i - idx) + + return res +``` + +```java +public class Solution { + public int findMaxLength(int[] nums) { + int zero = 0, one = 0, res = 0; + HashMap diffIndex = new HashMap<>(); + + for (int i = 0; i < nums.length; i++) { + if (nums[i] == 0) { + zero++; + } else { + one++; + } + + int diff = one - zero; + if (!diffIndex.containsKey(diff)) { + diffIndex.put(diff, i); + } + + if (one == zero) { + res = one + zero; + } else { + res = Math.max(res, i - diffIndex.get(diff)); + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int findMaxLength(vector& nums) { + int zero = 0, one = 0, res = 0; + unordered_map diffIndex; + + for (int i = 0; i < nums.size(); i++) { + if (nums[i] == 0) { + zero++; + } else { + one++; + } + + int diff = one - zero; + if (diffIndex.find(diff) == diffIndex.end()) { + diffIndex[diff] = i; + } + + if (one == zero) { + res = one + zero; + } else { + res = max(res, i - diffIndex[diff]); + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + findMaxLength(nums) { + let zero = 0, one = 0, res = 0; + const diffIndex = new Map(); + + for (let i = 0; i < nums.length; i++) { + if (nums[i] === 0) { + zero++; + } else { + one++; + } + + const diff = one - zero; + if (!diffIndex.has(diff)) { + diffIndex.set(diff, i); + } + + if (one === zero) { + res = one + zero; + } else { + res = Math.max(res, i - diffIndex.get(diff)); + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/convert-an-array-into-a-2d-array-with-conditions.md b/articles/convert-an-array-into-a-2d-array-with-conditions.md new file mode 100644 index 000000000..7f5906f0d --- /dev/null +++ b/articles/convert-an-array-into-a-2d-array-with-conditions.md @@ -0,0 +1,317 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def findMatrix(self, nums: List[int]) -> List[List[int]]: + res = [] + + for num in nums: + r = 0 + while r < len(res): + if num not in res[r]: + break + r += 1 + if r == len(res): + res.append([]) + res[r].append(num) + + return res +``` + +```java +public class Solution { + public List> findMatrix(int[] nums) { + List> res = new ArrayList<>(); + + for (int num : nums) { + int r = 0; + while (r < res.size()) { + if (!res.get(r).contains(num)) { + break; + } + r++; + } + if (r == res.size()) { + res.add(new ArrayList<>()); + } + res.get(r).add(num); + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector> findMatrix(vector& nums) { + vector> res; + + for (int num : nums) { + int r = 0; + while (r < res.size()) { + if (find(res[r].begin(), res[r].end(), num) == res[r].end()) { + break; + } + r++; + } + if (r == res.size()) { + res.push_back({}); + } + res[r].push_back(num); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number[][]} + */ + findMatrix(nums) { + const res = []; + + for (const num of nums) { + let r = 0; + while (r < res.length) { + if (!res[r].includes(num)) { + break; + } + r++; + } + if (r === res.length) { + res.push([]); + } + res[r].push(num); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * m)$ +* Space complexity: $O(n)$ for the output array. + +> Where $n$ is the size of the array $nums$ and $m$ is the frequency of the most frequent element in the given array. + +--- + +## 2. Sorting + +::tabs-start + +```python +class Solution: + def findMatrix(self, nums: List[int]) -> List[List[int]]: + nums.sort() + res = [] + + i = 0 + while i < len(nums): + j = i + r = 0 + while j < len(nums) and nums[i] == nums[j]: + if r == len(res): + res.append([]) + res[r].append(nums[i]) + r += 1 + j += 1 + i = j + + return res +``` + +```java +public class Solution { + public List> findMatrix(int[] nums) { + Arrays.sort(nums); + List> res = new ArrayList<>(); + + int i = 0; + while (i < nums.length) { + int j = i; + int r = 0; + while (j < nums.length && nums[i] == nums[j]) { + if (r == res.size()) { + res.add(new ArrayList<>()); + } + res.get(r).add(nums[i]); + r++; + j++; + } + i = j; + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector> findMatrix(vector& nums) { + sort(nums.begin(), nums.end()); + vector> res; + + int i = 0; + while (i < nums.size()) { + int j = i, r = 0; + while (j < nums.size() && nums[i] == nums[j]) { + if (r == res.size()) { + res.push_back({}); + } + res[r].push_back(nums[i]); + r++; + j++; + } + i = j; + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number[][]} + */ + findMatrix(nums) { + nums.sort((a, b) => a - b); + const res = []; + + let i = 0; + while (i < nums.length) { + let j = i; + let r = 0; + while (j < nums.length && nums[i] === nums[j]) { + if (r === res.length) { + res.push([]); + } + res[r].push(nums[i]); + r++; + j++; + } + i = j; + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ for the output array. + +--- + +## 3. Frequency Count + +::tabs-start + +```python +class Solution: + def findMatrix(self, nums: List[int]) -> List[List[int]]: + count = defaultdict(int) + res = [] + + for num in nums: + row = count[num] + if len(res) == row: + res.append([]) + res[row].append(num) + count[num] += 1 + + return res +``` + +```java +public class Solution { + public List> findMatrix(int[] nums) { + Map count = new HashMap<>(); + List> res = new ArrayList<>(); + + for (int num : nums) { + int row = count.getOrDefault(num, 0); + if (res.size() == row) { + res.add(new ArrayList<>()); + } + res.get(row).add(num); + count.put(num, row + 1); + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector> findMatrix(vector& nums) { + unordered_map count; + vector> res; + + for (int num : nums) { + int row = count[num]; + if (res.size() == row) { + res.push_back({}); + } + res[row].push_back(num); + count[num]++; + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number[][]} + */ + findMatrix(nums) { + const count = new Map(); + const res = []; + + for (const num of nums) { + const row = count.get(num) || 0; + if (res.length === row) { + res.push([]); + } + res[row].push(num); + count.set(num, row + 1); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/divide-array-into-arrays-with-max-difference.md b/articles/divide-array-into-arrays-with-max-difference.md new file mode 100644 index 000000000..d969ca851 --- /dev/null +++ b/articles/divide-array-into-arrays-with-max-difference.md @@ -0,0 +1,238 @@ +## 1. Sorting + +::tabs-start + +```python +class Solution: + def divideArray(self, nums: List[int], k: int) -> List[List[int]]: + nums.sort() + res = [] + + for i in range(0, len(nums), 3): + if nums[i + 2] - nums[i] > k: + return [] + res.append(nums[i: i + 3]) + + return res +``` + +```java +public class Solution { + public int[][] divideArray(int[] nums, int k) { + Arrays.sort(nums); + int[][] res = new int[nums.length / 3][3]; + + for (int i = 0, idx = 0; i < nums.length; i += 3, idx++) { + if (nums[i + 2] - nums[i] > k) { + return new int[][]{}; + } + res[idx][0] = nums[i]; + res[idx][1] = nums[i + 1]; + res[idx][2] = nums[i + 2]; + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector> divideArray(vector& nums, int k) { + sort(nums.begin(), nums.end()); + vector> res; + + for (int i = 0; i < nums.size(); i += 3) { + if (nums[i + 2] - nums[i] > k) { + return {}; + } + res.push_back({nums[i], nums[i + 1], nums[i + 2]}); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {number[][]} + */ + divideArray(nums, k) { + nums.sort((a, b) => a - b); + const res = []; + + for (let i = 0; i < nums.length; i += 3) { + if (nums[i + 2] - nums[i] > k) { + return []; + } + res.push([nums[i], nums[i + 1], nums[i + 2]]); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ for the output array. + +--- + +## 2. Counting Sort + +::tabs-start + +```python +class Solution: + def divideArray(self, nums: List[int], k: int) -> List[List[int]]: + max_num = max(nums) + count = [0] * (max_num + 1) + for num in nums: + count[num] += 1 + + res = [] + group = [] + + for num in range(max_num + 1): + while count[num] > 0: + group.append(num) + count[num] -= 1 + + if len(group) == 3: + if group[2] - group[0] > k: + return [] + res.append(group) + group = [] + + return res +``` + +```java +public class Solution { + public int[][] divideArray(int[] nums, int k) { + int max = 0; + for (int num : nums) { + max = Math.max(max, num); + } + + int[] count = new int[max + 1]; + for (int num : nums) { + count[num]++; + } + + int[][] res = new int[nums.length / 3][3]; + int[] group = new int[3]; + int i = 0; + + for (int num = 0, idx = 0; num <= max; num++) { + while (count[num] > 0) { + group[i++] = num; + count[num]--; + + if (i == 3) { + if (group[2] - group[0] > k) { + return new int[][]{}; + } + for (int j = 0; j < 3; j++) { + res[idx][j] = group[j]; + } + i = 0; + idx++; + } + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector> divideArray(vector& nums, int k) { + int maxNum = *max_element(nums.begin(), nums.end()); + vector count(maxNum + 1, 0); + + for (int& num : nums) { + count[num]++; + } + + vector> res; + vector group; + + for (int num = 0; num <= maxNum; num++) { + while (count[num] > 0) { + group.push_back(num); + count[num]--; + + if (group.size() == 3) { + if (group[2] - group[0] > k) { + return {}; + } + res.push_back(group); + group.clear(); + } + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {number[][]} + */ + divideArray(nums, k) { + const max = Math.max(...nums); + const count = new Array(max + 1).fill(0); + + for (const num of nums) { + count[num]++; + } + + const res = []; + let group = []; + + for (let num = 0; num <= max; num++) { + while (count[num] > 0) { + group.push(num); + count[num]--; + + if (group.length === 3) { + if (group[2] - group[0] > k) { + return []; + } + res.push(group); + group = []; + } + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n + m)$ +* Space complexity: $O(n + m)$ + +> Where $n$ is the size of the array $nums$ and $m$ is the maximum element in $nums$. \ No newline at end of file diff --git a/articles/encode-and-decode-tinyurl.md b/articles/encode-and-decode-tinyurl.md new file mode 100644 index 000000000..0da60014f --- /dev/null +++ b/articles/encode-and-decode-tinyurl.md @@ -0,0 +1,312 @@ +## 1. List + +::tabs-start + +```python +class Codec: + + def __init__(self): + self.urls = [] + + def encode(self, longUrl: str) -> str: + self.urls.append(longUrl) + return 'http://tinyurl.com/' + str(len(self.urls) - 1) + + def decode(self, shortUrl: str) -> str: + return self.urls[int(shortUrl.split('/')[-1])] +``` + +```java +public class Codec { + private List urls; + + public Codec() { + urls = new ArrayList<>(); + } + + public String encode(String longUrl) { + urls.add(longUrl); + return "http://tinyurl.com/" + (urls.size() - 1); + } + + public String decode(String shortUrl) { + int index = Integer.parseInt(shortUrl.substring(shortUrl.lastIndexOf('/') + 1)); + return urls.get(index); + } +} +``` + +```cpp +class Solution { +public: + vector urls; + + string encode(string longUrl) { + urls.push_back(longUrl); + return "http://tinyurl.com/" + to_string(urls.size() - 1); + } + + string decode(string shortUrl) { + int index = stoi(shortUrl.substr(shortUrl.find_last_of('/') + 1)); + return urls[index]; + } +}; +``` + +```javascript +class Codec { + constructor() { + this.urls = []; + } + + /** + * Encodes a URL to a shortened URL. + * + * @param {string} longUrl + * @return {string} + */ + encode(longUrl) { + this.urls.push(longUrl); + return 'http://tinyurl.com/' + (this.urls.length - 1); + } + + /** + * Decodes a shortened URL to its original URL. + * + * @param {string} shortUrl + * @return {string} + */ + decode(shortUrl) { + const index = parseInt(shortUrl.split('/').pop()); + return this.urls[index]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(1)$ for $encode()$ and $decode()$. +* Space complexity: $O(n * m)$ + +> Where $n$ is the number of $longUrls$, $m$ is the average length of the URLs. + +--- + +## 2. Hash Map - I + +::tabs-start + +```python +class Codec: + + def __init__(self): + self.url_map = {} + self.id = 0 + + def encode(self, longUrl: str) -> str: + self.url_map[self.id] = longUrl + short_url = f"http://tinyurl.com/{self.id}" + self.id += 1 + return short_url + + def decode(self, shortUrl: str) -> str: + url_id = int(shortUrl.split('/')[-1]) + return self.url_map[url_id] +``` + +```java +public class Codec { + private HashMap urlMap; + private int id; + + public Codec() { + urlMap = new HashMap<>(); + id = 0; + } + + public String encode(String longUrl) { + urlMap.put(id, longUrl); + return "http://tinyurl.com/" + id++; + } + + public String decode(String shortUrl) { + int urlId = Integer.parseInt(shortUrl.substring(shortUrl.lastIndexOf('/') + 1)); + return urlMap.get(urlId); + } +} +``` + +```cpp +class Solution { + unordered_map urlMap; + int id; + +public: + Solution() : id(0) {} + + string encode(string longUrl) { + urlMap[id] = longUrl; + return "http://tinyurl.com/" + to_string(id++); + } + + string decode(string shortUrl) { + int urlId = stoi(shortUrl.substr(shortUrl.find_last_of('/') + 1)); + return urlMap[urlId]; + } +}; +``` + +```javascript +class Codec { + constructor() { + this.urlMap = {}; + this.id = 0; + } + + /** + * Encodes a URL to a shortened URL. + * + * @param {string} longUrl + * @return {string} + */ + encode(longUrl) { + this.urlMap[this.id] = longUrl; + return `http://tinyurl.com/${this.id++}`; + } + + /** + * Decodes a shortened URL to its original URL. + * + * @param {string} shortUrl + * @return {string} + */ + decode(shortUrl) { + const urlId = parseInt(shortUrl.split('/').pop(), 10); + return this.urlMap[urlId]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(1)$ for $encode()$ and $decode()$. +* Space complexity: $O(n * m)$ + +> Where $n$ is the number of $longUrls$, $m$ is the average length of the URLs. + +--- + +## 3. Hash Map - II + +::tabs-start + +```python +class Codec: + + def __init__(self): + self.encodeMap = {} + self.decodeMap = {} + self.base = "http://tinyurl.com/" + + def encode(self, longUrl: str) -> str: + if longUrl not in self.encodeMap: + shortUrl = self.base + str(len(self.encodeMap) + 1) + self.encodeMap[longUrl] = shortUrl + self.decodeMap[shortUrl] = longUrl + return self.encodeMap[longUrl] + + def decode(self, shortUrl: str) -> str: + return self.decodeMap[shortUrl] +``` + +```java +public class Codec { + private HashMap encodeMap = new HashMap<>(); + private HashMap decodeMap = new HashMap<>(); + private String base = "http://tinyurl.com/"; + + public String encode(String longUrl) { + if (!encodeMap.containsKey(longUrl)) { + String shortUrl = base + (encodeMap.size() + 1); + encodeMap.put(longUrl, shortUrl); + decodeMap.put(shortUrl, longUrl); + } + return encodeMap.get(longUrl); + } + + public String decode(String shortUrl) { + return decodeMap.get(shortUrl); + } +} +``` + +```cpp +class Solution { +private: + unordered_map encodeMap; + unordered_map decodeMap; + string base = "http://tinyurl.com/"; + +public: + string encode(string longUrl) { + if (encodeMap.find(longUrl) == encodeMap.end()) { + string shortUrl = base + to_string(encodeMap.size() + 1); + encodeMap[longUrl] = shortUrl; + decodeMap[shortUrl] = longUrl; + } + return encodeMap[longUrl]; + } + + string decode(string shortUrl) { + return decodeMap[shortUrl]; + } +}; +``` + +```javascript +class Codec { + constructor() { + this.encodeMap = new Map(); + this.decodeMap = new Map(); + this.base = "http://tinyurl.com/"; + } + + /** + * Encodes a URL to a shortened URL. + * + * @param {string} longUrl + * @return {string} + */ + encode(longUrl) { + if (!this.encodeMap.has(longUrl)) { + const shortUrl = this.base + (this.encodeMap.size + 1); + this.encodeMap.set(longUrl, shortUrl); + this.decodeMap.set(shortUrl, longUrl); + } + return this.encodeMap.get(longUrl); + } + + /** + * Decodes a shortened URL to its original URL. + * + * @param {string} shortUrl + * @return {string} + */ + decode(shortUrl) { + return this.decodeMap.get(shortUrl); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(1)$ for $encode()$ and $decode()$. +* Space complexity: $O(n * m)$ + +> Where $n$ is the number of $longUrls$, $m$ is the average length of the URLs. \ No newline at end of file diff --git a/articles/find-the-difference-of-two-arrays.md b/articles/find-the-difference-of-two-arrays.md new file mode 100644 index 000000000..853638267 --- /dev/null +++ b/articles/find-the-difference-of-two-arrays.md @@ -0,0 +1,467 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def findDifference(self, nums1: list[int], nums2: list[int]) -> list[list[int]]: + res = [set(), set()] + + for num1 in nums1: + found = False + for num2 in nums2: + if num1 == num2: + found = True + break + if not found: + res[0].add(num1) + + for num2 in nums2: + found = False + for num1 in nums1: + if num1 == num2: + found = True + break + if not found: + res[1].add(num2) + + return [list(res[0]), list(res[1])] +``` + +```java +public class Solution { + public List> findDifference(int[] nums1, int[] nums2) { + Set res1 = new HashSet<>(); + Set res2 = new HashSet<>(); + + for (int num1 : nums1) { + boolean found = false; + for (int num2 : nums2) { + if (num1 == num2) { + found = true; + break; + } + } + if (!found) { + res1.add(num1); + } + } + + for (int num2 : nums2) { + boolean found = false; + for (int num1 : nums1) { + if (num1 == num2) { + found = true; + break; + } + } + if (!found) { + res2.add(num2); + } + } + + List> result = new ArrayList<>(); + result.add(new ArrayList<>(res1)); + result.add(new ArrayList<>(res2)); + return result; + } +} +``` + +```cpp +class Solution { +public: + vector> findDifference(vector& nums1, vector& nums2) { + set res1, res2; + + for (int num1 : nums1) { + bool found = false; + for (int num2 : nums2) { + if (num1 == num2) { + found = true; + break; + } + } + if (!found) { + res1.insert(num1); + } + } + + for (int num2 : nums2) { + bool found = false; + for (int num1 : nums1) { + if (num1 == num2) { + found = true; + break; + } + } + if (!found) { + res2.insert(num2); + } + } + + return {vector(res1.begin(), res1.end()), + vector(res2.begin(), res2.end())}; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums1 + * @param {number[]} nums2 + * @return {number[][]} + */ + findDifference(nums1, nums2) { + const res1 = new Set(); + const res2 = new Set(); + + for (const num1 of nums1) { + let found = false; + for (const num2 of nums2) { + if (num1 === num2) { + found = true; + break; + } + } + if (!found) { + res1.add(num1); + } + } + + for (const num2 of nums2) { + let found = false; + for (const num1 of nums1) { + if (num1 === num2) { + found = true; + break; + } + } + if (!found) { + res2.add(num2); + } + } + + return [Array.from(res1), Array.from(res2)]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * m)$ +* Space complexity: $O(n + m)$ + +> Where $n$ is the size of the array $nums1$ and $m$ is the size of the array $nums2$. + +--- + +## 2. Sorting + +::tabs-start + +```python +class Solution: + def findDifference(self, nums1: list[int], nums2: list[int]) -> list[list[int]]: + nums1.sort() + nums2.sort() + + def helper(A, B): + n, m = len(A), len(B) + res = [] + + j = 0 + prev = float('-inf') + for num in A: + if prev == num: + continue + while j < m and B[j] < num: + j += 1 + if j == m or B[j] != num: + res.append(num) + prev = num + return res + + return [helper(nums1, nums2), helper(nums2, nums1)] +``` + +```java +public class Solution { + public List> findDifference(int[] nums1, int[] nums2) { + Arrays.sort(nums1); + Arrays.sort(nums2); + + List diff1 = helper(nums1, nums2); + List diff2 = helper(nums2, nums1); + + List> result = new ArrayList<>(); + result.add(diff1); + result.add(diff2); + + return result; + } + + private List helper(int[] A, int[] B) { + int n = A.length, m = B.length, j = 0; + List res = new ArrayList<>(); + int prev = Integer.MIN_VALUE; + + for (int num : A) { + if (num == prev) continue; + while (j < m && B[j] < num) j++; + if (j == m || B[j] != num) res.add(num); + prev = num; + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector> findDifference(vector& nums1, vector& nums2) { + sort(nums1.begin(), nums1.end()); + sort(nums2.begin(), nums2.end()); + + return {helper(nums1, nums2), helper(nums2, nums1)}; + } + +private: + vector helper(vector& A, vector& B) { + vector res; + int n = A.size(), m = B.size(), j = 0, prev = INT_MIN; + + for (int num : A) { + if (num == prev) continue; + while (j < m && B[j] < num) j++; + if (j == m || B[j] != num) res.push_back(num); + prev = num; + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums1 + * @param {number[]} nums2 + * @return {number[][]} + */ + findDifference(nums1, nums2) { + nums1.sort((a, b) => a - b); + nums2.sort((a, b) => a - b); + + const helper = (A, B) => { + const res = []; + let j = 0; + let prev = -Infinity; + + for (const num of A) { + if (num === prev) continue; + while (j < B.length && B[j] < num) j++; + if (j === B.length || B[j] !== num) res.push(num); + prev = num; + } + + return res; + }; + + return [helper(nums1, nums2), helper(nums2, nums1)]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n + m \log m)$ +* Space complexity: $O(1)$ or $O(n + m)$ depending on the sorting algorithm. + +> Where $n$ is the size of the array $nums1$ and $m$ is the size of the array $nums2$. + +--- + +## 3. Hash Set + +::tabs-start + +```python +class Solution: + def findDifference(self, nums1: List[int], nums2: List[int]) -> List[List[int]]: + num1Set, num2Set = set(nums1), set(nums2) + res1, res2 = [], [] + + for num in num1Set: + if num not in num2Set: + res1.append(num) + + for num in num2Set: + if num not in num1Set: + res2.append(num) + + return [res1, res2] +``` + +```java +public class Solution { + public List> findDifference(int[] nums1, int[] nums2) { + Set num1Set = new HashSet<>(); + Set num2Set = new HashSet<>(); + for (int num : nums1) num1Set.add(num); + for (int num : nums2) num2Set.add(num); + + List res1 = new ArrayList<>(); + List res2 = new ArrayList<>(); + + for (int num : num1Set) { + if (!num2Set.contains(num)) res1.add(num); + } + + for (int num : num2Set) { + if (!num1Set.contains(num)) res2.add(num); + } + + return Arrays.asList(res1, res2); + } +} +``` + +```cpp +class Solution { +public: + vector> findDifference(vector& nums1, vector& nums2) { + unordered_set num1Set(nums1.begin(), nums1.end()); + unordered_set num2Set(nums2.begin(), nums2.end()); + vector res1, res2; + + for (int num : num1Set) { + if (num2Set.find(num) == num2Set.end()) res1.push_back(num); + } + + for (int num : num2Set) { + if (num1Set.find(num) == num1Set.end()) res2.push_back(num); + } + + return {res1, res2}; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums1 + * @param {number[]} nums2 + * @return {number[][]} + */ + findDifference(nums1, nums2) { + const num1Set = new Set(nums1); + const num2Set = new Set(nums2); + const res1 = []; + const res2 = []; + + for (const num of num1Set) { + if (!num2Set.has(num)) res1.push(num); + } + + for (const num of num2Set) { + if (!num1Set.has(num)) res2.push(num); + } + + return [res1, res2]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n + m)$ +* Space complexity: $O(n + m)$ + +> Where $n$ is the size of the array $nums1$ and $m$ is the size of the array $nums2$. + +--- + +## 4. Hash Set Difference + +::tabs-start + +```python +class Solution: + def findDifference(self, nums1: List[int], nums2: List[int]) -> List[List[int]]: + numSet1, numSet2 = set(nums1), set(nums2) + return [list(numSet1 - numSet2), list(numSet2 - numSet1)] +``` + +```java +public class Solution { + public List> findDifference(int[] nums1, int[] nums2) { + Set numSet1 = new HashSet<>(); + Set numSet2 = new HashSet<>(); + for (int num : nums1) numSet1.add(num); + for (int num : nums2) numSet2.add(num); + + List res1 = new ArrayList<>(numSet1); + res1.removeAll(numSet2); + + List res2 = new ArrayList<>(numSet2); + res2.removeAll(numSet1); + + return Arrays.asList(res1, res2); + } +} +``` + +```cpp +class Solution { +public: + vector> findDifference(vector& nums1, vector& nums2) { + vector res1, res2; + set numSet1(begin(nums1), end(nums1)), numSet2(begin(nums2), end(nums2)); + + set_difference(begin(numSet1), end(numSet1), begin(numSet2), end(numSet2), back_inserter(res1)); + set_difference(begin(numSet2), end(numSet2), begin(numSet1), end(numSet1), back_inserter(res2)); + + return {res1, res2}; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums1 + * @param {number[]} nums2 + * @return {number[][]} + */ + findDifference(nums1, nums2) { + const numSet1 = new Set(nums1); + const numSet2 = new Set(nums2); + + const res1 = Array.from(numSet1).filter(num => !numSet2.has(num)); + const res2 = Array.from(numSet2).filter(num => !numSet1.has(num)); + + return [res1, res2]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n + m)$ +* Space complexity: $O(n + m)$ + +> Where $n$ is the size of the array $nums1$ and $m$ is the size of the array $nums2$. \ No newline at end of file diff --git a/articles/first-missing-positive.md b/articles/first-missing-positive.md new file mode 100644 index 000000000..f0bef0643 --- /dev/null +++ b/articles/first-missing-positive.md @@ -0,0 +1,536 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def firstMissingPositive(self, nums: List[int]) -> int: + missing = 1 + while True: + flag = True + for num in nums: + if missing == num: + flag = False + break + + if flag: + return missing + missing += 1 +``` + +```java +public class Solution { + public int firstMissingPositive(int[] nums) { + int missing = 1; + while (true) { + boolean flag = true; + for (int num : nums) { + if (missing == num) { + flag = false; + break; + } + } + if (flag) return missing; + missing++; + } + } +} +``` + +```cpp +class Solution { +public: + int firstMissingPositive(vector& nums) { + int missing = 1; + while (true) { + bool flag = true; + for (int& num : nums) { + if (missing == num) { + flag = false; + break; + } + } + if (flag) return missing; + missing++; + } + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + firstMissingPositive(nums) { + let missing = 1; + while (true) { + let flag = true; + for (let num of nums) { + if (missing === num) { + flag = false; + break; + } + } + if (flag) return missing; + missing++; + } + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(1)$ + +--- + +## 2. Boolean Array + +::tabs-start + +```python +class Solution: + def firstMissingPositive(self, nums: list[int]) -> int: + n = len(nums) + seen = [False] * n + for num in nums: + if num > 0 and num <= n: + seen[num - 1] = True + + for num in range(1, n + 1): + if not seen[num - 1]: + return num + + return n + 1 +``` + +```java +public class Solution { + public int firstMissingPositive(int[] nums) { + int n = nums.length; + boolean[] seen = new boolean[n]; + + for (int num : nums) { + if (num > 0 && num <= n) { + seen[num - 1] = true; + } + } + + for (int i = 0; i < n; i++) { + if (!seen[i]) { + return i + 1; + } + } + + return n + 1; + } +} +``` + +```cpp +class Solution { +public: + int firstMissingPositive(vector& nums) { + int n = nums.size(); + vector seen(n, false); + + for (int num : nums) { + if (num > 0 && num <= n) { + seen[num - 1] = true; + } + } + + for (int i = 0; i < n; i++) { + if (!seen[i]) { + return i + 1; + } + } + + return n + 1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + firstMissingPositive(nums) { + const n = nums.length; + const seen = new Array(n).fill(false); + + for (const num of nums) { + if (num > 0 && num <= n) { + seen[num - 1] = true; + } + } + + for (let i = 0; i < n; i++) { + if (!seen[i]) { + return i + 1; + } + } + + return n + 1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Sorting + +::tabs-start + +```python +class Solution: + def firstMissingPositive(self, nums: list[int]) -> int: + nums.sort() + missing = 1 + for num in nums: + if num > 0 and missing == num: + missing += 1 + return missing +``` + +```java +public class Solution { + public int firstMissingPositive(int[] nums) { + Arrays.sort(nums); + int missing = 1; + for (int num : nums) { + if (num > 0 && missing == num) { + missing++; + } + } + return missing; + } +} +``` + +```cpp +class Solution { +public: + int firstMissingPositive(vector& nums) { + sort(nums.begin(), nums.end()); + int missing = 1; + for (int num : nums) { + if (num > 0 && missing == num) { + missing++; + } + } + return missing; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + firstMissingPositive(nums) { + nums.sort((a, b) => a - b); + let missing = 1; + for (const num of nums) { + if (num > 0 && missing === num) { + missing++; + } + } + return missing; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(1)$ or $O(n)$ depending on the sorting algorithm. + +--- + +## 4. Negative Marking + +::tabs-start + +```python +class Solution: + def firstMissingPositive(self, nums: list[int]) -> int: + for i in range(len(nums)): + if nums[i] < 0: + nums[i] = 0 + + for i in range(len(nums)): + val = abs(nums[i]) + if 1 <= val <= len(nums): + if nums[val - 1] > 0: + nums[val - 1] *= -1 + elif nums[val - 1] == 0: + nums[val - 1] = -1 * (len(nums) + 1) + + for i in range(1, len(nums) + 1): + if nums[i - 1] >= 0: + return i + + return len(nums) + 1 +``` + +```java +public class Solution { + public int firstMissingPositive(int[] nums) { + int n = nums.length; + + for (int i = 0; i < n; i++) { + if (nums[i] < 0) { + nums[i] = 0; + } + } + + for (int i = 0; i < n; i++) { + int val = Math.abs(nums[i]); + if (val >= 1 && val <= n) { + if (nums[val - 1] > 0) { + nums[val - 1] *= -1; + } else if (nums[val - 1] == 0) { + nums[val - 1] = -1 * (n + 1); + } + } + } + + for (int i = 1; i <= n; i++) { + if (nums[i - 1] >= 0) { + return i; + } + } + + return n + 1; + } +} +``` + +```cpp +class Solution { +public: + int firstMissingPositive(vector& nums) { + int n = nums.size(); + + for (int i = 0; i < n; i++) { + if (nums[i] < 0) { + nums[i] = 0; + } + } + + for (int i = 0; i < n; i++) { + int val = abs(nums[i]); + if (val >= 1 && val <= n) { + if (nums[val - 1] > 0) { + nums[val - 1] *= -1; + } else if (nums[val - 1] == 0) { + nums[val - 1] = -1 * (n + 1); + } + } + } + + for (int i = 1; i <= n; i++) { + if (nums[i - 1] >= 0) { + return i; + } + } + + return n + 1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + firstMissingPositive(nums) { + const n = nums.length; + + for (let i = 0; i < n; i++) { + if (nums[i] < 0) { + nums[i] = 0; + } + } + + for (let i = 0; i < n; i++) { + const val = Math.abs(nums[i]); + if (val >= 1 && val <= n) { + if (nums[val - 1] > 0) { + nums[val - 1] *= -1; + } else if (nums[val - 1] === 0) { + nums[val - 1] = -1 * (n + 1); + } + } + } + + for (let i = 1; i <= n; i++) { + if (nums[i - 1] >= 0) { + return i; + } + } + + return n + 1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. + +--- + +## 5. Cycle Sort + +::tabs-start + +```python +class Solution: + def firstMissingPositive(self, nums: list[int]) -> int: + n = len(nums) + i = 0 + while i < n: + if nums[i] <= 0 or nums[i] > n: + i += 1 + continue + + index = nums[i] - 1 + if nums[i] != nums[index]: + nums[i], nums[index] = nums[index], nums[i] + else: + i += 1 + + for i in range(n): + if nums[i] != i + 1: + return i + 1 + + return n + 1 +``` + +```java +public class Solution { + public int firstMissingPositive(int[] nums) { + int n = nums.length; + int i = 0; + + while (i < n) { + if (nums[i] <= 0 || nums[i] > n) { + i++; + continue; + } + int index = nums[i] - 1; + if (nums[i] != nums[index]) { + int temp = nums[i]; + nums[i] = nums[index]; + nums[index] = temp; + } else { + i++; + } + } + + for (i = 0; i < n; i++) { + if (nums[i] != i + 1) { + return i + 1; + } + } + + return n + 1; + } +} +``` + +```cpp +class Solution { +public: + int firstMissingPositive(vector& nums) { + int n = nums.size(); + int i = 0; + + while (i < n) { + if (nums[i] <= 0 || nums[i] > n) { + i++; + continue; + } + int index = nums[i] - 1; + if (nums[i] != nums[index]) { + swap(nums[i], nums[index]); + } else { + i++; + } + } + + for (i = 0; i < n; i++) { + if (nums[i] != i + 1) { + return i + 1; + } + } + + return n + 1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + firstMissingPositive(nums) { + let n = nums.length; + let i = 0; + while (i < n) { + if (nums[i] <= 0 || nums[i] > n) { + i++; + continue; + } + let index = nums[i] - 1; + if (nums[i] != nums[index]) { + [nums[i], nums[index]] = [nums[index], nums[i]]; + } else { + i++; + } + } + + for (let i = 0; i < n; i++) { + if (nums[i] != i + 1) { + return i + 1; + } + } + + return n + 1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. \ No newline at end of file diff --git a/articles/insert-delete-getrandom-o1.md b/articles/insert-delete-getrandom-o1.md new file mode 100644 index 000000000..000979641 --- /dev/null +++ b/articles/insert-delete-getrandom-o1.md @@ -0,0 +1,300 @@ +## 1. Hash Map + +::tabs-start + +```python +class RandomizedSet: + + def __init__(self): + self.numMap = {} + self.size = 0 + + def insert(self, val: int) -> bool: + if val in self.numMap: + return False + self.numMap[val] = 1 + self.size += 1 + return True + + def remove(self, val: int) -> bool: + if val not in self.numMap: + return False + del self.numMap[val] + self.size -= 1 + return True + + def getRandom(self) -> int: + idx = random.randint(0, self.size - 1) + return list(self.numMap.keys())[idx] +``` + +```java +public class RandomizedSet { + private HashMap numMap; + private int size; + + public RandomizedSet() { + numMap = new HashMap<>(); + size = 0; + } + + public boolean insert(int val) { + if (numMap.containsKey(val)) { + return false; + } + numMap.put(val, 1); + size++; + return true; + } + + public boolean remove(int val) { + if (!numMap.containsKey(val)) { + return false; + } + numMap.remove(val); + size--; + return true; + } + + public int getRandom() { + int idx = new Random().nextInt(size); + Iterator it = numMap.keySet().iterator(); + while (idx-- > 0) { + it.next(); + } + return it.next(); + } +} +``` + +```cpp +class RandomizedSet { +private: + unordered_map numMap; + int size; + +public: + RandomizedSet() : size(0) {} + + bool insert(int val) { + if (numMap.count(val)) return false; + numMap[val] = 1; + size++; + return true; + } + + bool remove(int val) { + if (!numMap.count(val)) return false; + numMap.erase(val); + size--; + return true; + } + + int getRandom() { + int idx = rand() % size; + auto it = numMap.begin(); + advance(it, idx); + return it->first; + } +}; +``` + +```javascript +class RandomizedSet { + constructor() { + this.numMap = new Map(); + this.size = 0; + } + + /** + * @param {number} val + * @return {boolean} + */ + insert(val) { + if (this.numMap.has(val)) return false; + this.numMap.set(val, 1); + this.size++; + return true; + } + + /** + * @param {number} val + * @return {boolean} + */ + remove(val) { + if (!this.numMap.has(val)) return false; + this.numMap.delete(val); + this.size--; + return true; + } + + /** + * @return {number} + */ + getRandom() { + const keys = Array.from(this.numMap.keys()); + const idx = Math.floor(Math.random() * this.size); + return keys[idx]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ for $getRandom()$, $O(1)$ for other function calls. +* Space complexity: $O(n)$ + +--- + +## 2. Hash Map + List + +::tabs-start + +```python +class RandomizedSet: + + def __init__(self): + self.numMap = {} + self.nums = [] + + def insert(self, val: int) -> bool: + if val in self.numMap: + return False + self.numMap[val] = len(self.nums) + self.nums.append(val) + return True + + def remove(self, val: int) -> bool: + if val not in self.numMap: + return False + idx = self.numMap[val] + last = self.nums[-1] + self.nums[idx] = last + self.numMap[last] = idx + self.nums.pop() + del self.numMap[val] + return True + + def getRandom(self) -> int: + return random.choice(self.nums) +``` + +```java +public class RandomizedSet { + private Map numMap; + private List nums; + private Random rand; + + public RandomizedSet() { + numMap = new HashMap<>(); + nums = new ArrayList<>(); + rand = new Random(); + } + + public boolean insert(int val) { + if (numMap.containsKey(val)) return false; + numMap.put(val, nums.size()); + nums.add(val); + return true; + } + + public boolean remove(int val) { + if (!numMap.containsKey(val)) return false; + int idx = numMap.get(val); + int last = nums.get(nums.size() - 1); + nums.set(idx, last); + numMap.put(last, idx); + nums.remove(nums.size() - 1); + numMap.remove(val); + return true; + } + + public int getRandom() { + return nums.get(rand.nextInt(nums.size())); + } +} +``` + +```cpp +class RandomizedSet { +private: + unordered_map numMap; + vector nums; + +public: + RandomizedSet() {} + + bool insert(int val) { + if (numMap.count(val)) return false; + numMap[val] = nums.size(); + nums.push_back(val); + return true; + } + + bool remove(int val) { + if (!numMap.count(val)) return false; + int idx = numMap[val]; + int last = nums.back(); + nums[idx] = last; + numMap[last] = idx; + nums.pop_back(); + numMap.erase(val); + return true; + } + + int getRandom() { + return nums[rand() % nums.size()]; + } +}; +``` + +```javascript +class RandomizedSet { + constructor() { + this.numMap = new Map(); + this.nums = []; + } + + /** + * @param {number} val + * @return {boolean} + */ + insert(val) { + if (this.numMap.has(val)) return false; + this.numMap.set(val, this.nums.length); + this.nums.push(val); + return true; + } + + /** + * @param {number} val + * @return {boolean} + */ + remove(val) { + if (!this.numMap.has(val)) return false; + const idx = this.numMap.get(val); + const last = this.nums[this.nums.length - 1]; + this.nums[idx] = last; + this.numMap.set(last, idx); + this.nums.pop(); + this.numMap.delete(val); + return true; + } + + /** + * @return {number} + */ + getRandom() { + return this.nums[Math.floor(Math.random() * this.nums.length)]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(1)$ for each function call. +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/majority-element-ii.md b/articles/majority-element-ii.md new file mode 100644 index 000000000..6ba0f2fd1 --- /dev/null +++ b/articles/majority-element-ii.md @@ -0,0 +1,597 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def majorityElement(self, nums: List[int]) -> List[int]: + res = set() + for num in nums: + count = sum(1 for i in nums if i == num) + if count > len(nums) // 3: + res.add(num) + return list(res) +``` + +```java +public class Solution { + public List majorityElement(int[] nums) { + Set res = new HashSet<>(); + for (int num : nums) { + int count = 0; + for (int i : nums) { + if (i == num) count++; + } + if (count > nums.length / 3) { + res.add(num); + } + } + return new ArrayList<>(res); + } +} +``` + +```cpp +class Solution { +public: + vector majorityElement(vector& nums) { + unordered_set res; + for (int num : nums) { + int count = 0; + for (int i : nums) { + if (i == num) count++; + } + if (count > nums.size() / 3) { + res.insert(num); + } + } + return vector(res.begin(), res.end()); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number[]} + */ + majorityElement(nums) { + const res = new Set(); + for (const num of nums) { + let count = 0; + for (const i of nums) { + if (i === num) count++; + } + if (count > Math.floor(nums.length / 3)) { + res.add(num); + } + } + return Array.from(res); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(1)$ since output array size will be at most $2$. + +--- + +## 2. Sorting + +::tabs-start + +```python +class Solution: + def majorityElement(self, nums: List[int]) -> List[int]: + nums.sort() + res, n = [], len(nums) + + i = 0 + while i < n: + j = i + 1 + while j < n and nums[i] == nums[j]: + j += 1 + if (j - i) > n // 3: + res.append(nums[i]) + i = j + + return res +``` + +```java +public class Solution { + public List majorityElement(int[] nums) { + Arrays.sort(nums); + List res = new ArrayList<>(); + int n = nums.length; + + int i = 0; + while (i < n) { + int j = i + 1; + while (j < n && nums[i] == nums[j]) { + j++; + } + if (j - i > n / 3) { + res.add(nums[i]); + } + i = j; + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector majorityElement(vector& nums) { + sort(nums.begin(), nums.end()); + vector res; + int n = nums.size(); + + int i = 0; + while (i < n) { + int j = i + 1; + while (j < n && nums[i] == nums[j]) { + j++; + } + if (j - i > n / 3) { + res.push_back(nums[i]); + } + i = j; + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number[]} + */ + majorityElement(nums) { + nums.sort((a, b) => a - b); + const res = []; + const n = nums.length; + + let i = 0; + while (i < n) { + let j = i + 1; + while (j < n && nums[i] === nums[j]) { + j++; + } + if (j - i > Math.floor(n / 3)) { + res.push(nums[i]); + } + i = j; + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(1)$ or $O(n)$ depending on the sorting algorithm. + +--- + +## 3. Frequency Count + +::tabs-start + +```python +class Solution: + def majorityElement(self, nums: List[int]) -> List[int]: + count = Counter(nums) + res = [] + + for key in count: + if count[key] > len(nums) // 3: + res.append(key) + + return res +``` + +```java +public class Solution { + public List majorityElement(int[] nums) { + Map count = new HashMap<>(); + for (int num : nums) { + count.put(num, count.getOrDefault(num, 0) + 1); + } + + List res = new ArrayList<>(); + for (int key : count.keySet()) { + if (count.get(key) > nums.length / 3) { + res.add(key); + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector majorityElement(vector& nums) { + unordered_map count; + for (int num : nums) { + count[num]++; + } + + vector res; + for (auto& pair : count) { + if (pair.second > nums.size() / 3) { + res.push_back(pair.first); + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number[]} + */ + majorityElement(nums) { + const count = new Map(); + for (const num of nums) { + count.set(num, (count.get(num) || 0) + 1); + } + + const res = []; + for (const [key, value] of count.entries()) { + if (value > Math.floor(nums.length / 3)) { + res.push(key); + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 4. Boyer-Moore Voting Algorithm + +::tabs-start + +```python +class Solution: + def majorityElement(self, nums: List[int]) -> List[int]: + n = len(nums) + num1 = num2 = -1 + cnt1 = cnt2 = 0 + + for num in nums: + if num == num1: + cnt1 += 1 + elif num == num2: + cnt2 += 1 + elif cnt1 == 0: + cnt1 = 1 + num1 = num + elif cnt2 == 0: + cnt2 = 1 + num2 = num + else: + cnt1 -= 1 + cnt2 -= 1 + + cnt1 = cnt2 = 0 + for num in nums: + if num == num1: + cnt1 += 1 + elif num == num2: + cnt2 += 1 + + res = [] + if cnt1 > n // 3: + res.append(num1) + if cnt2 > n // 3: + res.append(num2) + + return res +``` + +```java +public class Solution { + public List majorityElement(int[] nums) { + int n = nums.length; + int num1 = -1, num2 = -1, cnt1 = 0, cnt2 = 0; + + for (int num : nums) { + if (num == num1) { + cnt1++; + } else if (num == num2) { + cnt2++; + } else if (cnt1 == 0) { + cnt1 = 1; + num1 = num; + } else if (cnt2 == 0) { + cnt2 = 1; + num2 = num; + } else { + cnt1--; + cnt2--; + } + } + + cnt1 = cnt2 = 0; + for (int num : nums) { + if (num == num1) { + cnt1++; + } else if (num == num2) { + cnt2++; + } + } + + List res = new ArrayList<>(); + if (cnt1 > n / 3) res.add(num1); + if (cnt2 > n / 3) res.add(num2); + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector majorityElement(vector& nums) { + int n = nums.size(); + int num1 = -1, num2 = -1, cnt1 = 0, cnt2 = 0; + + for (int num : nums) { + if (num == num1) { + cnt1++; + } else if (num == num2) { + cnt2++; + } else if (cnt1 == 0) { + num1 = num; + cnt1 = 1; + } else if (cnt2 == 0) { + num2 = num; + cnt2 = 1; + } else { + cnt1--; + cnt2--; + } + } + + cnt1 = cnt2 = 0; + for (int num : nums) { + if (num == num1) cnt1++; + else if (num == num2) cnt2++; + } + + vector res; + if (cnt1 > n / 3) res.push_back(num1); + if (cnt2 > n / 3) res.push_back(num2); + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number[]} + */ + majorityElement(nums) { + const n = nums.length; + let num1 = -1, num2 = -1, cnt1 = 0, cnt2 = 0; + + for (const num of nums) { + if (num === num1) { + cnt1++; + } else if (num === num2) { + cnt2++; + } else if (cnt1 === 0) { + cnt1 = 1; + num1 = num; + } else if (cnt2 === 0) { + cnt2 = 1; + num2 = num; + } else { + cnt1--; + cnt2--; + } + } + + cnt1 = cnt2 = 0; + for (const num of nums) { + if (num === num1) cnt1++; + else if (num === num2) cnt2++; + } + + const res = []; + if (cnt1 > Math.floor(n / 3)) res.push(num1); + if (cnt2 > Math.floor(n / 3)) res.push(num2); + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ since output array size will be at most $2$. + +--- + +## 5. Boyer-Moore Voting Algorithm (Hash Map) + +::tabs-start + +```python +class Solution: + def majorityElement(self, nums: List[int]) -> List[int]: + count = defaultdict(int) + + for num in nums: + count[num] += 1 + + if len(count) <= 2: + continue + + new_count = defaultdict(int) + for num, c in count.items(): + if c > 1: + new_count[num] = c - 1 + count = new_count + + res = [] + for num in count: + if nums.count(num) > len(nums) // 3: + res.append(num) + + return res +``` + +```java +public class Solution { + public List majorityElement(int[] nums) { + Map count = new HashMap<>(); + + for (int num : nums) { + count.put(num, count.getOrDefault(num, 0) + 1); + + if (count.size() > 2) { + Map newCount = new HashMap<>(); + for (Map.Entry entry : count.entrySet()) { + if (entry.getValue() > 1) { + newCount.put(entry.getKey(), entry.getValue() - 1); + } + } + count = newCount; + } + } + + List res = new ArrayList<>(); + for (int key : count.keySet()) { + int frequency = 0; + for (int num : nums) { + if (num == key) frequency++; + } + if (frequency > nums.length / 3) { + res.add(key); + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector majorityElement(vector& nums) { + unordered_map count; + + for (int num : nums) { + count[num]++; + + if (count.size() > 2) { + unordered_map newCount; + for (auto& entry : count) { + if (entry.second > 1) { + newCount[entry.first] = entry.second - 1; + } + } + count = newCount; + } + } + + vector res; + for (auto& entry : count) { + int frequency = 0; + for (int num : nums) { + if (num == entry.first) frequency++; + } + if (frequency > nums.size() / 3) { + res.push_back(entry.first); + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number[]} + */ + majorityElement(nums) { + let count = new Map(); + + for (const num of nums) { + count.set(num, (count.get(num) || 0) + 1); + + if (count.size > 2) { + const newCount = new Map(); + for (const [key, value] of count.entries()) { + if (value > 1) { + newCount.set(key, value - 1); + } + } + count = newCount; + } + } + + const res = []; + for (const [key] of count.entries()) { + const frequency = nums.filter(num => num === key).length; + if (frequency > Math.floor(nums.length / 3)) { + res.push(key); + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ since output array size will be at most $2$. \ No newline at end of file diff --git a/articles/minimum-number-of-operations-to-make-array-empty.md b/articles/minimum-number-of-operations-to-make-array-empty.md new file mode 100644 index 000000000..fbb2aa367 --- /dev/null +++ b/articles/minimum-number-of-operations-to-make-array-empty.md @@ -0,0 +1,563 @@ +## 1. Recursion + +::tabs-start + +```python +class Solution: + def minOperations(self, nums: List[int]) -> int: + def dfs(cur): + if cur < 0: + return float('inf') + if cur == 0: + return 0 + + ops = min(dfs(cur - 2), dfs(cur - 3)) + return 1 + ops + + count = Counter(nums) + res = 0 + for num, cnt in count.items(): + op = dfs(cnt) + if op == float("inf"): + return -1 + res += op + + return res +``` + +```java +public class Solution { + public int minOperations(int[] nums) { + Map count = new HashMap<>(); + for (int num : nums) { + count.put(num, count.getOrDefault(num, 0) + 1); + } + + int res = 0; + for (int cnt : count.values()) { + int op = dfs(cnt); + if (op == Integer.MAX_VALUE) { + return -1; + } + res += op; + } + + return res; + } + + private int dfs(int cur) { + if (cur < 0) { + return Integer.MAX_VALUE; + } + if (cur == 0) { + return 0; + } + + int ops = Math.min(dfs(cur - 2), dfs(cur - 3)); + return ops == Integer.MAX_VALUE ? ops : 1 + ops; + } +} +``` + +```cpp +class Solution { +public: + int minOperations(vector& nums) { + unordered_map count; + for (int num : nums) { + count[num]++; + } + + int res = 0; + for (auto& [num, cnt] : count) { + int op = dfs(cnt); + if (op == INT_MAX) { + return -1; + } + res += op; + } + + return res; + } + +private: + int dfs(int cur) { + if (cur < 0) { + return INT_MAX; + } + if (cur == 0) { + return 0; + } + + int ops = min(dfs(cur - 2), dfs(cur - 3)); + return ops == INT_MAX ? ops : 1 + ops; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + minOperations(nums) { + const dfs = (cur) => { + if (cur < 0) { + return Infinity; + } + if (cur === 0) { + return 0; + } + + const ops = Math.min(dfs(cur - 2), dfs(cur - 3)); + return isFinite(ops) ? 1 + ops : ops; + }; + + const count = new Map(); + for (const num of nums) { + count.set(num, (count.get(num) || 0) + 1); + } + + let res = 0; + for (const cnt of count.values()) { + const op = dfs(cnt); + if (op === Infinity) { + return -1; + } + res += op; + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * 2 ^ m)$ +* Space complexity: $O(n + m)$ + +> Where $n$ is the size of the array $nums$ and $m$ is the average frequency of the array elements. + +--- + +## 2. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def minOperations(self, nums: List[int]) -> int: + cache = {} + + def dfs(num): + if num < 0: + return float("inf") + if num in [2, 3]: + return 1 + if num in cache: + return cache[num] + + res = min(dfs(num - 2), dfs(num - 3)) + cache[num] = res + 1 + return cache[num] + + count = Counter(nums) + res = 0 + for num, cnt in count.items(): + op = dfs(cnt) + if op == float("inf"): + return -1 + res += op + + return res +``` + +```java +public class Solution { + private Map cache; + + public int minOperations(int[] nums) { + Map count = new HashMap<>(); + for (int num : nums) { + count.put(num, count.getOrDefault(num, 0) + 1); + } + + cache = new HashMap<>(); + int res = 0; + for (int cnt : count.values()) { + int op = dfs(cnt); + if (op == Integer.MAX_VALUE) { + return -1; + } + res += op; + } + + return res; + } + + private int dfs(int cur) { + if (cur < 0) { + return Integer.MAX_VALUE; + } + if (cur == 2 || cur == 3) { + return 1; + } + if (cache.containsKey(cur)) { + return cache.get(cur); + } + + int res = Math.min(dfs(cur - 2), dfs(cur - 3)); + cache.put(cur, res == Integer.MAX_VALUE ? res : res + 1); + return cache.get(cur); + } +} +``` + +```cpp +class Solution { + unordered_map cache; +public: + int minOperations(vector& nums) { + unordered_map count; + for (int num : nums) { + count[num]++; + } + + int res = 0; + for (auto& [num, cnt] : count) { + int op = dfs(cnt); + if (op == INT_MAX) { + return -1; + } + res += op; + } + + return res; + } + +private: + int dfs(int cur) { + if (cur < 0) { + return INT_MAX; + } + if (cur == 2 || cur == 3) { + return 1; + } + if (cache.count(cur)) { + return cache[cur]; + } + + int res = min(dfs(cur - 2), dfs(cur - 3)); + cache[cur] = res == INT_MAX ? res : res + 1; + return cache[cur]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + minOperations(nums) { + const cache = new Map(); + const dfs = (cur) => { + if (cur < 0) { + return Infinity; + } + if (cur === 2 || cur === 3) { + return 1; + } + if (cache.has(cur)) { + return cache.get(cur); + } + + const res = Math.min(dfs(cur - 2), dfs(cur - 3)); + cache.set(cur, isFinite(res) ? 1 + res : res); + return cache.get(cur); + }; + + const count = new Map(); + for (const num of nums) { + count.set(num, (count.get(num) || 0) + 1); + } + + let res = 0; + for (const cnt of count.values()) { + const op = dfs(cnt); + if (!isFinite(op)) { + return -1; + } + res += op; + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n + m)$ +* Space complexity: $O(n + m)$ + +> Where $n$ is the size of the array $nums$ and $m$ is the average frequency of the array elements. + +--- + +## 3. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def minOperations(self, nums: List[int]) -> int: + count = Counter(nums) + maxf = max(count.values()) + minOps = [0] * (maxf + 1) + minOps[1] = float("inf") + + for i in range(2, maxf + 1): + minOps[i] = minOps[i - 2] + if i - 3 >= 0: + minOps[i] = min(minOps[i], minOps[i - 3]) + if minOps[i] != float("inf"): + minOps[i] += 1 + + res = 0 + for num, cnt in count.items(): + op = minOps[cnt] + if op == float("inf"): + return -1 + res += op + + return res +``` + +```java +public class Solution { + public int minOperations(int[] nums) { + Map count = new HashMap<>(); + for (int num : nums) { + count.put(num, count.getOrDefault(num, 0) + 1); + } + + int maxf = count.values().stream().max(Integer::compare).orElse(0); + int[] minOps = new int[maxf + 1]; + minOps[1] = Integer.MAX_VALUE; + + for (int i = 2; i <= maxf; i++) { + minOps[i] = minOps[i - 2]; + if (i - 3 >= 0) { + minOps[i] = Math.min(minOps[i], minOps[i - 3]); + } + if (minOps[i] != Integer.MAX_VALUE) { + minOps[i] += 1; + } + } + + int res = 0; + for (int cnt : count.values()) { + int op = minOps[cnt]; + if (op == Integer.MAX_VALUE) { + return -1; + } + res += op; + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int minOperations(vector& nums) { + unordered_map count; + for (int num : nums) { + count[num]++; + } + + int maxf = 0; + for (auto& [num, freq] : count) { + maxf = max(maxf, freq); + } + + vector minOps(maxf + 1, 0); + minOps[1] = INT_MAX; + + for (int i = 2; i <= maxf; i++) { + minOps[i] = minOps[i - 2]; + if (i - 3 >= 0) { + minOps[i] = min(minOps[i], minOps[i - 3]); + } + if (minOps[i] != INT_MAX) { + minOps[i] += 1; + } + } + + int res = 0; + for (auto& [num, cnt] : count) { + int op = minOps[cnt]; + if (op == INT_MAX) { + return -1; + } + res += op; + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + minOperations(nums) { + const count = new Map(); + for (const num of nums) { + count.set(num, (count.get(num) || 0) + 1); + } + + const maxf = Math.max(...count.values()); + const minOps = Array(maxf + 1).fill(0); + minOps[1] = Infinity; + + for (let i = 2; i <= maxf; i++) { + minOps[i] = minOps[i - 2]; + if (i - 3 >= 0) { + minOps[i] = Math.min(minOps[i], minOps[i - 3]); + } + if (minOps[i] !== Infinity) { + minOps[i] += 1; + } + } + + let res = 0; + for (const cnt of count.values()) { + const op = minOps[cnt]; + if (op === Infinity) { + return -1; + } + res += op; + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n + m)$ +* Space complexity: $O(n + m)$ + +> Where $n$ is the size of the array $nums$ and $m$ is the average frequency of the array elements. + +--- + +## 4. Greedy + +::tabs-start + +```python +class Solution: + def minOperations(self, nums: List[int]) -> int: + count = Counter(nums) + res = 0 + + for num, cnt in count.items(): + if cnt == 1: + return -1 + res += math.ceil(cnt / 3) + + return res +``` + +```java +public class Solution { + public int minOperations(int[] nums) { + Map count = new HashMap<>(); + for (int num : nums) { + count.put(num, count.getOrDefault(num, 0) + 1); + } + + int res = 0; + for (int cnt : count.values()) { + if (cnt == 1) { + return -1; + } + res += (cnt + 2) / 3; + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int minOperations(vector& nums) { + unordered_map count; + for (int num : nums) { + count[num]++; + } + + int res = 0; + for (auto& [num, cnt] : count) { + if (cnt == 1) { + return -1; + } + res += (cnt + 2) / 3; + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + minOperations(nums) { + const count = new Map(); + for (const num of nums) { + count.set(num, (count.get(num) || 0) + 1); + } + + let res = 0; + for (const cnt of count.values()) { + if (cnt === 1) { + return -1; + } + res += Math.ceil(cnt / 3); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/minimum-penalty-for-a-shop.md b/articles/minimum-penalty-for-a-shop.md new file mode 100644 index 000000000..a3e506a88 --- /dev/null +++ b/articles/minimum-penalty-for-a-shop.md @@ -0,0 +1,497 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def bestClosingTime(self, customers: str) -> int: + n = len(customers) + res = n + minPenalty = n + + for i in range(n + 1): + penalty = 0 + for j in range(i): + if customers[j] == 'N': + penalty += 1 + for j in range(i, n): + if customers[j] == 'Y': + penalty += 1 + + if penalty < minPenalty: + minPenalty = penalty + res = i + + return res +``` + +```java +public class Solution { + public int bestClosingTime(String customers) { + int n = customers.length(); + int res = n, minPenalty = n; + + for (int i = 0; i <= n; i++) { + int penalty = 0; + for (int j = 0; j < i; j++) { + if (customers.charAt(j) == 'N') { + penalty++; + } + } + for (int j = i; j < n; j++) { + if (customers.charAt(j) == 'Y') { + penalty++; + } + } + + if (penalty < minPenalty) { + minPenalty = penalty; + res = i; + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int bestClosingTime(string customers) { + int n = customers.size(); + int res = n, minPenalty = n; + + for (int i = 0; i <= n; i++) { + int penalty = 0; + for (int j = 0; j < i; j++) { + if (customers[j] == 'N') { + penalty++; + } + } + for (int j = i; j < n; j++) { + if (customers[j] == 'Y') { + penalty++; + } + } + + if (penalty < minPenalty) { + minPenalty = penalty; + res = i; + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} customers + * @return {number} + */ + bestClosingTime(customers) { + const n = customers.length; + let res = n, minPenalty = n; + + for (let i = 0; i <= n; i++) { + let penalty = 0; + for (let j = 0; j < i; j++) { + if (customers[j] === 'N') { + penalty++; + } + } + for (let j = i; j < n; j++) { + if (customers[j] === 'Y') { + penalty++; + } + } + + if (penalty < minPenalty) { + minPenalty = penalty; + res = i; + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(1)$ + +--- + +## 2. Prefix & Suffix + +::tabs-start + +```python +class Solution: + def bestClosingTime(self, customers: str) -> int: + n = len(customers) + cnt = 0 + + prefixN = [] + for c in customers: + prefixN.append(cnt) + if c == 'N': + cnt += 1 + prefixN.append(cnt) + + suffixY = [0] * (n + 1) + for i in range(n - 1, -1, -1): + suffixY[i] = suffixY[i + 1] + if customers[i] == 'Y': + suffixY[i] += 1 + + res = n + minPenalty = n + for i in range(n + 1): + penalty = prefixN[i] + suffixY[i] + if penalty < minPenalty: + minPenalty = penalty + res = i + + return res +``` + +```java +public class Solution { + public int bestClosingTime(String customers) { + int n = customers.length(); + int cnt = 0; + + int[] prefixN = new int[n + 1]; + for (int i = 0; i < n; i++) { + prefixN[i] = cnt; + if (customers.charAt(i) == 'N') { + cnt++; + } + } + prefixN[n] = cnt; + + int[] suffixY = new int[n + 1]; + for (int i = n - 1; i >= 0; i--) { + suffixY[i] = suffixY[i + 1]; + if (customers.charAt(i) == 'Y') { + suffixY[i]++; + } + } + + int res = n, minPenalty = n; + for (int i = 0; i <= n; i++) { + int penalty = prefixN[i] + suffixY[i]; + if (penalty < minPenalty) { + minPenalty = penalty; + res = i; + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int bestClosingTime(string customers) { + int n = customers.size(), cnt = 0; + + vector prefixN(n + 1); + for (int i = 0; i < n; i++) { + prefixN[i] = cnt; + if (customers[i] == 'N') { + cnt++; + } + } + prefixN[n] = cnt; + + vector suffixY(n + 1, 0); + for (int i = n - 1; i >= 0; i--) { + suffixY[i] = suffixY[i + 1]; + if (customers[i] == 'Y') { + suffixY[i]++; + } + } + + int res = n, minPenalty = n; + for (int i = 0; i <= n; i++) { + int penalty = prefixN[i] + suffixY[i]; + if (penalty < minPenalty) { + minPenalty = penalty; + res = i; + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} customers + * @return {number} + */ + bestClosingTime(customers) { + const n = customers.length; + let cnt = 0; + + const prefixN = []; + for (const c of customers) { + prefixN.push(cnt); + if (c === 'N') { + cnt++; + } + } + prefixN.push(cnt); + + const suffixY = new Array(n + 1).fill(0); + for (let i = n - 1; i >= 0; i--) { + suffixY[i] = suffixY[i + 1]; + if (customers[i] === 'Y') { + suffixY[i]++; + } + } + + let res = n, minPenalty = n; + for (let i = 0; i <= n; i++) { + const penalty = prefixN[i] + suffixY[i]; + if (penalty < minPenalty) { + minPenalty = penalty; + res = i; + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Iteration (Two Pass) + +::tabs-start + +```python +class Solution: + def bestClosingTime(self, customers: str) -> int: + cntY = sum(c == "Y" for c in customers) + + minPenalty = cntY + res = cntN = 0 + for i, c in enumerate(customers): + if c == "Y": + cntY -= 1 + else: + cntN += 1 + + penalty = cntN + cntY + if penalty < minPenalty: + res = i + 1 + minPenalty = penalty + + return res +``` + +```java +public class Solution { + public int bestClosingTime(String customers) { + int cntY = 0; + for (char c : customers.toCharArray()) { + if (c == 'Y') cntY++; + } + + int minPenalty = cntY, res = 0, cntN = 0; + for (int i = 0; i < customers.length(); i++) { + if (customers.charAt(i) == 'Y') { + cntY--; + } else { + cntN++; + } + + int penalty = cntN + cntY; + if (penalty < minPenalty) { + res = i + 1; + minPenalty = penalty; + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int bestClosingTime(string customers) { + int cntY = count(customers.begin(), customers.end(), 'Y'); + + int minPenalty = cntY, res = 0, cntN = 0; + for (int i = 0; i < customers.size(); i++) { + if (customers[i] == 'Y') { + cntY--; + } else { + cntN++; + } + + int penalty = cntN + cntY; + if (penalty < minPenalty) { + res = i + 1; + minPenalty = penalty; + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} customers + * @return {number} + */ + bestClosingTime(customers) { + let cntY = 0; + for (let c of customers) { + if (c === 'Y') cntY++; + } + + let minPenalty = cntY, res = 0, cntN = 0; + for (let i = 0; i < customers.length; i++) { + if (customers[i] === 'Y') { + cntY--; + } else { + cntN++; + } + + const penalty = cntN + cntY; + if (penalty < minPenalty) { + res = i + 1; + minPenalty = penalty; + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ + +--- + +## 4. Iteration (One Pass) + +::tabs-start + +```python +class Solution: + def bestClosingTime(self, customers: str) -> int: + res = minPenalty = 0 + penalty = 0 + + for i, c in enumerate(customers): + penalty += 1 if c == 'Y' else -1 + + if penalty > minPenalty: + minPenalty = penalty + res = i + 1 + + return res +``` + +```java +public class Solution { + public int bestClosingTime(String customers) { + int res = 0, minPenalty = 0, penalty = 0; + + for (int i = 0; i < customers.length(); i++) { + penalty += customers.charAt(i) == 'Y' ? 1 : -1; + + if (penalty > minPenalty) { + minPenalty = penalty; + res = i + 1; + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int bestClosingTime(string customers) { + int res = 0, minPenalty = 0, penalty = 0; + + for (int i = 0; i < customers.size(); i++) { + penalty += customers[i] == 'Y' ? 1 : -1; + + if (penalty > minPenalty) { + minPenalty = penalty; + res = i + 1; + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} customers + * @return {number} + */ + bestClosingTime(customers) { + let res = 0, minPenalty = 0, penalty = 0; + + for (let i = 0; i < customers.length; i++) { + penalty += customers[i] === 'Y' ? 1 : -1; + + if (penalty > minPenalty) { + minPenalty = penalty; + res = i + 1; + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/non-decreasing-array.md b/articles/non-decreasing-array.md new file mode 100644 index 000000000..aed007f6c --- /dev/null +++ b/articles/non-decreasing-array.md @@ -0,0 +1,105 @@ +## 1. Greedy + +::tabs-start + +```python +class Solution: + def checkPossibility(self, nums: list[int]) -> bool: + changed = False + + for i in range(len(nums) - 1): + if nums[i] <= nums[i + 1]: + continue + if changed: + return False + if i == 0 or nums[i + 1] >= nums[i - 1]: + nums[i] = nums[i + 1] + else: + nums[i + 1] = nums[i] + changed = True + return True +``` + +```java +public class Solution { + public boolean checkPossibility(int[] nums) { + boolean changed = false; + + for (int i = 0; i < nums.length - 1; i++) { + if (nums[i] <= nums[i + 1]) { + continue; + } + if (changed) { + return false; + } + if (i == 0 || nums[i + 1] >= nums[i - 1]) { + nums[i] = nums[i + 1]; + } else { + nums[i + 1] = nums[i]; + } + changed = true; + } + return true; + } +} +``` + +```cpp +class Solution { +public: + bool checkPossibility(vector& nums) { + bool changed = false; + + for (int i = 0; i < nums.size() - 1; i++) { + if (nums[i] <= nums[i + 1]) { + continue; + } + if (changed) { + return false; + } + if (i == 0 || nums[i + 1] >= nums[i - 1]) { + nums[i] = nums[i + 1]; + } else { + nums[i + 1] = nums[i]; + } + changed = true; + } + return true; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {boolean} + */ + checkPossibility(nums) { + let changed = false; + + for (let i = 0; i < nums.length - 1; i++) { + if (nums[i] <= nums[i + 1]) { + continue; + } + if (changed) { + return false; + } + if (i === 0 || nums[i + 1] >= nums[i - 1]) { + nums[i] = nums[i + 1]; + } else { + nums[i + 1] = nums[i]; + } + changed = true; + } + return true; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/number-of-zero-filled-subarrays.md b/articles/number-of-zero-filled-subarrays.md new file mode 100644 index 000000000..266353c04 --- /dev/null +++ b/articles/number-of-zero-filled-subarrays.md @@ -0,0 +1,338 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def zeroFilledSubarray(self, nums: List[int]) -> int: + res = 0 + for i in range(len(nums)): + for j in range(i, len(nums)): + if nums[j] != 0: + break + res += 1 + return res +``` + +```java +public class Solution { + public long zeroFilledSubarray(int[] nums) { + long res = 0; + for (int i = 0; i < nums.length; i++) { + for (int j = i; j < nums.length; j++) { + if (nums[j] != 0) break; + res++; + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + long long zeroFilledSubarray(vector& nums) { + long long res = 0; + for (int i = 0; i < nums.size(); i++) { + for (int j = i; j < nums.size(); j++) { + if (nums[j] != 0) break; + res++; + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + zeroFilledSubarray(nums) { + let res = 0; + for (let i = 0; i < nums.length; i++) { + for (let j = i; j < nums.length; j++) { + if (nums[j] != 0) break; + res++; + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(1)$ + +--- + +## 2. Count Consecutive Zeros - I + +::tabs-start + +```python +class Solution: + def zeroFilledSubarray(self, nums: List[int]) -> int: + res = i = 0 + while i < len(nums): + count = 0 + while i < len(nums) and nums[i] == 0: + count += 1 + i += 1 + res += count + i += 1 + return res +``` + +```java +public class Solution { + public long zeroFilledSubarray(int[] nums) { + long res = 0; + int i = 0; + while (i < nums.length) { + long count = 0; + while (i < nums.length && nums[i] == 0) { + count++; + i++; + res += count; + } + i++; + } + return res; + } +} +``` + +```cpp +class Solution { +public: + long long zeroFilledSubarray(vector& nums) { + long long res = 0; + int i = 0; + while (i < nums.size()) { + long long count = 0; + while (i < nums.size() && nums[i] == 0) { + count++; + i++; + res += count; + } + i++; + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + zeroFilledSubarray(nums) { + let res = 0, i = 0; + while (i < nums.length) { + let count = 0; + while (i < nums.length && nums[i] === 0) { + count++; + i++; + res += count; + } + i++; + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ + +--- + +## 3. Count Consecutive Zeros - II + +::tabs-start + +```python +class Solution: + def zeroFilledSubarray(self, nums: List[int]) -> int: + res = count = 0 + + for num in nums: + if num == 0: + count += 1 + else: + count = 0 + res += count + + return res +``` + +```java +public class Solution { + public long zeroFilledSubarray(int[] nums) { + long res = 0; + int count = 0; + + for (int num : nums) { + if (num == 0) { + count++; + } else { + count = 0; + } + res += count; + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + long long zeroFilledSubarray(vector& nums) { + long long res = 0; + int count = 0; + + for (int& num : nums) { + if (num == 0) { + count++; + } else { + count = 0; + } + res += count; + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + zeroFilledSubarray(nums) { + let res = 0, count = 0; + + for (let num of nums) { + if (num === 0) { + count++; + } else { + count = 0; + } + res += count; + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ + +--- + +## 4. Count Consecutive Zeros (Math) + +::tabs-start + +```python +class Solution: + def zeroFilledSubarray(self, nums: List[int]) -> int: + res = count = 0 + for num in nums: + if num == 0: + count += 1 + else: + res += (count * (count + 1)) // 2 + count = 0 + res += (count * (count + 1)) // 2 + return res +``` + +```java +public class Solution { + public long zeroFilledSubarray(int[] nums) { + long res = 0, count = 0; + for (int num : nums) { + if (num == 0) { + count++; + } else { + res += count * (count + 1) / 2; + count = 0; + } + } + res += count * (count + 1) / 2; + return res; + } +} +``` + +```cpp +class Solution { +public: + long long zeroFilledSubarray(vector& nums) { + long long res = 0, count = 0; + for (int num : nums) { + if (num == 0) { + count++; + } else { + res += count * (count + 1) / 2; + count = 0; + } + } + res += count * (count + 1) / 2; + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + zeroFilledSubarray(nums) { + let res = 0, count = 0; + for (let num of nums) { + if (num === 0) { + count++; + } else { + res += (count * (count + 1)) / 2; + count = 0; + } + } + res += (count * (count + 1)) / 2; + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/optimal-partition-of-string.md b/articles/optimal-partition-of-string.md new file mode 100644 index 000000000..c4d89ab77 --- /dev/null +++ b/articles/optimal-partition-of-string.md @@ -0,0 +1,250 @@ +## 1. Greedy (Hash Set) + +::tabs-start + +```python +class Solution: + def partitionString(self, s: str) -> int: + curSet = set() + res = 1 + for c in s: + if c in curSet: + res += 1 + curSet.clear() + curSet.add(c) + return res +``` + +```java +public class Solution { + public int partitionString(String s) { + Set curSet = new HashSet<>(); + int res = 1; + for (char c : s.toCharArray()) { + if (curSet.contains(c)) { + res++; + curSet.clear(); + } + curSet.add(c); + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int partitionString(string s) { + unordered_set curSet; + int res = 1; + for (char c : s) { + if (curSet.count(c)) { + res++; + curSet.clear(); + } + curSet.insert(c); + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {number} + */ + partitionString(s) { + let curSet = new Set(); + let res = 1; + for (let c of s) { + if (curSet.has(c)) { + res++; + curSet.clear(); + } + curSet.add(c); + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ since we have at most $26$ different characters. + +--- + +## 2. Greedy (Array) + +::tabs-start + +```python +class Solution: + def partitionString(self, s: str) -> int: + lastIdx = [-1] * 26 + res = 1 + start = 0 + for i, c in enumerate(s): + j = ord(c) - ord('a') + if lastIdx[j] >= start: + start = i + res += 1 + lastIdx[j] = i + return res +``` + +```java +public class Solution { + public int partitionString(String s) { + int[] lastIdx = new int[26]; + Arrays.fill(lastIdx, -1); + int res = 1, start = 0; + for (int i = 0; i < s.length(); i++) { + int j = s.charAt(i) - 'a'; + if (lastIdx[j] >= start) { + start = i; + res++; + } + lastIdx[j] = i; + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int partitionString(string s) { + vector lastIdx(26, -1); + int res = 1, start = 0; + for (int i = 0; i < s.size(); i++) { + int j = s[i] - 'a'; + if (lastIdx[j] >= start) { + start = i; + res++; + } + lastIdx[j] = i; + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {number} + */ + partitionString(s) { + const lastIdx = Array(26).fill(-1); + let res = 1, start = 0; + for (let i = 0; i < s.length; i++) { + const j = s.charCodeAt(i) - 97; + if (lastIdx[j] >= start) { + start = i; + res++; + } + lastIdx[j] = i; + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ since we have at most $26$ different characters. + +--- + +## 3. Greedy (Bit Mask) + +::tabs-start + +```python +class Solution: + def partitionString(self, s: str) -> int: + res = 1 + mask = 0 + for c in s: + i = ord(c) - ord('a') + if mask & (1 << i): + mask = 0 + res += 1 + mask |= (1 << i) + return res +``` + +```java +public class Solution { + public int partitionString(String s) { + int res = 1, mask = 0; + for (char c : s.toCharArray()) { + int i = c - 'a'; + if ((mask & (1 << i)) != 0) { + mask = 0; + res++; + } + mask |= (1 << i); + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int partitionString(string s) { + int res = 1, mask = 0; + for (char c : s) { + int i = c - 'a'; + if (mask & (1 << i)) { + mask = 0; + res++; + } + mask |= (1 << i); + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {number} + */ + partitionString(s) { + let res = 1, mask = 0; + for (const c of s) { + const i = c.charCodeAt(0) - 97; + if (mask & (1 << i)) { + mask = 0; + res++; + } + mask |= (1 << i); + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/range-sum-query-2d-immutable.md b/articles/range-sum-query-2d-immutable.md new file mode 100644 index 000000000..3d19e47b0 --- /dev/null +++ b/articles/range-sum-query-2d-immutable.md @@ -0,0 +1,369 @@ +## 1. Brute Force + +::tabs-start + +```python +class NumMatrix: + + def __init__(self, matrix: list[list[int]]): + self.matrix = matrix + + def sumRegion(self, row1: int, col1: int, row2: int, col2: int) -> int: + res = 0 + for r in range(row1, row2 + 1): + for c in range(col1, col2 + 1): + res += self.matrix[r][c] + return res +``` + +```java +public class NumMatrix { + + private int[][] matrix; + + public NumMatrix(int[][] matrix) { + this.matrix = matrix; + } + + public int sumRegion(int row1, int col1, int row2, int col2) { + int res = 0; + for (int r = row1; r <= row2; r++) { + for (int c = col1; c <= col2; c++) { + res += matrix[r][c]; + } + } + return res; + } +} +``` + +```cpp +class NumMatrix { +private: + vector> matrix; + +public: + NumMatrix(vector>& matrix) { + this->matrix = matrix; + } + + int sumRegion(int row1, int col1, int row2, int col2) { + int res = 0; + for (int r = row1; r <= row2; r++) { + for (int c = col1; c <= col2; c++) { + res += matrix[r][c]; + } + } + return res; + } +}; +``` + +```javascript +class NumMatrix { + /** + * @param {number[][]} matrix + */ + constructor(matrix) { + this.matrix = matrix; + } + + /** + * @param {number} row1 + * @param {number} col1 + * @param {number} row2 + * @param {number} col2 + * @return {number} + */ + sumRegion(row1, col1, row2, col2) { + let res = 0; + for (let r = row1; r <= row2; r++) { + for (let c = col1; c <= col2; c++) { + res += this.matrix[r][c]; + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n)$ for each query. +* Space complexity: $O(1)$ + +> Where $m$ is the number of rows and $n$ is the number of columns in the matrix. + +--- + +## 2. One Dimensional Prefix Sum + +::tabs-start + +```python +class NumMatrix: + + def __init__(self, matrix: list[list[int]]): + self.prefixSum = [[0] * len(matrix[0]) for _ in range(len(matrix))] + + for row in range(len(matrix)): + self.prefixSum[row][0] = matrix[row][0] + for col in range(1, len(matrix[0])): + self.prefixSum[row][col] = self.prefixSum[row][col - 1] + matrix[row][col] + + def sumRegion(self, row1: int, col1: int, row2: int, col2: int) -> int: + res = 0 + for row in range(row1, row2 + 1): + if col1 > 0: + res += self.prefixSum[row][col2] - self.prefixSum[row][col1 - 1] + else: + res += self.prefixSum[row][col2] + return res +``` + +```java +public class NumMatrix { + + private int[][] prefixSum; + + public NumMatrix(int[][] matrix) { + int rows = matrix.length, cols = matrix[0].length; + prefixSum = new int[rows][cols]; + + for (int row = 0; row < rows; row++) { + prefixSum[row][0] = matrix[row][0]; + for (int col = 1; col < cols; col++) { + prefixSum[row][col] = prefixSum[row][col - 1] + matrix[row][col]; + } + } + } + + public int sumRegion(int row1, int col1, int row2, int col2) { + int res = 0; + for (int row = row1; row <= row2; row++) { + if (col1 > 0) { + res += prefixSum[row][col2] - prefixSum[row][col1 - 1]; + } else { + res += prefixSum[row][col2]; + } + } + return res; + } +} +``` + +```cpp +class NumMatrix { +private: + vector> prefixSum; + +public: + NumMatrix(vector>& matrix) { + int rows = matrix.size(), cols = matrix[0].size(); + prefixSum = vector>(rows, vector(cols, 0)); + + for (int row = 0; row < rows; row++) { + prefixSum[row][0] = matrix[row][0]; + for (int col = 1; col < cols; col++) { + prefixSum[row][col] = prefixSum[row][col - 1] + matrix[row][col]; + } + } + } + + int sumRegion(int row1, int col1, int row2, int col2) { + int res = 0; + for (int row = row1; row <= row2; row++) { + if (col1 > 0) { + res += prefixSum[row][col2] - prefixSum[row][col1 - 1]; + } else { + res += prefixSum[row][col2]; + } + } + return res; + } +}; +``` + +```javascript +class NumMatrix { + /** + * @param {number[][]} matrix + */ + constructor(matrix) { + this.prefixSum = Array.from({ length: matrix.length }, () => Array(matrix[0].length).fill(0)); + + for (let row = 0; row < matrix.length; row++) { + this.prefixSum[row][0] = matrix[row][0]; + for (let col = 1; col < matrix[0].length; col++) { + this.prefixSum[row][col] = this.prefixSum[row][col - 1] + matrix[row][col]; + } + } + } + + /** + * @param {number} row1 + * @param {number} col1 + * @param {number} row2 + * @param {number} col2 + * @return {number} + */ + sumRegion(row1, col1, row2, col2) { + let res = 0; + for (let row = row1; row <= row2; row++) { + if (col1 > 0) { + res += this.prefixSum[row][col2] - this.prefixSum[row][col1 - 1]; + } else { + res += this.prefixSum[row][col2]; + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m)$ +* Space complexity: $O(m * n)$ + +> Where $m$ is the number of rows and $n$ is the number of columns in the matrix. + +--- + +## 3. Two Dimensional Prefix Sum + +::tabs-start + +```python +class NumMatrix: + + def __init__(self, matrix: list[list[int]]): + ROWS, COLS = len(matrix), len(matrix[0]) + self.sumMat = [[0] * (COLS + 1) for _ in range(ROWS + 1)] + + for r in range(ROWS): + prefix = 0 + for c in range(COLS): + prefix += matrix[r][c] + above = self.sumMat[r][c + 1] + self.sumMat[r + 1][c + 1] = prefix + above + + def sumRegion(self, row1: int, col1: int, row2: int, col2: int) -> int: + row1, col1, row2, col2 = row1 + 1, col1 + 1, row2 + 1, col2 + 1 + bottomRight = self.sumMat[row2][col2] + above = self.sumMat[row1 - 1][col2] + left = self.sumMat[row2][col1 - 1] + topLeft = self.sumMat[row1 - 1][col1 - 1] + return bottomRight - above - left + topLeft +``` + +```java +public class NumMatrix { + + private int[][] sumMat; + + public NumMatrix(int[][] matrix) { + int ROWS = matrix.length, COLS = matrix[0].length; + sumMat = new int[ROWS + 1][COLS + 1]; + + for (int r = 0; r < ROWS; r++) { + int prefix = 0; + for (int c = 0; c < COLS; c++) { + prefix += matrix[r][c]; + int above = sumMat[r][c + 1]; + sumMat[r + 1][c + 1] = prefix + above; + } + } + } + + public int sumRegion(int row1, int col1, int row2, int col2) { + row1++; col1++; row2++; col2++; + int bottomRight = sumMat[row2][col2]; + int above = sumMat[row1 - 1][col2]; + int left = sumMat[row2][col1 - 1]; + int topLeft = sumMat[row1 - 1][col1 - 1]; + return bottomRight - above - left + topLeft; + } +} +``` + +```cpp +class NumMatrix { +private: + vector> sumMat; + +public: + NumMatrix(vector>& matrix) { + int ROWS = matrix.size(), COLS = matrix[0].size(); + sumMat = vector>(ROWS + 1, vector(COLS + 1, 0)); + + for (int r = 0; r < ROWS; r++) { + int prefix = 0; + for (int c = 0; c < COLS; c++) { + prefix += matrix[r][c]; + int above = sumMat[r][c + 1]; + sumMat[r + 1][c + 1] = prefix + above; + } + } + } + + int sumRegion(int row1, int col1, int row2, int col2) { + row1++; col1++; row2++; col2++; + int bottomRight = sumMat[row2][col2]; + int above = sumMat[row1 - 1][col2]; + int left = sumMat[row2][col1 - 1]; + int topLeft = sumMat[row1 - 1][col1 - 1]; + return bottomRight - above - left + topLeft; + } +}; +``` + +```javascript +class NumMatrix { + /** + * @param {number[][]} matrix + */ + constructor(matrix) { + const ROWS = matrix.length, COLS = matrix[0].length; + this.sumMat = Array.from({ length: ROWS + 1 }, () => Array(COLS + 1).fill(0)); + + for (let r = 0; r < ROWS; r++) { + let prefix = 0; + for (let c = 0; c < COLS; c++) { + prefix += matrix[r][c]; + const above = this.sumMat[r][c + 1]; + this.sumMat[r + 1][c + 1] = prefix + above; + } + } + } + + /** + * @param {number} row1 + * @param {number} col1 + * @param {number} row2 + * @param {number} col2 + * @return {number} + */ + sumRegion(row1, col1, row2, col2) { + row1++; col1++; row2++; col2++; + const bottomRight = this.sumMat[row2][col2]; + const above = this.sumMat[row1 - 1][col2]; + const left = this.sumMat[row2][col1 - 1]; + const topLeft = this.sumMat[row1 - 1][col1 - 1]; + return bottomRight - above - left + topLeft; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(1)$ for each query. +* Space complexity: $O(m * n)$ + +> Where $m$ is the number of rows and $n$ is the number of columns in the matrix. \ No newline at end of file diff --git a/articles/repeated-dna-sequences.md b/articles/repeated-dna-sequences.md new file mode 100644 index 000000000..6976f8850 --- /dev/null +++ b/articles/repeated-dna-sequences.md @@ -0,0 +1,470 @@ +## 1. Hash Set + +::tabs-start + +```python +class Solution: + def findRepeatedDnaSequences(self, s: str) -> List[str]: + seen, res = set(), set() + + for l in range(len(s) - 9): + cur = s[l: l + 10] + if cur in seen: + res.add(cur) + seen.add(cur) + return list(res) +``` + +```java +public class Solution { + public List findRepeatedDnaSequences(String s) { + Set seen = new HashSet<>(); + Set res = new HashSet<>(); + + for (int l = 0; l < s.length() - 9; l++) { + String cur = s.substring(l, l + 10); + if (seen.contains(cur)) { + res.add(cur); + } + seen.add(cur); + } + return new ArrayList<>(res); + } +} +``` + +```cpp +class Solution { +public: + vector findRepeatedDnaSequences(string s) { + if (s.size() < 10) return {}; + unordered_set seen, res; + + for (int l = 0; l < s.size() - 9; l++) { + string cur = s.substr(l, 10); + if (seen.count(cur)) { + res.insert(cur); + } + seen.insert(cur); + } + return vector(res.begin(), res.end()); + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {string[]} + */ + findRepeatedDnaSequences(s) { + const seen = new Set(); + const res = new Set(); + + for (let l = 0; l < s.length - 9; l++) { + const cur = s.substring(l, l + 10); + if (seen.has(cur)) { + res.add(cur); + } + seen.add(cur); + } + return Array.from(res); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Hash Map + +::tabs-start + +```python +class Solution: + def findRepeatedDnaSequences(self, s: str) -> List[str]: + if len(s) < 10: + return [] + + mp = defaultdict(int) + res = [] + for l in range(len(s) - 9): + cur = s[l: l + 10] + mp[cur] += 1 + if mp[cur] == 2: + res.append(cur) + + return res +``` + +```java +public class Solution { + public List findRepeatedDnaSequences(String s) { + if (s.length() < 10) { + return new ArrayList<>(); + } + + Map mp = new HashMap<>(); + List res = new ArrayList<>(); + + for (int l = 0; l < s.length() - 9; l++) { + String cur = s.substring(l, l + 10); + mp.put(cur, mp.getOrDefault(cur, 0) + 1); + if (mp.get(cur) == 2) { + res.add(cur); + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector findRepeatedDnaSequences(string s) { + if (s.size() < 10) { + return {}; + } + + unordered_map mp; + vector res; + + for (int l = 0; l < s.size() - 9; l++) { + string cur = s.substr(l, 10); + mp[cur]++; + if (mp[cur] == 2) { + res.push_back(cur); + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {string[]} + */ + findRepeatedDnaSequences(s) { + if (s.length < 10) { + return []; + } + + const mp = new Map(); + const res = []; + + for (let l = 0; l <= s.length - 10; l++) { + const cur = s.substring(l, l + 10); + mp.set(cur, (mp.get(cur) || 0) + 1); + if (mp.get(cur) === 2) { + res.push(cur); + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Rabin-Karp Algorithm (Double Hashing) + +::tabs-start + +```python +class Solution: + def findRepeatedDnaSequences(self, s: str) -> List[str]: + n = len(s) + if n < 10: + return [] + + cnt = defaultdict(int) + res = [] + base1, base2 = 31, 37 + hash1 = hash2 = 0 + power1, power2 = 1, 1 + MOD1, MOD2 = 685683731, 768258391 + + for i in range(9): + power1 *= base1 + power2 *= base2 + power1 %= MOD1 + power2 %= MOD2 + + for i in range(n): + hash1 = (hash1 * base1 + ord(s[i])) % MOD1 + hash2 = (hash2 * base2 + ord(s[i])) % MOD2 + + if i >= 9: + key = (hash1 << 31) | hash2 + cnt[key] += 1 + if cnt[key] == 2: + res.append(s[i - 9 : i + 1]) + + hash1 = (hash1 - power1 * ord(s[i - 9]) % MOD1 + MOD1) % MOD1 + hash2 = (hash2 - power2 * ord(s[i - 9]) % MOD2 + MOD2) + + return res +``` + +```java +public class Solution { + public List findRepeatedDnaSequences(String s) { + int n = s.length(); + if (n < 10) return new ArrayList<>(); + + Map cnt = new HashMap<>(); + List res = new ArrayList<>(); + int base1 = 31, base2 = 37; + long hash1 = 0, hash2 = 0, power1 = 1, power2 = 1; + int MOD1 = 685683731, MOD2 = 768258391; + + for (int i = 0; i < 9; i++) { + power1 = (power1 * base1) % MOD1; + power2 = (power2 * base2) % MOD2; + } + + for (int i = 0; i < n; i++) { + hash1 = (hash1 * base1 + s.charAt(i)) % MOD1; + hash2 = (hash2 * base2 + s.charAt(i)) % MOD2; + + if (i >= 9) { + long key = (hash1 << 31) | hash2; + cnt.put(key, cnt.getOrDefault(key, 0) + 1); + if (cnt.get(key) == 2) { + res.add(s.substring(i - 9, i + 1)); + } + + hash1 = (hash1 - power1 * s.charAt(i - 9) % MOD1 + MOD1) % MOD1; + hash2 = (hash2 - power2 * s.charAt(i - 9) % MOD2 + MOD2) % MOD2; + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector findRepeatedDnaSequences(string s) { + int n = s.length(); + if (n < 10) return {}; + + unordered_map cnt; + vector res; + int base1 = 31, base2 = 37; + long long hash1 = 0, hash2 = 0, power1 = 1, power2 = 1; + int MOD1 = 685683731, MOD2 = 768258391; + + for (int i = 0; i < 9; i++) { + power1 = (power1 * base1) % MOD1; + power2 = (power2 * base2) % MOD2; + } + + for (int i = 0; i < n; i++) { + hash1 = (hash1 * base1 + s[i]) % MOD1; + hash2 = (hash2 * base2 + s[i]) % MOD2; + + if (i >= 9) { + long long key = (hash1 << 31) | hash2; + cnt[key]++; + if (cnt[key] == 2) { + res.push_back(s.substr(i - 9, 10)); + } + + hash1 = (hash1 - power1 * s[i - 9] % MOD1 + MOD1) % MOD1; + hash2 = (hash2 - power2 * s[i - 9] % MOD2 + MOD2) % MOD2; + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {string[]} + */ + findRepeatedDnaSequences(s) { + const n = s.length; + if (n < 10) return []; + + const cnt = new Map(); + const res = []; + const base1 = 31, base2 = 37; + let hash1 = 0, hash2 = 0, power1 = 1, power2 = 1; + const MOD1 = 685683731, MOD2 = 768258391; + + for (let i = 0; i < 9; i++) { + power1 = (power1 * base1) % MOD1; + power2 = (power2 * base2) % MOD2; + } + + for (let i = 0; i < n; i++) { + hash1 = (hash1 * base1 + s.charCodeAt(i)) % MOD1; + hash2 = (hash2 * base2 + s.charCodeAt(i)) % MOD2; + + if (i >= 9) { + const key = `${hash1},${hash2}`; + cnt.set(key, (cnt.get(key) || 0) + 1); + if (cnt.get(key) === 2) { + res.push(s.substring(i - 9, i + 1)); + } + + hash1 = (hash1 - power1 * s.charCodeAt(i - 9) % MOD1 + MOD1) % MOD1; + hash2 = (hash2 - power2 * s.charCodeAt(i - 9) % MOD2 + MOD2) % MOD2; + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 4. Bit Mask + +::tabs-start + +```python +class Solution: + def findRepeatedDnaSequences(self, s: str) -> list[str]: + if len(s) < 10: + return [] + + mp = {'A': 0, 'C': 1, 'G': 2, 'T': 3} + seen, res = set(), set() + mask = 0 + for i in range(len(s)): + mask = ((mask << 2) | mp[s[i]]) & 0xFFFFF + if i >= 9: + if mask in seen: + res.add(s[i - 9: i + 1]) + else: + seen.add(mask) + + return list(res) +``` + +```java +public class Solution { + public List findRepeatedDnaSequences(String s) { + if (s.length() < 10) return new ArrayList<>(); + + Map mp = Map.of('A', 0, 'C', 1, 'G', 2, 'T', 3); + Map cnt = new HashMap<>(); + List res = new ArrayList<>(); + int mask = 0; + + for (int i = 0; i < s.length(); i++) { + mask = ((mask << 2) | mp.get(s.charAt(i))) & 0xFFFFF; + if (i >= 9) { + cnt.put(mask, cnt.getOrDefault(mask, 0) + 1); + if (cnt.get(mask) == 2) { + res.add(s.substring(i - 9, i + 1)); + } + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector findRepeatedDnaSequences(string s) { + if (s.length() < 10) return {}; + + unordered_map mp = {{'A', 0}, {'C', 1}, + {'G', 2}, {'T', 3}}; + unordered_map cnt; + vector res; + int mask = 0; + + for (int i = 0; i < s.size(); i++) { + mask = ((mask << 2) | mp[s[i]]) & 0xFFFFF; + + if (i >= 9) { + cnt[mask]++; + if (cnt[mask] == 2) { + res.push_back(s.substr(i - 9, 10)); + } + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {string[]} + */ + findRepeatedDnaSequences(s) { + if (s.length < 10) return []; + + const mp = { 'A': 0, 'C': 1, 'G': 2, 'T': 3 }; + const cnt = new Map(); + const res = []; + let mask = 0; + + for (let i = 0; i < s.length; i++) { + mask = ((mask << 2) | mp[s[i]]) & 0xFFFFF; + + if (i >= 9) { + cnt.set(mask, (cnt.get(mask) || 0) + 1); + if (cnt.get(mask) === 2) { + res.push(s.substring(i - 9, i + 1)); + } + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/sign-of-the-product-of-an-array.md b/articles/sign-of-the-product-of-an-array.md new file mode 100644 index 000000000..76922c8d0 --- /dev/null +++ b/articles/sign-of-the-product-of-an-array.md @@ -0,0 +1,158 @@ +## 1. Count Negative Numbers + +::tabs-start + +```python +class Solution: + def arraySign(self, nums: list[int]) -> int: + neg = 0 + for num in nums: + if num == 0: + return 0 + neg += (1 if num < 0 else 0) + return -1 if neg % 2 else 1 +``` + +```java +public class Solution { + public int arraySign(int[] nums) { + int neg = 0; + for (int num : nums) { + if (num == 0) { + return 0; + } + if (num < 0) { + neg++; + } + } + return neg % 2 == 0 ? 1 : -1; + } +} +``` + +```cpp +class Solution { +public: + int arraySign(vector& nums) { + int neg = 0; + for (int num : nums) { + if (num == 0) { + return 0; + } + if (num < 0) { + neg++; + } + } + return neg % 2 == 0 ? 1 : -1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + arraySign(nums) { + let neg = 0; + for (const num of nums) { + if (num === 0) { + return 0; + } + if (num < 0) { + neg++; + } + } + return neg % 2 === 0 ? 1 : -1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ + +--- + +## 2. Track the Sign of the Product + +::tabs-start + +```python +class Solution: + def arraySign(self, nums: list[int]) -> int: + sign = 1 + for num in nums: + if num == 0: + return 0 + if num < 0: + sign *= -1 + return sign +``` + +```java +public class Solution { + public int arraySign(int[] nums) { + int sign = 1; + for (int num : nums) { + if (num == 0) { + return 0; + } + if (num < 0) { + sign *= -1; + } + } + return sign; + } +} +``` + +```cpp +class Solution { +public: + int arraySign(vector& nums) { + int sign = 1; + for (int num : nums) { + if (num == 0) { + return 0; + } + if (num < 0) { + sign *= -1; + } + } + return sign; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + arraySign(nums) { + let sign = 1; + for (const num of nums) { + if (num === 0) { + return 0; + } + if (num < 0) { + sign *= -1; + } + } + return sign; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/sort-an-array.md b/articles/sort-an-array.md index 7551c767e..ba9edb507 100644 --- a/articles/sort-an-array.md +++ b/articles/sort-an-array.md @@ -209,7 +209,7 @@ class Solution { ### Time & Space Complexity -* Time complexity: $O(n)$ in average case, O(n ^ 2) in worst case. +* Time complexity: $O(n)$ in average case, $O(n ^ 2)$ in worst case. * Space complexity: $O(\log n)$ for recursive stack. --- diff --git a/articles/sort-characters-by-frequency.md b/articles/sort-characters-by-frequency.md new file mode 100644 index 000000000..779312faf --- /dev/null +++ b/articles/sort-characters-by-frequency.md @@ -0,0 +1,340 @@ +## 1. Sorting + +::tabs-start + +```python +class Solution: + def frequencySort(self, s: str) -> str: + count = Counter(s) + sorted_chars = sorted(s, key=lambda x: (-count[x], x)) + return ''.join(sorted_chars) +``` + +```java +public class Solution { + public String frequencySort(String s) { + int[] count = new int[123]; + for (char c : s.toCharArray()) { + count[c]++; + } + + Character[] chars = new Character[s.length()]; + for (int i = 0; i < s.length(); i++) { + chars[i] = s.charAt(i); + } + + Arrays.sort(chars, (a, b) -> { + if (count[b] == count[a]) { + return a - b; + } + return count[b] - count[a]; + }); + + StringBuilder result = new StringBuilder(); + for (char c : chars) { + result.append(c); + } + + return result.toString(); + } +} +``` + +```cpp +class Solution { +public: + string frequencySort(string s) { + vector count(123); + for (char c : s) { + count[c]++; + } + + vector chars(s.begin(), s.end()); + sort(chars.begin(), chars.end(), [&](char a, char b) { + if (count[b] == count[a]) { + return a < b; + } + return count[b] < count[a]; + }); + + return string(chars.begin(), chars.end()); + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {string} + */ + frequencySort(s) { + const count = {}; + for (const char of s) { + count[char] = (count[char] || 0) + 1; + } + + const sortedChars = [...s].sort((a, b) => { + if (count[b] === count[a]) { + return a.localeCompare(b); + } + return count[b] - count[a]; + }); + + return sortedChars.join(''); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(1)$ or $O(n)$ depending on the sorting algorithm. + +--- + +## 2. Frequency Sort + +::tabs-start + +```python +class Solution: + def frequencySort(self, s: str) -> str: + count = [0] * 123 + for c in s: + count[ord(c)] += 1 + + freq = [(chr(i), count[i]) for i in range(123) if count[i] > 0] + freq.sort(key=lambda x: (-x[1], x[0])) + + return ''.join(char * freq for char, freq in freq) +``` + +```java +public class Solution { + public String frequencySort(String s) { + int[] count = new int[123]; + for (char c : s.toCharArray()) { + count[c]++; + } + + List freq = new ArrayList<>(); + for (int i = 0; i < 123; i++) { + if (count[i] > 0) { + freq.add(new int[]{i, count[i]}); + } + } + + freq.sort((a, b) -> { + if (b[1] == a[1]) { + return a[0] - b[0]; + } + return b[1] - a[1]; + }); + + StringBuilder res = new StringBuilder(); + for (int[] entry : freq) { + for (int i = 0; i < entry[1]; i++) { + res.append((char) entry[0]); + } + } + + return res.toString(); + } +} +``` + +```cpp +class Solution { +public: + string frequencySort(string s) { + vector count(123, 0); + for (char c : s) { + count[c]++; + } + + vector> freq; + for (int i = 0; i < 123; i++) { + if (count[i] > 0) { + freq.emplace_back((char)i, count[i]); + } + } + + sort(freq.begin(), freq.end(), [](auto& a, auto& b) { + if (a.second == b.second) { + return a.first < b.first; + } + return a.second > b.second; + }); + + string res; + for (const auto& entry : freq) { + res += string(entry.second, entry.first); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {string} + */ + frequencySort(s) { + const count = new Array(123).fill(0); + for (const char of s) { + count[char.charCodeAt(0)]++; + } + + const freq = []; + for (let i = 0; i < 123; i++) { + if (count[i] > 0) { + freq.push([String.fromCharCode(i), count[i]]); + } + } + + freq.sort((a, b) => { + if (b[1] === a[1]) { + return a[0].localeCompare(b[0]); + } + return b[1] - a[1]; + }); + + let res = ''; + for (const [char, freqCount] of freq) { + res += char.repeat(freqCount); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ for the output string. + +--- + +## 3. Bucket Sort + +::tabs-start + +```python +class Solution: + def frequencySort(self, s: str) -> str: + count = Counter(s) # char -> freq + buckets = defaultdict(list) # freq -> [char] + + for char, freq in count.items(): + buckets[freq].append(char) + + res = [] + for i in range(len(s), 0, -1): + if i in buckets: + for c in buckets[i]: + res.append(c * i) + + return "".join(res) +``` + +```java +public class Solution { + public String frequencySort(String s) { + Map count = new HashMap<>(); + for (char c : s.toCharArray()) { + count.put(c, count.getOrDefault(c, 0) + 1); + } + + List> buckets = new ArrayList<>(s.length() + 1); + for (int i = 0; i <= s.length(); i++) { + buckets.add(new ArrayList<>()); + } + + for (Map.Entry entry : count.entrySet()) { + buckets.get(entry.getValue()).add(entry.getKey()); + } + + StringBuilder res = new StringBuilder(); + for (int i = s.length(); i > 0; i--) { + for (char c : buckets.get(i)) { + for (int j = 0; j < i; j++) { + res.append(c); + } + } + } + + return res.toString(); + } +} +``` + +```cpp +class Solution { +public: + string frequencySort(string s) { + unordered_map count; + for (char c : s) { + count[c]++; + } + + vector> buckets(s.size() + 1); + for (auto& entry : count) { + buckets[entry.second].push_back(entry.first); + } + + string res; + for (int i = s.size(); i > 0; i--) { + for (char c : buckets[i]) { + res += string(i, c); + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {string} + */ + frequencySort(s) { + const count = {}; + for (const char of s) { + count[char] = (count[char] || 0) + 1; + } + + const buckets = Array.from({ length: s.length + 1 }, () => []); + for (const [char, freq] of Object.entries(count)) { + buckets[freq].push(char); + } + + let res = ''; + for (let i = s.length; i > 0; i--) { + for (const char of buckets[i]) { + res += char.repeat(i); + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/sum-of-absolute-differences-in-a-sorted-array.md b/articles/sum-of-absolute-differences-in-a-sorted-array.md new file mode 100644 index 000000000..f705f47d7 --- /dev/null +++ b/articles/sum-of-absolute-differences-in-a-sorted-array.md @@ -0,0 +1,430 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def getSumAbsoluteDifferences(self, nums: List[int]) -> List[int]: + res = [] + + for i in nums: + sum = 0 + for j in nums: + sum += abs(i - j) + res.append(sum) + + return res +``` + +```java +public class Solution { + public int[] getSumAbsoluteDifferences(int[] nums) { + int n = nums.length; + int[] res = new int[n]; + + for (int i = 0; i < n; i++) { + int sum = 0; + for (int j = 0; j < n; j++) { + sum += Math.abs(nums[i] - nums[j]); + } + res[i] = sum; + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector getSumAbsoluteDifferences(vector& nums) { + int n = nums.size(); + vector res; + + for (int i = 0; i < n; i++) { + int sum = 0; + for (int j = 0; j < n; j++) { + sum += abs(nums[i] - nums[j]); + } + res.push_back(sum); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number[]} + */ + getSumAbsoluteDifferences(nums) { + const n = nums.length; + const res = []; + + for (let i = 0; i < n; i++) { + let sum = 0; + for (let j = 0; j < n; j++) { + sum += Math.abs(nums[i] - nums[j]); + } + res.push(sum); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n)$ for the output array. + +--- + +## 2. Prefix & Suffix Sums (Extra Space) + +::tabs-start + +```python +class Solution: + def getSumAbsoluteDifferences(self, nums: List[int]) -> List[int]: + n = len(nums) + prefix_sum = [0] * n + suffix_sum = [0] * n + res = [0] * n + + prefix_sum[0] = nums[0] + for i in range(1, n): + prefix_sum[i] = prefix_sum[i - 1] + nums[i] + + suffix_sum[n - 1] = nums[n - 1] + for i in range(n - 2, -1, -1): + suffix_sum[i] = suffix_sum[i + 1] + nums[i] + + for i in range(n): + left_sum = (i * nums[i]) - (prefix_sum[i - 1] if i > 0 else 0) + right_sum = (suffix_sum[i + 1] if i < n - 1 else 0) - ((n - i - 1) * nums[i]) + res[i] = left_sum + right_sum + + return res +``` + +```java +public class Solution { + public int[] getSumAbsoluteDifferences(int[] nums) { + int n = nums.length; + int[] prefixSum = new int[n]; + int[] suffixSum = new int[n]; + int[] res = new int[n]; + + prefixSum[0] = nums[0]; + for (int i = 1; i < n; i++) { + prefixSum[i] = prefixSum[i - 1] + nums[i]; + } + + suffixSum[n - 1] = nums[n - 1]; + for (int i = n - 2; i >= 0; i--) { + suffixSum[i] = suffixSum[i + 1] + nums[i]; + } + + for (int i = 0; i < n; i++) { + int leftSum = i > 0 ? (i * nums[i] - prefixSum[i - 1]) : 0; + int rightSum = i < n - 1 ? (suffixSum[i + 1] - (n - i - 1) * nums[i]) : 0; + res[i] = leftSum + rightSum; + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector getSumAbsoluteDifferences(vector& nums) { + int n = nums.size(); + vector prefixSum(n, 0), suffixSum(n, 0), res(n, 0); + + prefixSum[0] = nums[0]; + for (int i = 1; i < n; i++) { + prefixSum[i] = prefixSum[i - 1] + nums[i]; + } + + suffixSum[n - 1] = nums[n - 1]; + for (int i = n - 2; i >= 0; i--) { + suffixSum[i] = suffixSum[i + 1] + nums[i]; + } + + for (int i = 0; i < n; i++) { + int leftSum = i > 0 ? (i * nums[i] - prefixSum[i - 1]) : 0; + int rightSum = i < n - 1 ? (suffixSum[i + 1] - (n - i - 1) * nums[i]) : 0; + res[i] = leftSum + rightSum; + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number[]} + */ + getSumAbsoluteDifferences(nums) { + const n = nums.length; + const prefixSum = Array(n).fill(0); + const suffixSum = Array(n).fill(0); + const res = Array(n).fill(0); + + prefixSum[0] = nums[0]; + for (let i = 1; i < n; i++) { + prefixSum[i] = prefixSum[i - 1] + nums[i]; + } + + suffixSum[n - 1] = nums[n - 1]; + for (let i = n - 2; i >= 0; i--) { + suffixSum[i] = suffixSum[i + 1] + nums[i]; + } + + for (let i = 0; i < n; i++) { + const leftSum = i > 0 ? (i * nums[i] - prefixSum[i - 1]) : 0; + const rightSum = i < n - 1 ? (suffixSum[i + 1] - (n - i - 1) * nums[i]) : 0; + res[i] = leftSum + rightSum; + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Prefix & Suffix Sums + +::tabs-start + +```python +class Solution: + def getSumAbsoluteDifferences(self, nums: List[int]) -> List[int]: + n = len(nums) + res = [0] * n + + res[n - 1] = nums[n - 1] + for i in range(n - 2, -1, -1): + res[i] = res[i + 1] + nums[i] + + prefix_sum = 0 + for i in range(n): + left_sum = (i * nums[i]) - prefix_sum + right_sum = (res[i + 1] if i < n - 1 else 0) - ((n - i - 1) * nums[i]) + res[i] = left_sum + right_sum + prefix_sum += nums[i] + + return res +``` + +```java +public class Solution { + public int[] getSumAbsoluteDifferences(int[] nums) { + int n = nums.length; + int[] res = new int[n]; + + res[n - 1] = nums[n - 1]; + for (int i = n - 2; i >= 0; i--) { + res[i] = res[i + 1] + nums[i]; + } + + int prefixSum = 0; + for (int i = 0; i < n; i++) { + int leftSum = i * nums[i] - prefixSum; + int rightSum = i < n - 1 ? (res[i + 1] - (n - i - 1) * nums[i]) : 0; + res[i] = leftSum + rightSum; + prefixSum += nums[i]; + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector getSumAbsoluteDifferences(vector& nums) { + int n = nums.size(); + vector res(n, 0); + + res[n - 1] = nums[n - 1]; + for (int i = n - 2; i >= 0; i--) { + res[i] = res[i + 1] + nums[i]; + } + + int prefixSum = 0; + for (int i = 0; i < n; i++) { + int leftSum = i * nums[i] - prefixSum; + int rightSum = i < n - 1 ? (res[i + 1] - (n - i - 1) * nums[i]) : 0; + res[i] = leftSum + rightSum; + prefixSum += nums[i]; + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number[]} + */ + getSumAbsoluteDifferences(nums) { + const n = nums.length; + const res = Array(n).fill(0); + + res[n - 1] = nums[n - 1]; + for (let i = n - 2; i >= 0; i--) { + res[i] = res[i + 1] + nums[i]; + } + + let prefixSum = 0; + for (let i = 0; i < n; i++) { + const leftSum = i * nums[i] - prefixSum; + const rightSum = i < n - 1 ? (res[i + 1] - (n - i - 1) * nums[i]) : 0; + res[i] = leftSum + rightSum; + prefixSum += nums[i]; + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ for the output array. + +--- + +## 4. Prefix & Suffix Sums (Optimal) + +::tabs-start + +```python +class Solution: + def getSumAbsoluteDifferences(self, nums: List[int]) -> List[int]: + n = len(nums) + res = [0] * n + + total_sum = sum(nums) + prefix_sum = 0 + + for i, num in enumerate(nums): + total_sum -= nums[i] + left_sum = i * nums[i] - prefix_sum + right_sum = total_sum - (n - i - 1) * nums[i] + res[i] = left_sum + right_sum + prefix_sum += nums[i] + + return res +``` + +```java +public class Solution { + public int[] getSumAbsoluteDifferences(int[] nums) { + int n = nums.length; + int[] res = new int[n]; + + int totalSum = 0, prefixSum = 0; + for (int num : nums) { + totalSum += num; + } + + for (int i = 0; i < n; i++) { + totalSum -= nums[i]; + int leftSum = i * nums[i] - prefixSum; + int rightSum = totalSum - (n - i - 1) * nums[i]; + res[i] = leftSum + rightSum; + prefixSum += nums[i]; + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector getSumAbsoluteDifferences(vector& nums) { + int n = nums.size(); + vector res(n, 0); + + int totalSum = 0, prefixSum = 0; + for (int& num : nums) { + totalSum += num; + } + + for (int i = 0; i < n; i++) { + totalSum -= nums[i]; + int leftSum = i * nums[i] - prefixSum; + int rightSum = totalSum - (n - i - 1) * nums[i]; + res[i] = leftSum + rightSum; + prefixSum += nums[i]; + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number[]} + */ + getSumAbsoluteDifferences(nums) { + const n = nums.length; + const res = Array(n).fill(0); + + let totalSum = nums.reduce((sum, num) => sum + num, 0); + let prefixSum = 0; + + for (let i = 0; i < n; i++) { + totalSum -= nums[i]; + const leftSum = i * nums[i] - prefixSum; + const rightSum = totalSum - (n - i - 1) * nums[i]; + res[i] = leftSum + rightSum; + prefixSum += nums[i]; + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ for the output array. \ No newline at end of file diff --git a/articles/wiggle-sort.md b/articles/wiggle-sort.md new file mode 100644 index 000000000..d18f99598 --- /dev/null +++ b/articles/wiggle-sort.md @@ -0,0 +1,293 @@ +## 1. Max-Heap + +::tabs-start + +```python +class Solution: + def wiggleSort(self, nums: List[int]) -> None: + """ + Do not return anything, modify nums in-place instead. + """ + + maxHeap = [] + for num in nums: + heappush(maxHeap, -num) + + n = len(nums) + for i in range(1, n, 2): + nums[i] = -heappop(maxHeap) + for i in range(0, n, 2): + nums[i] = -heappop(maxHeap) +``` + +```java +public class Solution { + public void wiggleSort(int[] nums) { + PriorityQueue maxHeap = new PriorityQueue<>(Collections.reverseOrder()); + for (int num : nums) { + maxHeap.add(num); + } + + for (int i = 1; i < nums.length; i += 2) { + nums[i] = maxHeap.poll(); + } + for (int i = 0; i < nums.length; i += 2) { + nums[i] = maxHeap.poll(); + } + } +} +``` + +```cpp +class Solution { +public: + void wiggleSort(vector& nums) { + priority_queue maxHeap; + for (int& num : nums) { + maxHeap.push(num); + } + + for (int i = 1; i < nums.size(); i += 2) { + nums[i] = maxHeap.top(); + maxHeap.pop(); + } + for (int i = 0; i < nums.size(); i += 2) { + nums[i] = maxHeap.top(); + maxHeap.pop(); + } + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {void} Do not return anything, modify nums in-place instead. + */ + wiggleSort(nums) { + const maxHeap = new PriorityQueue((a, b) => b - a); + nums.forEach(num => maxHeap.enqueue(num)); + + for (let i = 1; i < nums.length; i += 2) { + nums[i] = maxHeap.dequeue(); + } + for (let i = 0; i < nums.length; i += 2) { + nums[i] = maxHeap.dequeue(); + } + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Sorting + +::tabs-start + +```python +class Solution: + def wiggleSort(self, nums: List[int]) -> None: + """ + Do not return anything, modify nums in-place instead. + """ + + nums.sort() + for i in range(1, len(nums) - 1, 2): + nums[i], nums[i + 1] = nums[i + 1], nums[i] +``` + +```java +public class Solution { + public void wiggleSort(int[] nums) { + Arrays.sort(nums); + for (int i = 1; i < nums.length - 1; i += 2) { + int temp = nums[i]; + nums[i] = nums[i + 1]; + nums[i + 1] = temp; + } + } +} +``` + +```cpp +class Solution { +public: + void wiggleSort(vector& nums) { + sort(nums.begin(), nums.end()); + for (int i = 1; i < nums.size() - 1; i += 2) { + swap(nums[i], nums[i + 1]); + } + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {void} Do not return anything, modify nums in-place instead. + */ + wiggleSort(nums) { + nums.sort((a, b) => a - b); + for (let i = 1; i < nums.length - 1; i += 2) { + [nums[i], nums[i + 1]] = [nums[i + 1], nums[i]]; + } + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(1)$ or $O(n)$ depending on the sorting algorithm. + +--- + +## 3. Greedy - I + +::tabs-start + +```python +class Solution: + def wiggleSort(self, nums: List[int]) -> None: + """ + Do not return anything, modify nums in-place instead. + """ + + for i in range(1, len(nums)): + if ((i % 2 == 1 and nums[i] < nums[i - 1]) or + (i % 2 == 0 and nums[i] > nums[i - 1]) + ): + nums[i], nums[i - 1] = nums[i - 1], nums[i] +``` + +```java +public class Solution { + public void wiggleSort(int[] nums) { + for (int i = 1; i < nums.length; i++) { + if ((i % 2 == 1 && nums[i] < nums[i - 1]) || + (i % 2 == 0 && nums[i] > nums[i - 1])) { + int temp = nums[i]; + nums[i] = nums[i - 1]; + nums[i - 1] = temp; + } + } + } +} +``` + +```cpp +class Solution { +public: + void wiggleSort(vector& nums) { + for (int i = 1; i nums[i - 1])) { + swap(nums[i], nums[i - 1]); + } + } + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {void} Do not return anything, modify nums in-place instead. + */ + wiggleSort(nums) { + for (let i = 1; i < nums.length; i++) { + if ((i % 2 == 1 && nums[i] < nums[i - 1]) || + (i % 2 == 0 && nums[i] > nums[i - 1])) { + [nums[i], nums[i - 1]] = [nums[i - 1], nums[i]]; + } + } + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ + +--- + +## 4. Greedy - II + +::tabs-start + +```python +class Solution: + def wiggleSort(self, nums: List[int]) -> None: + """ + Do not return anything, modify nums in-place instead. + """ + + for i in range(1, len(nums)): + if (i % 2) ^ (nums[i] > nums[i - 1]): + nums[i], nums[i - 1] = nums[i - 1], nums[i] +``` + +```java +public class Solution { + public void wiggleSort(int[] nums) { + for (int i = 1; i < nums.length; i++) { + if (((i % 2) ^ (nums[i] > nums[i - 1] ? 1 : 0)) != 0) { + int temp = nums[i]; + nums[i] = nums[i - 1]; + nums[i - 1] = temp; + } + } + } +} +``` + +```cpp +class Solution { +public: + void wiggleSort(vector& nums) { + for (int i = 1; i nums[i - 1])) { + swap(nums[i], nums[i - 1]); + } + } + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {void} Do not return anything, modify nums in-place instead. + */ + wiggleSort(nums) { + for(var i = 1; i < nums.length; i++) { + if ((i % 2) ^ (nums[i] > nums[i - 1])) { + [nums[i], nums[i - 1]] = [nums[i - 1], nums[i]]; + } + } + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ \ No newline at end of file From ff45c135f63b957d77fb2b7dcb30d8cdc6a4ec51 Mon Sep 17 00:00:00 2001 From: Sri Hari <94112314+Srihari2222@users.noreply.github.com> Date: Sat, 14 Dec 2024 21:28:12 +0530 Subject: [PATCH 16/45] Batch-4/Neetcode-All/Added-articles (#3766) --- articles/arranging-coins.md | 401 ++++++++++ articles/assign-cookies.md | 300 ++++++++ articles/backspace-string-compare.md | 506 +++++++++++++ articles/baseball-game.md | 223 ++++++ articles/binary-search.md | 2 +- ...eck-if-two-string-arrays-are-equivalent.md | 270 +++++++ articles/contains-duplicate-ii.md | 259 +++++++ ...d-first-palindromic-string-in-the-array.md | 147 ++++ .../frequency-of-the-most-frequent-element.md | 420 +++++++++++ articles/fruit-into-baskets.md | 468 ++++++++++++ articles/guess-number-higher-or-lower.md | 352 +++++++++ ...f-vowels-in-a-substring-of-given-length.md | 385 ++++++++++ articles/merge-sorted-array.md | 374 ++++++++++ articles/merge-strings-alternately.md | 250 +++++++ ...-between-highest-and-lowest-of-k-scores.md | 74 ++ ...s-to-make-the-binary-string-alternating.md | 701 ++++++++++++++++++ articles/minimum-size-subarray-sum.md | 332 +++++++++ articles/move-zeroes.md | 253 +++++++ ...rage-greater-than-or-equal-to-threshold.md | 398 ++++++++++ .../remove-duplicates-from-sorted-array.md | 214 ++++++ articles/reverse-string.md | 357 +++++++++ articles/reverse-words-in-a-string-iii.md | 387 ++++++++++ articles/search-insert-position.md | 396 ++++++++++ articles/sort-array-by-parity.md | 325 ++++++++ articles/squares-of-a-sorted-array.md | 266 +++++++ articles/valid-palindrome-ii.md | 395 ++++++++++ 26 files changed, 8454 insertions(+), 1 deletion(-) create mode 100644 articles/arranging-coins.md create mode 100644 articles/assign-cookies.md create mode 100644 articles/backspace-string-compare.md create mode 100644 articles/baseball-game.md create mode 100644 articles/check-if-two-string-arrays-are-equivalent.md create mode 100644 articles/contains-duplicate-ii.md create mode 100644 articles/find-first-palindromic-string-in-the-array.md create mode 100644 articles/frequency-of-the-most-frequent-element.md create mode 100644 articles/fruit-into-baskets.md create mode 100644 articles/guess-number-higher-or-lower.md create mode 100644 articles/maximum-number-of-vowels-in-a-substring-of-given-length.md create mode 100644 articles/merge-sorted-array.md create mode 100644 articles/merge-strings-alternately.md create mode 100644 articles/minimum-difference-between-highest-and-lowest-of-k-scores.md create mode 100644 articles/minimum-number-of-flips-to-make-the-binary-string-alternating.md create mode 100644 articles/minimum-size-subarray-sum.md create mode 100644 articles/move-zeroes.md create mode 100644 articles/number-of-sub-arrays-of-size-k-and-average-greater-than-or-equal-to-threshold.md create mode 100644 articles/remove-duplicates-from-sorted-array.md create mode 100644 articles/reverse-string.md create mode 100644 articles/reverse-words-in-a-string-iii.md create mode 100644 articles/search-insert-position.md create mode 100644 articles/sort-array-by-parity.md create mode 100644 articles/squares-of-a-sorted-array.md create mode 100644 articles/valid-palindrome-ii.md diff --git a/articles/arranging-coins.md b/articles/arranging-coins.md new file mode 100644 index 000000000..cb8920a4a --- /dev/null +++ b/articles/arranging-coins.md @@ -0,0 +1,401 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def arrangeCoins(self, n: int) -> int: + row = 0 + while n - row > 0: + row += 1 + n -= row + return row +``` + +```java +public class Solution { + public int arrangeCoins(int n) { + int row = 0; + while (n - row > 0) { + row++; + n -= row; + } + return row; + } +} +``` + +```cpp +class Solution { +public: + int arrangeCoins(int n) { + int row = 0; + while (n - row > 0) { + row++; + n -= row; + } + return row; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {number} + */ + arrangeCoins(n) { + let row = 0; + while (n - row > 0) { + row++; + n -= row; + } + return row; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(\sqrt {n})$ +* Space complexity: $O(1)$ + +--- + +## 2. Binary Search + +::tabs-start + +```python +class Solution: + def arrangeCoins(self, n: int) -> int: + l, r = 1, n + res = 0 + + while l <= r: + mid = (l + r) // 2 + coins = (mid * (mid + 1)) // 2 + if coins > n: + r = mid - 1 + else: + l = mid + 1 + res = max(res, mid) + + return res +``` + +```java +public class Solution { + public int arrangeCoins(int n) { + int l = 1, r = n, res = 0; + + while (l <= r) { + int mid = l + (r - l) / 2; + long coins = (long) mid * (mid + 1) / 2; + if (coins > n) { + r = mid - 1; + } else { + l = mid + 1; + res = Math.max(res, mid); + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int arrangeCoins(int n) { + long long l = 1, r = n, res = 0; + + while (l <= r) { + long long mid = l + (r - l) / 2; + long long coins = (mid * (mid + 1)) / 2; + if (coins > n) { + r = mid - 1; + } else { + l = mid + 1; + res = max(res, mid); + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {number} + */ + arrangeCoins(n) { + let l = 1, r = n, res = 0; + + while (l <= r) { + let mid = Math.floor((l + r) / 2); + let coins = (mid * (mid + 1)) / 2; + if (coins > n) { + r = mid - 1; + } else { + l = mid + 1; + res = Math.max(res, mid); + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(\log n)$ +* Space complexity: $O(1)$ + +--- + +## 3. Binary Search (Optimal) + +::tabs-start + +```python +class Solution: + def arrangeCoins(self, n: int) -> int: + if n <= 3: + return n if n == 1 else n - 1 + + l, r = 1, (n // 2) + 1 + while l < r: + mid = (l + r) // 2 + if (mid * (mid + 1)) // 2 <= n: + l = mid + 1 + else: + r = mid + + return l - 1 +``` + +```java +public class Solution { + public int arrangeCoins(int n) { + if (n <= 3) { + return n == 1 ? 1 : n - 1; + } + + int l = 1, r = (n / 2) + 1; + while (l < r) { + int mid = l + (r - l) / 2; + long coins = (long) mid * (mid + 1) / 2; + if (coins <= n) { + l = mid + 1; + } else { + r = mid; + } + } + + return l - 1; + } +} +``` + +```cpp +class Solution { +public: + int arrangeCoins(int n) { + if (n <= 3) { + return n == 1 ? 1 : n - 1; + } + + int l = 1, r = (n / 2) + 1; + while (l < r) { + int mid = l + (r - l) / 2; + long long coins = (mid * (mid + 1LL)) / 2; + if (coins <= n) { + l = mid + 1; + } else { + r = mid; + } + } + + return l - 1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {number} + */ + arrangeCoins(n) { + if (n <= 3) { + return n == 1 ? 1 : n - 1; + } + + let l = 1, r = (n / 2) + 1; + while (l < r) { + let mid = Math.floor((l + r) / 2); + let coins = (mid * (mid + 1)) / 2; + if (coins <= n) { + l = mid + 1; + } else { + r = mid; + } + } + + return l - 1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(\log n)$ +* Space complexity: $O(1)$ + +--- + +## 4. Bit Manipulation + +::tabs-start + +```python +class Solution: + def arrangeCoins(self, n: int) -> int: + mask = 1 << 15 + rows = 0 + while mask > 0: + rows |= mask + coins = rows * (rows + 1) // 2 + if coins > n: + rows ^= mask + mask >>= 1 + return rows +``` + +```java +public class Solution { + public int arrangeCoins(int n) { + int mask = 1 << 15; + int rows = 0; + while (mask > 0) { + rows |= mask; + long coins = (long) rows * (rows + 1) / 2; + if (coins > n) { + rows ^= mask; + } + mask >>= 1; + } + return rows; + } +} +``` + +```cpp +class Solution { +public: + int arrangeCoins(int n) { + int mask = 1 << 15; + int rows = 0; + while (mask > 0) { + rows |= mask; + long long coins = (long long) rows * (rows + 1) / 2; + if (coins > n) { + rows ^= mask; + } + mask >>= 1; + } + return rows; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {number} + */ + arrangeCoins(n) { + let mask = 1 << 15; + let rows = 0; + while (mask > 0) { + rows |= mask; + let coins = (rows * (rows + 1)) / 2; + if (coins > n) { + rows ^= mask; + } + mask >>= 1; + } + return rows; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(1)$ since we iterate $15$ times. +* Space complexity: $O(1)$ + +--- + +## 5. Math + +::tabs-start + +```python +class Solution: + def arrangeCoins(self, n: int) -> int: + return int(sqrt(2 * n + 0.25) - 0.5) +``` + +```java +public class Solution { + public int arrangeCoins(int n) { + return (int) (Math.sqrt(2L * n + 0.25) - 0.5); + } +} +``` + +```cpp +class Solution { +public: + int arrangeCoins(int n) { + return (int)(sqrt(2.0 * n + 0.25) - 0.5); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {number} + */ + arrangeCoins(n) { + return Math.floor(Math.sqrt(2 * n + 0.25) - 0.5); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(1)$ or $O(\sqrt {n})$ depending on the language. +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/assign-cookies.md b/articles/assign-cookies.md new file mode 100644 index 000000000..b591a8ef9 --- /dev/null +++ b/articles/assign-cookies.md @@ -0,0 +1,300 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def findContentChildren(self, g: List[int], s: List[int]) -> int: + s.sort() + res = 0 + + for i in g: + minIdx = -1 + for j in range(len(s)): + if s[j] < i: + continue + + if minIdx == -1 or s[minIdx] > s[j]: + minIdx = j + + if minIdx != -1: + s[minIdx] = -1 + res += 1 + + return res +``` + +```java +public class Solution { + public int findContentChildren(int[] g, int[] s) { + Arrays.sort(s); + int res = 0; + + for (int i : g) { + int minIdx = -1; + for (int j = 0; j < s.length; j++) { + if (s[j] < i) continue; + + if (minIdx == -1 || s[minIdx] > s[j]) { + minIdx = j; + } + } + + if (minIdx != -1) { + s[minIdx] = -1; + res++; + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int findContentChildren(vector& g, vector& s) { + sort(s.begin(), s.end()); + int res = 0; + + for (int i : g) { + int minIdx = -1; + for (int j = 0; j < s.size(); j++) { + if (s[j] < i) continue; + + if (minIdx == -1 || s[minIdx] > s[j]) { + minIdx = j; + } + } + + if (minIdx != -1) { + s[minIdx] = -1; + res++; + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} g + * @param {number[]} s + * @return {number} + */ + findContentChildren(g, s) { + s.sort((a, b) => a - b); + let res = 0; + + for (let i of g) { + let minIdx = -1; + for (let j = 0; j < s.length; j++) { + if (s[j] < i) continue; + + if (minIdx === -1 || s[minIdx] > s[j]) { + minIdx = j; + } + } + + if (minIdx !== -1) { + s[minIdx] = -1; + res++; + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * m + m \log m)$ +* Space complexity: $O(1)$ or $O(m)$ depending on the sorting algorithm. + +> Where $n$ is the size of the array $g$ and $m$ is the size of the array $s$. + +--- + +## 2. Two Pointers - I + +::tabs-start + +```python +class Solution: + def findContentChildren(self, g: List[int], s: List[int]) -> int: + g.sort() + s.sort() + + i = j = 0 + while i < len(g): + while j < len(s) and g[i] > s[j]: + j += 1 + if j == len(s): + break + i += 1 + j += 1 + return i +``` + +```java +public class Solution { + public int findContentChildren(int[] g, int[] s) { + Arrays.sort(g); + Arrays.sort(s); + + int i = 0, j = 0; + while (i < g.length) { + while (j < s.length && g[i] > s[j]) { + j++; + } + if (j == s.length) break; + i++; + j++; + } + return i; + } +} +``` + +```cpp +class Solution { +public: + int findContentChildren(vector& g, vector& s) { + sort(g.begin(), g.end()); + sort(s.begin(), s.end()); + + int i = 0, j = 0; + while (i < g.size()) { + while (j < s.size() && g[i] > s[j]) { + j++; + } + if (j == s.size()) break; + i++; + j++; + } + return i; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} g + * @param {number[]} s + * @return {number} + */ + findContentChildren(g, s) { + g.sort((a, b) => a - b); + s.sort((a, b) => a - b); + + let i = 0, j = 0; + while (i < g.length) { + while (j < s.length && g[i] > s[j]) { + j++; + } + if (j === s.length) break; + i++; + j++; + } + return i; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n + m \log m)$ +* Space complexity: $O(1)$ or $O(n + m)$ depending on the sorting algorithm. + +> Where $n$ is the size of the array $g$ and $m$ is the size of the array $s$. + +--- + +## 3. Two Pointers - II + +::tabs-start + +```python +class Solution: + def findContentChildren(self, g: List[int], s: List[int]) -> int: + g.sort() + s.sort() + + i = j = 0 + while i < len(g) and j < len(s): + if g[i] <= s[j]: + i += 1 + j += 1 + + return i +``` + +```java +public class Solution { + public int findContentChildren(int[] g, int[] s) { + Arrays.sort(g); + Arrays.sort(s); + + int i = 0; + for (int j = 0; i < g.length && j < s.length; j++) { + if (g[i] <= s[j]) i++; + } + return i; + } +} +``` + +```cpp +class Solution { +public: + int findContentChildren(vector& g, vector& s) { + sort(g.begin(), g.end()); + sort(s.begin(), s.end()); + + int i = 0; + for (int j = 0; i < g.size() && j < s.size(); j++) { + if (g[i] <= s[j]) i++; + } + return i; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} g + * @param {number[]} s + * @return {number} + */ + findContentChildren(g, s) { + g.sort((a, b) => a - b); + s.sort((a, b) => a - b); + + let i = 0; + for (let j = 0; i < g.length && j < s.length; j++) { + if (g[i] <= s[j]) i++; + } + return i; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n + m \log m)$ +* Space complexity: $O(1)$ or $O(n + m)$ depending on the sorting algorithm. + +> Where $n$ is the size of the array $g$ and $m$ is the size of the array $s$. \ No newline at end of file diff --git a/articles/backspace-string-compare.md b/articles/backspace-string-compare.md new file mode 100644 index 000000000..6064e5bd6 --- /dev/null +++ b/articles/backspace-string-compare.md @@ -0,0 +1,506 @@ +## 1. Stack + +::tabs-start + +```python +class Solution: + def backspaceCompare(self, s: str, t: str) -> bool: + def convert(s): + res = [] + for char in s: + if char == '#': + if res: + res.pop() + else: + res.append(char) + return "".join(res) + + return convert(s) == convert(t) +``` + +```java +public class Solution { + public boolean backspaceCompare(String s, String t) { + return convert(s).equals(convert(t)); + } + + private List convert(String s) { + List res = new ArrayList<>(); + for (char c : s.toCharArray()) { + if (c == '#') { + if (!res.isEmpty()) { + res.remove(res.size() - 1); + } + } else { + res.add(c); + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + bool backspaceCompare(string s, string t) { + return convert(s) == convert(t); + } + +private: + string convert(const string& s) { + string res = ""; + for (char c : s) { + if (c == '#') { + if (!res.empty()) { + res.pop_back(); + } + } else { + res += c; + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @param {string} t + * @return {boolean} + */ + backspaceCompare(s, t) { + const convert = (str) => { + const res = []; + for (const char of str) { + if (char === '#') { + if (res.length > 0) { + res.pop(); + } + } else { + res.push(char); + } + } + return res.join(''); + }; + return convert(s) === convert(t); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n + m)$ +* Space complexity: $O(n + m)$ + +> Where $n$ is the length of the string $s$ and $m$ is the length of the string $t$. + +--- + +## 2. Reverse iteration + +::tabs-start + +```python +class Solution: + def backspaceCompare(self, s: str, t: str) -> bool: + def convert(s): + res = [] + backspace = 0 + for i in range(len(s) - 1, -1, -1): + if s[i] == '#': + backspace += 1 + elif backspace: + backspace -= 1 + else: + res.append(s[i]) + return res + + return convert(s) == convert(t) +``` + +```java +public class Solution { + public boolean backspaceCompare(String s, String t) { + return convert(s).equals(convert(t)); + } + + private List convert(String s) { + List res = new ArrayList<>(); + int backspace = 0; + for (int i = s.length() - 1; i >= 0; i--) { + if (s.charAt(i) == '#') { + backspace++; + } else if (backspace > 0) { + backspace--; + } else { + res.add(s.charAt(i)); + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + bool backspaceCompare(string s, string t) { + return convert(s) == convert(t); + } + +private: + string convert(string s) { + string res; + int backspace = 0; + for (int i = s.size() - 1; i >= 0; i--) { + if (s[i] == '#') { + backspace++; + } else if (backspace > 0) { + backspace--; + } else { + res += s[i]; + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @param {string} t + * @return {boolean} + */ + backspaceCompare(s, t) { + const convert = (s) => { + const res = []; + let backspace = 0; + for (let i = s.length - 1; i >= 0; i--) { + if (s[i] === '#') { + backspace++; + } else if (backspace > 0) { + backspace--; + } else { + res.push(s[i]); + } + } + return res.join(''); + }; + return convert(s) === convert(t); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n + m)$ +* Space complexity: $O(n + m)$ + +> Where $n$ is the length of the string $s$ and $m$ is the length of the string $t$. + +--- + +## 3. Two Pointers - I + +::tabs-start + +```python +class Solution: + def backspaceCompare(self, s: str, t: str) -> bool: + def nextValidChar(string, index): + backspace = 0 + while index >= 0: + if string[index] == '#': + backspace += 1 + elif backspace > 0: + backspace -= 1 + else: + break + index -= 1 + return index + + index_s, index_t = len(s) - 1, len(t) - 1 + + while index_s >= 0 or index_t >= 0: + index_s = nextValidChar(s, index_s) + index_t = nextValidChar(t, index_t) + + char_s = s[index_s] if index_s >= 0 else "" + char_t = t[index_t] if index_t >= 0 else "" + + if char_s != char_t: + return False + + index_s -= 1 + index_t -= 1 + + return True +``` + +```java +public class Solution { + public boolean backspaceCompare(String s, String t) { + int indexS = s.length() - 1, indexT = t.length() - 1; + + while (indexS >= 0 || indexT >= 0) { + indexS = nextValidChar(s, indexS); + indexT = nextValidChar(t, indexT); + + char charS = indexS >= 0 ? s.charAt(indexS) : '\0'; + char charT = indexT >= 0 ? t.charAt(indexT) : '\0'; + + if (charS != charT) return false; + + indexS--; + indexT--; + } + + return true; + } + + private int nextValidChar(String str, int index) { + int backspace = 0; + + while (index >= 0) { + if (str.charAt(index) == '#') { + backspace++; + } else if (backspace > 0) { + backspace--; + } else { + break; + } + index--; + } + + return index; + } +} +``` + +```cpp +class Solution { +public: + bool backspaceCompare(string s, string t) { + int indexS = s.size() - 1, indexT = t.size() - 1; + + while (indexS >= 0 || indexT >= 0) { + indexS = nextValidChar(s, indexS); + indexT = nextValidChar(t, indexT); + + char charS = indexS >= 0 ? s[indexS] : '\0'; + char charT = indexT >= 0 ? t[indexT] : '\0'; + + if (charS != charT) return false; + + indexS--; + indexT--; + } + + return true; + } + +private: + int nextValidChar(string &str, int index) { + int backspace = 0; + + while (index >= 0) { + if (str[index] == '#') { + backspace++; + } else if (backspace > 0) { + backspace--; + } else { + break; + } + index--; + } + + return index; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @param {string} t + * @return {boolean} + */ + backspaceCompare(s, t) { + const nextValidChar = (str, index) => { + let backspace = 0; + + while (index >= 0) { + if (str[index] === '#') { + backspace++; + } else if (backspace > 0) { + backspace--; + } else { + break; + } + index--; + } + + return index; + }; + + let indexS = s.length - 1, indexT = t.length - 1; + + while (indexS >= 0 || indexT >= 0) { + indexS = nextValidChar(s, indexS); + indexT = nextValidChar(t, indexT); + + const charS = indexS >= 0 ? s[indexS] : ''; + const charT = indexT >= 0 ? t[indexT] : ''; + + if (charS !== charT) return false; + + indexS--; + indexT--; + } + + return true; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n + m)$ +* Space complexity: $O(1)$ + +> Where $n$ is the length of the string $s$ and $m$ is the length of the string $t$. + +--- + +## 4. Two Pointers - II + +::tabs-start + +```python +class Solution: + def backspaceCompare(self, s: str, t: str) -> bool: + index_s, index_t = len(s) - 1, len(t) - 1 + backspace_s = backspace_t = 0 + + while True: + while index_s >= 0 and (backspace_s or s[index_s] == '#'): + backspace_s += 1 if s[index_s] == '#' else -1 + index_s -= 1 + + while index_t >= 0 and (backspace_t or t[index_t] == '#'): + backspace_t += 1 if t[index_t] == '#' else -1 + index_t -= 1 + + if not (index_s >= 0 and index_t >= 0 and s[index_s] == t[index_t]): + return index_s == index_t == -1 + index_s, index_t = index_s - 1, index_t - 1 +``` + +```java +public class Solution { + public boolean backspaceCompare(String s, String t) { + int indexS = s.length() - 1, indexT = t.length() - 1; + int backspaceS = 0, backspaceT = 0; + + while (true) { + while (indexS >= 0 && (backspaceS > 0 || s.charAt(indexS) == '#')) { + backspaceS += s.charAt(indexS) == '#' ? 1 : -1; + indexS--; + } + + while (indexT >= 0 && (backspaceT > 0 || t.charAt(indexT) == '#')) { + backspaceT += t.charAt(indexT) == '#' ? 1 : -1; + indexT--; + } + + if (!(indexS >= 0 && indexT >= 0 && s.charAt(indexS) == t.charAt(indexT))) { + return indexS == -1 && indexT == -1; + } + indexS--; + indexT--; + } + } +} +``` + +```cpp +class Solution { +public: + bool backspaceCompare(string s, string t) { + int indexS = s.size() - 1, indexT = t.size() - 1; + int backspaceS = 0, backspaceT = 0; + + while (true) { + + while (indexS >= 0 && (backspaceS > 0 || s[indexS] == '#')) { + backspaceS += (s[indexS] == '#') ? 1 : -1; + indexS--; + } + + while (indexT >= 0 && (backspaceT > 0 || t[indexT] == '#')) { + backspaceT += (t[indexT] == '#') ? 1 : -1; + indexT--; + } + + if (!(indexS >= 0 && indexT >= 0 && s[indexS] == t[indexT])) { + return indexS == -1 && indexT == -1; + } + indexS--; + indexT--; + } + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @param {string} t + * @return {boolean} + */ + backspaceCompare(s, t) { + let indexS = s.length - 1, indexT = t.length - 1; + let backspaceS = 0, backspaceT = 0; + + while (true) { + while (indexS >= 0 && (backspaceS > 0 || s[indexS] === '#')) { + backspaceS += s[indexS] === '#' ? 1 : -1; + indexS--; + } + + while (indexT >= 0 && (backspaceT > 0 || t[indexT] === '#')) { + backspaceT += t[indexT] === '#' ? 1 : -1; + indexT--; + } + + if (!(indexS >= 0 && indexT >= 0 && s.charAt(indexS) === t.charAt(indexT))) { + return indexS === -1 && indexT === -1; + } + indexS--; + indexT--; + } + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n + m)$ +* Space complexity: $O(1)$ + +> Where $n$ is the length of the string $s$ and $m$ is the length of the string $t$. \ No newline at end of file diff --git a/articles/baseball-game.md b/articles/baseball-game.md new file mode 100644 index 000000000..2d04f4311 --- /dev/null +++ b/articles/baseball-game.md @@ -0,0 +1,223 @@ +## 1. Stack + +::tabs-start + +```python +class Solution: + def calPoints(self, operations: List[str]) -> int: + stack = [] + for op in operations: + if op == "+": + stack.append(stack[-1] + stack[-2]) + elif op == "D": + stack.append(2 * stack[-1]) + elif op == "C": + stack.pop() + else: + stack.append(int(op)) + return sum(stack) +``` + +```java +public class Solution { + public int calPoints(String[] operations) { + Stack stack = new Stack<>(); + for (String op : operations) { + if (op.equals("+")) { + int top = stack.pop(); + int newTop = top + stack.peek(); + stack.push(top); + stack.push(newTop); + } else if (op.equals("D")) { + stack.push(2 * stack.peek()); + } else if (op.equals("C")) { + stack.pop(); + } else { + stack.push(Integer.parseInt(op)); + } + } + int sum = 0; + for (int score : stack) { + sum += score; + } + return sum; + } +} +``` + +```cpp +class Solution { +public: + int calPoints(vector& operations) { + vector stack; + for (const string& op : operations) { + if (op == "+") { + int top = stack.back(); stack.pop_back(); + int newTop = top + stack.back(); + stack.push_back(top); + stack.push_back(newTop); + } else if (op == "D") { + stack.push_back(2 * stack.back()); + } else if (op == "C") { + stack.pop_back(); + } else { + stack.push_back(stoi(op)); + } + } + return accumulate(stack.begin(), stack.end(), 0); + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} operations + * @return {number} + */ + calPoints(operations) { + const stack = []; + for (const op of operations) { + if (op === "+") { + const top = stack.pop(); + const newTop = top + stack[stack.length - 1]; + stack.push(top); + stack.push(newTop); + } else if (op === "D") { + stack.push(2 * stack[stack.length - 1]); + } else if (op === "C") { + stack.pop(); + } else { + stack.push(parseInt(op)); + } + } + return stack.reduce((a, b) => a + b, 0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Stack - II + +::tabs-start + +```python +class Solution: + def calPoints(self, operations: List[str]) -> int: + stack, res = [], 0 + for op in operations: + if op == "+": + res += stack[-1] + stack[-2] + stack.append(stack[-1] + stack[-2]) + elif op == "D": + res += (2 * stack[-1]) + stack.append(2 * stack[-1]) + elif op == "C": + res -= stack.pop() + else: + res += int(op) + stack.append(int(op)) + return res +``` + +```java +public class Solution { + public int calPoints(String[] ops) { + int res = 0; + Stack stack = new Stack<>(); + for (String op : ops) { + if (op.equals("+")) { + int top = stack.pop(); + int newTop = top + stack.peek(); + stack.push(top); + stack.push(newTop); + res += newTop; + } else if (op.equals("D")) { + stack.push(2 * stack.peek()); + res += stack.peek(); + } else if (op.equals("C")) { + res -= stack.pop(); + } else { + stack.push(Integer.parseInt(op)); + res += stack.peek(); + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int calPoints(vector& ops) { + stack stack; + int res = 0; + for (const string& op : ops) { + if (op == "+") { + int top = stack.top(); stack.pop(); + int newTop = top + stack.top(); + stack.push(top); + stack.push(newTop); + res += newTop; + } else if (op == "D") { + stack.push(2 * stack.top()); + res += stack.top(); + } else if (op == "C") { + res -= stack.top(); + stack.pop(); + } else { + stack.push(stoi(op)); + res += stack.top(); + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} operations + * @return {number} + */ + calPoints(operations) { + const stack = []; + let res = 0; + for (const op of operations) { + if (op === "+") { + const top = stack.pop(); + const newTop = top + stack[stack.length - 1]; + stack.push(top); + stack.push(newTop); + res += newTop; + } else if (op === "D") { + stack.push(2 * stack[stack.length - 1]); + res += stack[stack.length - 1]; + } else if (op === "C") { + res -= stack.pop(); + } else { + stack.push(parseInt(op)); + res += stack[stack.length - 1]; + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/binary-search.md b/articles/binary-search.md index a05c98fd0..5e88782ce 100644 --- a/articles/binary-search.md +++ b/articles/binary-search.md @@ -591,7 +591,7 @@ class Solution { --- -## 5. Built-In Tool +## 5. Built-In Function ::tabs-start diff --git a/articles/check-if-two-string-arrays-are-equivalent.md b/articles/check-if-two-string-arrays-are-equivalent.md new file mode 100644 index 000000000..39805de58 --- /dev/null +++ b/articles/check-if-two-string-arrays-are-equivalent.md @@ -0,0 +1,270 @@ +## 1. Concatenate Strings + +::tabs-start + +```python +class Solution: + def arrayStringsAreEqual(self, word1: List[str], word2: List[str]) -> bool: + return "".join(word1) == "".join(word2) +``` + +```java +public class Solution { + public boolean arrayStringsAreEqual(String[] word1, String[] word2) { + return String.join("", word1).equals(String.join("", word2)); + } +} +``` + +```cpp +class Solution { +public: + bool arrayStringsAreEqual(vector& word1, vector& word2) { + string str1 = accumulate(word1.begin(), word1.end(), string()); + string str2 = accumulate(word2.begin(), word2.end(), string()); + return str1 == str2; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} word1 + * @param {string[]} word2 + * @return {boolean} + */ + arrayStringsAreEqual(word1, word2) { + return word1.join("") === word2.join(""); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n + m)$ +* Space complexity: $O(n + m)$ + +> Where $n$ and $m$ are the total number of characters in both the arrays $word1$ and $word2$, respectively. + +--- + +## 2. Concatenate Strings Of One Array + +::tabs-start + +```python +class Solution: + def arrayStringsAreEqual(self, word1: List[str], word2: List[str]) -> bool: + s1 = "".join(word1) + i = 0 + for w in word2: + for c in w: + if i == len(s1) or s1[i] != c: + return False + i += 1 + return i == len(s1) +``` + +```java +public class Solution { + public boolean arrayStringsAreEqual(String[] word1, String[] word2) { + StringBuilder s1 = new StringBuilder(); + for (String w : word1) { + s1.append(w); + } + + int i = 0; + for (String w : word2) { + for (char c : w.toCharArray()) { + if (i == s1.length() || s1.charAt(i) != c) { + return false; + } + i++; + } + } + return i == s1.length(); + } +} +``` + +```cpp +class Solution { +public: + bool arrayStringsAreEqual(vector& word1, vector& word2) { + string s1 = ""; + for (string w : word1) s1 += w; + + int i = 0; + for (string w : word2) { + for (char c : w) { + if (i == s1.length() || s1[i] != c) return false; + i++; + } + } + return i == s1.length(); + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} word1 + * @param {string[]} word2 + * @return {boolean} + */ + arrayStringsAreEqual(word1, word2) { + let s1 = word1.join(""); + let i = 0; + + for (let w of word2) { + for (let c of w) { + if (i === s1.length || s1[i] !== c) { + return false; + } + i++; + } + } + return i === s1.length; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n + m)$ +* Space complexity: $O(n)$ + +> Where $n$ and $m$ are the total number of characters in both the arrays $word1$ and $word2$, respectively. + +--- + +## 3. Two Pointers + +::tabs-start + +```python +class Solution: + def arrayStringsAreEqual(self, word1: List[str], word2: List[str]) -> bool: + w1 = w2 = 0 # Index of word + i = j = 0 # Index of character + + while w1 < len(word1) and w2 < len(word2): + if word1[w1][i] != word2[w2][j]: + return False + + i, j = i + 1, j + 1 + + if i == len(word1[w1]): + w1 += 1 + i = 0 + if j == len(word2[w2]): + w2 += 1 + j = 0 + + return w1 == len(word1) and w2 == len(word2) +``` + +```java +public class Solution { + public boolean arrayStringsAreEqual(String[] word1, String[] word2) { + int w1 = 0, w2 = 0; // Index of word + int i = 0, j = 0; // Index of character + + while (w1 < word1.length && w2 < word2.length) { + if (word1[w1].charAt(i) != word2[w2].charAt(j)) { + return false; + } + + i++; + j++; + + if (i == word1[w1].length()) { + w1++; + i = 0; + } + if (j == word2[w2].length()) { + w2++; + j = 0; + } + } + return w1 == word1.length && w2 == word2.length; + } +} +``` + +```cpp +class Solution { +public: + bool arrayStringsAreEqual(vector& word1, vector& word2) { + int w1 = 0, w2 = 0; // Index of word + int i = 0, j = 0; // Index of character + + while (w1 < word1.size() && w2 < word2.size()) { + if (word1[w1][i] != word2[w2][j]) { + return false; + } + + i++; + j++; + + if (i == word1[w1].size()) { + w1++; + i = 0; + } + if (j == word2[w2].size()) { + w2++; + j = 0; + } + } + return w1 == word1.size() && w2 == word2.size(); + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} word1 + * @param {string[]} word2 + * @return {boolean} + */ + arrayStringsAreEqual(word1, word2) { + let w1 = 0, w2 = 0; // Index of word + let i = 0, j = 0; // Index of character + + while (w1 < word1.length && w2 < word2.length) { + if (word1[w1][i] !== word2[w2][j]) { + return false; + } + + i++; + j++; + + if (i === word1[w1].length) { + w1++; + i = 0; + } + if (j === word2[w2].length) { + w2++; + j = 0; + } + } + return w1 === word1.length && w2 === word2.length; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n + m)$ +* Space complexity: $O(1)$ extra space. + +> Where $n$ and $m$ are the total number of characters in both the arrays $word1$ and $word2$, respectively. \ No newline at end of file diff --git a/articles/contains-duplicate-ii.md b/articles/contains-duplicate-ii.md new file mode 100644 index 000000000..9e1c9367e --- /dev/null +++ b/articles/contains-duplicate-ii.md @@ -0,0 +1,259 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def containsNearbyDuplicate(self, nums: List[int], k: int) -> bool: + for L in range(len(nums)): + for R in range(L + 1, min(len(nums), L + k + 1)): + if nums[L] == nums[R]: + return True + return False +``` + +```java +public class Solution { + public boolean containsNearbyDuplicate(int[] nums, int k) { + for (int L = 0; L < nums.length; L++) { + for (int R = L + 1; R < Math.min(nums.length, L + k + 1); R++) { + if (nums[L] == nums[R]) { + return true; + } + } + } + return false; + } +} +``` + +```cpp +class Solution { +public: + bool containsNearbyDuplicate(vector& nums, int k) { + for (int L = 0; L < nums.size(); L++) { + for (int R = L + 1; R < min((int)nums.size(), L + k + 1); R++) { + if (nums[L] == nums[R]) { + return true; + } + } + } + return false; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {boolean} + */ + containsNearbyDuplicate(nums, k) { + for (let L = 0; L < nums.length; L++) { + for (let R = L + 1; R < Math.min(nums.length, L + k + 1); R++) { + if (nums[L] === nums[R]) { + return true; + } + } + } + return false; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * min(n, k))$ +* Space complexity: $O(1)$ + +> Where $n$ is the size of the array $nums$ and $k$ is the maximum distance between two equal numbers. + +--- + +## 2. Hash Map + +::tabs-start + +```python +class Solution: + def containsNearbyDuplicate(self, nums: List[int], k: int) -> bool: + mp = {} + + for i in range(len(nums)): + if nums[i] in mp and i - mp[nums[i]] <= k: + return True + mp[nums[i]] = i + + return False +``` + +```java +public class Solution { + public boolean containsNearbyDuplicate(int[] nums, int k) { + Map map = new HashMap<>(); + + for (int i = 0; i < nums.length; i++) { + if (map.containsKey(nums[i]) && i - map.get(nums[i]) <= k) { + return true; + } + map.put(nums[i], i); + } + + return false; + } +} +``` + +```cpp +class Solution { +public: + bool containsNearbyDuplicate(vector& nums, int k) { + unordered_map mp; + + for (int i = 0; i < nums.size(); i++) { + if (mp.find(nums[i]) != mp.end() && i - mp[nums[i]] <= k) { + return true; + } + mp[nums[i]] = i; + } + + return false; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {boolean} + */ + containsNearbyDuplicate(nums, k) { + const map = new Map(); + + for (let i = 0; i < nums.length; i++) { + if (map.has(nums[i]) && i - map.get(nums[i]) <= k) { + return true; + } + map.set(nums[i], i); + } + + return false; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +> Where $n$ is the size of the array $nums$ and $k$ is the maximum distance between two equal numbers. + +--- + +## 3. Hash Set + +::tabs-start + +```python +class Solution: + def containsNearbyDuplicate(self, nums: List[int], k: int) -> bool: + window = set() + L = 0 + + for R in range(len(nums)): + if R - L > k: + window.remove(nums[L]) + L += 1 + if nums[R] in window: + return True + window.add(nums[R]) + + return False +``` + +```java +public class Solution { + public boolean containsNearbyDuplicate(int[] nums, int k) { + Set window = new HashSet<>(); + int L = 0; + + for (int R = 0; R < nums.length; R++) { + if (R - L > k) { + window.remove(nums[L]); + L++; + } + if (window.contains(nums[R])) { + return true; + } + window.add(nums[R]); + } + return false; + } +} +``` + +```cpp +class Solution { +public: + bool containsNearbyDuplicate(vector& nums, int k) { + unordered_set window; + int L = 0; + + for (int R = 0; R < nums.size(); R++) { + if (R - L > k) { + window.erase(nums[L]); + L++; + } + if (window.find(nums[R]) != window.end()) { + return true; + } + window.insert(nums[R]); + } + return false; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {boolean} + */ + containsNearbyDuplicate(nums, k) { + let window = new Set(); + let L = 0; + + for (let R = 0; R < nums.length; R++) { + if (R - L > k) { + window.delete(nums[L]); + L++; + } + if (window.has(nums[R])) { + return true; + } + window.add(nums[R]); + } + return false; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(min(n, k))$ + +> Where $n$ is the size of the array $nums$ and $k$ is the maximum distance between two equal numbers. \ No newline at end of file diff --git a/articles/find-first-palindromic-string-in-the-array.md b/articles/find-first-palindromic-string-in-the-array.md new file mode 100644 index 000000000..03dd3683d --- /dev/null +++ b/articles/find-first-palindromic-string-in-the-array.md @@ -0,0 +1,147 @@ +## 1. Reverse String + +::tabs-start + +```python +class Solution: + def firstPalindrome(self, words: List[str]) -> str: + for w in words: + if w == w[::-1]: + return w + return "" +``` + +```java +public class Solution { + public String firstPalindrome(String[] words) { + for (String w : words) { + if (w.equals(new StringBuilder(w).reverse().toString())) { + return w; + } + } + return ""; + } +} +``` + +```cpp +class Solution { +public: + string firstPalindrome(vector& words) { + for (const string& w : words) { + string rev = w; + reverse(rev.begin(), rev.end()); + if (w == rev) { + return w; + } + } + return ""; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} words + * @return {string} + */ + firstPalindrome(words) { + for (let w of words) { + if (w === w.split('').reverse().join('')) { + return w; + } + } + return ""; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * m)$ +* Space complexity: $O(m)$ + +> Where $n$ is the size of the string array $words$ and $m$ is the average length of a word in the array. + +--- + +## 2. Two Pointers + +::tabs-start + +```python +class Solution: + def firstPalindrome(self, words: List[str]) -> str: + for w in words: + l, r = 0, len(w) - 1 + while w[l] == w[r]: + if l >= r: + return w + l, r = l + 1, r - 1 + return "" +``` + +```java +public class Solution { + public String firstPalindrome(String[] words) { + for (String w : words) { + int l = 0, r = w.length() - 1; + while (w.charAt(l) == w.charAt(r)) { + if (l >= r) return w; + l++; + r--; + } + } + return ""; + } +} +``` + +```cpp +class Solution { +public: + string firstPalindrome(vector& words) { + for (const string& w : words) { + int l = 0, r = w.length() - 1; + while (w[l] == w[r]) { + if (l >= r) return w; + l++; + r--; + } + } + return ""; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} words + * @return {string} + */ + firstPalindrome(words) { + for (let w of words) { + let l = 0, r = w.length - 1; + while (w.charAt(l) === w.charAt(r)) { + if (l >= r) return w; + l++; + r--; + } + } + return ""; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * m)$ +* Space complexity: $O(1)$ extra space. + +> Where $n$ is the size of the string array $words$ and $m$ is the average length of a word in the array. \ No newline at end of file diff --git a/articles/frequency-of-the-most-frequent-element.md b/articles/frequency-of-the-most-frequent-element.md new file mode 100644 index 000000000..51c727d97 --- /dev/null +++ b/articles/frequency-of-the-most-frequent-element.md @@ -0,0 +1,420 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def maxFrequency(self, nums: List[int], k: int) -> int: + nums.sort() + res = 1 + for i in range(len(nums)): + j = i - 1 + tmpK = k + while j >= 0 and (tmpK - (nums[i] - nums[j])) >= 0: + tmpK -= (nums[i] - nums[j]) + j -= 1 + res = max(res, i - j) + return res +``` + +```java +public class Solution { + public int maxFrequency(int[] nums, int k) { + Arrays.sort(nums); + int res = 1; + + for (int i = 0; i < nums.length; i++) { + int j = i - 1; + long tmpK = k; + + while (j >= 0 && (tmpK - (nums[i] - nums[j])) >= 0) { + tmpK -= (nums[i] - nums[j]); + j--; + } + res = Math.max(res, i - j); + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int maxFrequency(vector& nums, int k) { + sort(nums.begin(), nums.end()); + int res = 1; + + for (int i = 0; i < nums.size(); i++) { + int j = i - 1; + long long tmpK = k; + + while (j >= 0 && (tmpK - (nums[i] - nums[j])) >= 0) { + tmpK -= (nums[i] - nums[j]); + j--; + } + res = max(res, i - j); + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {number} + */ + maxFrequency(nums, k) { + nums.sort((a, b) => a - b); + let res = 1; + + for (let i = 0; i < nums.length; i++) { + let j = i - 1; + let tmpK = k; + + while (j >= 0 && (tmpK - (nums[i] - nums[j])) >= 0) { + tmpK -= (nums[i] - nums[j]); + j--; + } + res = Math.max(res, i - j); + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2 + n \log n)$ +* Space complexity: $O(1)$ or $O(n)$ depending on the sorting algorithm. + +--- + +## 2. Prefix Sum + Binary Search + +::tabs-start + +```python +class Solution: + def maxFrequency(self, nums: List[int], k: int) -> int: + nums.sort() + n = len(nums) + prefix_sum = [0] * (n + 1) + for i in range(n): + prefix_sum[i + 1] = prefix_sum[i] + nums[i] + + res = 1 + for i in range(n): + l, r = 0, i + while l <= r: + m = (l + r) // 2 + curSum = prefix_sum[i + 1] - prefix_sum[m] + need = (i - m + 1) * nums[i] - curSum + if need <= k: + r = m - 1 + res = max(res, i - m + 1) + else: + l = m + 1 + return res +``` + +```java +public class Solution { + public int maxFrequency(int[] nums, int k) { + Arrays.sort(nums); + int n = nums.length; + long[] prefixSum = new long[n + 1]; + for (int i = 0; i < n; i++) { + prefixSum[i + 1] = prefixSum[i] + nums[i]; + } + + int res = 1; + for (int i = 0; i < n; i++) { + int left = 0, right = i; + while (left <= right) { + int mid = (left + right) / 2; + long curSum = prefixSum[i + 1] - prefixSum[mid]; + long need = (i - mid + 1) * 1L * nums[i] - curSum; + if (need <= k) { + right = mid - 1; + res = Math.max(res, i - mid + 1); + } else { + left = mid + 1; + } + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int maxFrequency(vector& nums, int k) { + sort(nums.begin(), nums.end()); + int n = nums.size(); + vector prefixSum(n + 1, 0); + for (int i = 0; i < n; ++i) { + prefixSum[i + 1] = prefixSum[i] + nums[i]; + } + + int res = 1; + for (int i = 0; i < n; ++i) { + int l = 0, r = i; + while (l <= r) { + int m = (l + r) / 2; + long long curSum = prefixSum[i + 1] - prefixSum[m]; + long long need = (i - m + 1) * 1LL * nums[i] - curSum; + if (need <= k) { + r = m - 1; + res = max(res, i - m + 1); + } else { + l = m + 1; + } + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {number} + */ + maxFrequency(nums, k) { + nums.sort((a, b) => a - b); + const prefixSum = new Array(nums.length + 1).fill(0); + for (let i = 0; i < nums.length; i++) { + prefixSum[i + 1] = prefixSum[i] + nums[i]; + } + + let res = 1; + for (let i = 0; i < nums.length; i++) { + let left = 0, right = i; + while (left <= right) { + const mid = Math.floor((left + right) / 2); + const curSum = prefixSum[i + 1] - prefixSum[mid]; + const need = (i - mid + 1) * nums[i] - curSum; + if (need <= k) { + right = mid - 1; + res = Math.max(res, i - mid + 1); + } else { + left = mid + 1; + } + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Sliding Window + +::tabs-start + +```python +class Solution: + def maxFrequency(self, nums: List[int], k: int) -> int: + nums.sort() + total = res = 0 + l = 0 + + for r in range(len(nums)): + total += nums[r] + while nums[r] * (r - l + 1) > total + k: + total -= nums[l] + l += 1 + res = max(res, r - l + 1) + + return res +``` + +```java +public class Solution { + public int maxFrequency(int[] nums, int k) { + Arrays.sort(nums); + long total = 0; + int res = 0; + int l = 0; + + for (int r = 0; r < nums.length; r++) { + total += nums[r]; + while ((long) nums[r] * (r - l + 1) > total + k) { + total -= nums[l]; + l++; + } + res = Math.max(res, r - l + 1); + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int maxFrequency(vector& nums, int k) { + sort(nums.begin(), nums.end()); + long long total = 0; + int res = 0, l = 0; + + for (int r = 0; r < nums.size(); ++r) { + total += nums[r]; + while ((long long)nums[r] * (r - l + 1) > total + k) { + total -= nums[l]; + l++; + } + res = max(res, r - l + 1); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {number} + */ + maxFrequency(nums, k) { + nums.sort((a, b) => a - b); + let total = 0, res = 0, l = 0; + + for (let r = 0; r < nums.length; r++) { + total += nums[r]; + while (nums[r] * (r - l + 1) > total + k) { + total -= nums[l]; + l++; + } + res = Math.max(res, r - l + 1); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(1)$ or $O(n)$ depending on the sorting algorithm. + +--- + +## 4. Advanced Sliding Window + +::tabs-start + +```python +class Solution: + def maxFrequency(self, nums: List[int], k: int) -> int: + nums.sort() + l = 0 + total = 0 + + for r in range(len(nums)): + total += nums[r] + + if (r - l + 1) * nums[r] > total + k: + total -= nums[l] + l += 1 + + return len(nums) - l +``` + +```java +public class Solution { + public int maxFrequency(int[] nums, int k) { + Arrays.sort(nums); + long total = 0; + int l = 0; + + for (int r = 0; r < nums.length; r++) { + total += nums[r]; + if ((r - l + 1) * 1L * nums[r] > total + k) { + total -= nums[l]; + l++; + } + } + + return nums.length - l; + } +} +``` + +```cpp +class Solution { +public: + int maxFrequency(vector& nums, int k) { + sort(nums.begin(), nums.end()); + long long total = 0; + int l = 0; + + for (int r = 0; r < nums.size(); ++r) { + total += nums[r]; + if ((r - l + 1) * 1L * nums[r] > total + k) { + total -= nums[l]; + l++; + } + } + + return nums.size() - l; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {number} + */ + maxFrequency(nums, k) { + nums.sort((a, b) => a - b); + let total = 0, l = 0; + + for (let r = 0; r < nums.length; r++) { + total += nums[r]; + if (nums[r] * (r - l + 1) > total + k) { + total -= nums[l]; + l++; + } + } + + return nums.length - l; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(1)$ or $O(n)$ depending on the sorting algorithm. \ No newline at end of file diff --git a/articles/fruit-into-baskets.md b/articles/fruit-into-baskets.md new file mode 100644 index 000000000..7aa1b5bbe --- /dev/null +++ b/articles/fruit-into-baskets.md @@ -0,0 +1,468 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def totalFruit(self, fruits: List[int]) -> int: + n = len(fruits) + res = 0 + for i in range(n): + types = set() + j = i + while j < n and (len(types) < 2 or fruits[j] in types): + types.add(fruits[j]) + j += 1 + res = max(res, j - i) + return res +``` + +```java +public class Solution { + public int totalFruit(int[] fruits) { + int n = fruits.length, res = 0; + + for (int i = 0; i < n; i++) { + Set types = new HashSet<>(); + int j = i; + + while (j < n && (types.size() < 2 || types.contains(fruits[j]))) { + types.add(fruits[j]); + j++; + } + res = Math.max(res, j - i); + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int totalFruit(vector& fruits) { + int n = fruits.size(), res = 0; + + for (int i = 0; i < n; i++) { + unordered_set types; + int j = i; + + while (j < n && (types.size() < 2 || types.count(fruits[j]))) { + types.insert(fruits[j]); + j++; + } + res = max(res, j - i); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} fruits + * @return {number} + */ + totalFruit(fruits) { + let n = fruits.length, res = 0; + + for (let i = 0; i < n; i++) { + let types = new Set(); + let j = i; + + while (j < n && (types.size < 2 || types.has(fruits[j]))) { + types.add(fruits[j]); + j++; + } + res = Math.max(res, j - i); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(1)$ + +--- + +## 2. Sliding Window - I + +::tabs-start + +```python +class Solution: + def totalFruit(self, fruits: List[int]) -> int: + count = defaultdict(int) + l, total, res = 0, 0, 0 + + for r in range(len(fruits)): + count[fruits[r]] += 1 + total += 1 + + while len(count) > 2: + f = fruits[l] + count[f] -= 1 + total -= 1 + l += 1 + if not count[f]: + count.pop(f) + + res = max(res, total) + + return res +``` + +```java +public class Solution { + public int totalFruit(int[] fruits) { + HashMap count = new HashMap<>(); + int l = 0, total = 0, res = 0; + + for (int r = 0; r < fruits.length; r++) { + count.put(fruits[r], count.getOrDefault(fruits[r], 0) + 1); + total++; + + while (count.size() > 2) { + int f = fruits[l]; + count.put(f, count.get(f) - 1); + total--; + if (count.get(f) == 0) { + count.remove(f); + } + l++; + } + res = Math.max(res, total); + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int totalFruit(vector& fruits) { + unordered_map count; + int l = 0, total = 0, res = 0; + + for (int r = 0; r < fruits.size(); r++) { + count[fruits[r]]++; + total++; + + while (count.size() > 2) { + int f = fruits[l]; + count[f]--; + total--; + if (count[f] == 0) { + count.erase(f); + } + l++; + } + res = max(res, total); + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} fruits + * @return {number} + */ + totalFruit(fruits) { + let count = new Map(); + let l = 0, total = 0, res = 0; + + for (let r = 0; r < fruits.length; r++) { + count.set(fruits[r], (count.get(fruits[r]) || 0) + 1); + total++; + + while (count.size > 2) { + let f = fruits[l]; + count.set(f, count.get(f) - 1); + total--; + if (count.get(f) === 0) { + count.delete(f); + } + l++; + } + res = Math.max(res, total); + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. + +--- + +## 3. Sliding Window - II + +::tabs-start + +```python +class Solution: + def totalFruit(self, fruits: List[int]) -> int: + count = defaultdict(int) + l = 0 + + for r in range(len(fruits)): + count[fruits[r]] += 1 + + if len(count) > 2: + count[fruits[l]] -= 1 + if count[fruits[l]] == 0: + count.pop(fruits[l]) + l += 1 + + return len(fruits) - l +``` + +```java +public class Solution { + public int totalFruit(int[] fruits) { + HashMap count = new HashMap<>(); + int l = 0; + + for (int r = 0; r < fruits.length; r++) { + count.put(fruits[r], count.getOrDefault(fruits[r], 0) + 1); + + if (count.size() > 2) { + count.put(fruits[l], count.get(fruits[l]) - 1); + if (count.get(fruits[l]) == 0) { + count.remove(fruits[l]); + } + l++; + } + } + + return fruits.length - l; + } +} +``` + +```cpp +class Solution { +public: + int totalFruit(vector& fruits) { + unordered_map count; + int l = 0; + + for (int r = 0; r < fruits.size(); r++) { + count[fruits[r]]++; + + if (count.size() > 2) { + count[fruits[l]]--; + if (count[fruits[l]] == 0) { + count.erase(fruits[l]); + } + l++; + } + } + + return fruits.size() - l; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} fruits + * @return {number} + */ + totalFruit(fruits) { + let count = new Map(); + let l = 0; + + for (let r = 0; r < fruits.length; r++) { + count.set(fruits[r], (count.get(fruits[r]) || 0) + 1); + + if (count.size > 2) { + count.set(fruits[l], count.get(fruits[l]) - 1); + if (count.get(fruits[l]) === 0) { + count.delete(fruits[l]); + } + l++; + } + } + + return fruits.length - l; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. + +--- + +## 4. Sliding Window - III + +::tabs-start + +```python +class Solution: + def totalFruit(self, fruits: list[int]) -> int: + l = 0 + fruit1_lastIdx = 0 + fruit2_lastIdx = -1 + fruit1 = fruits[0] + fruit2 = -1 + total = res = 1 + + for r in range(len(fruits)): + f = fruits[r] + if f == fruit1: + total += 1 + fruit1_lastIdx = r + elif f == fruit2 or fruit2 == -1: + total += 1 + fruit2_lastIdx = r + fruit2 = f + else: + if fruit2_lastIdx == min(fruit1_lastIdx, fruit2_lastIdx): + fruit1_lastIdx, fruit2_lastIdx = fruit2_lastIdx, fruit1_lastIdx + fruit1, fruit2 = fruit2, fruit1 + + total -= (fruit1_lastIdx - l + 1) + l = fruit1_lastIdx + 1 + fruit1 = f + fruit1_lastIdx = r + res = max(res, r - l + 1) + + return res +``` + +```java +public class Solution { + public int totalFruit(int[] fruits) { + int l = 0, fruit1_lastIdx = 0, fruit2_lastIdx = -1; + int fruit1 = fruits[0], fruit2 = -1, total = 1, res = 1; + + for (int r = 0; r < fruits.length; r++) { + int f = fruits[r]; + if (f == fruit1) { + total++; + fruit1_lastIdx = r; + } else if (f == fruit2 || fruit2 == -1) { + total++; + fruit2_lastIdx = r; + fruit2 = f; + } else { + if (fruit2_lastIdx == Math.min(fruit1_lastIdx, fruit2_lastIdx)) { + int tempIdx = fruit1_lastIdx; + fruit1_lastIdx = fruit2_lastIdx; + fruit2_lastIdx = tempIdx; + int tempFruit = fruit1; + fruit1 = fruit2; + fruit2 = tempFruit; + } + total -= (fruit1_lastIdx - l + 1); + l = fruit1_lastIdx + 1; + fruit1 = f; + fruit1_lastIdx = r; + } + res = Math.max(res, r - l + 1); + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int totalFruit(vector& fruits) { + int l = 0, fruit1_lastIdx = 0, fruit2_lastIdx = -1; + int fruit1 = fruits[0], fruit2 = -1, total = 1, res = 1; + + for (int r = 0; r < fruits.size(); r++) { + int f = fruits[r]; + if (f == fruit1) { + total++; + fruit1_lastIdx = r; + } else if (f == fruit2 || fruit2 == -1) { + total++; + fruit2_lastIdx = r; + fruit2 = f; + } else { + if (fruit2_lastIdx == min(fruit1_lastIdx, fruit2_lastIdx)) { + swap(fruit1_lastIdx, fruit2_lastIdx); + swap(fruit1, fruit2); + } + total -= (fruit1_lastIdx - l + 1); + l = fruit1_lastIdx + 1; + fruit1 = f; + fruit1_lastIdx = r; + } + res = max(res, r - l + 1); + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} fruits + * @return {number} + */ + totalFruit(fruits) { + let l = 0, fruit1_lastIdx = 0, fruit2_lastIdx = -1; + let fruit1 = fruits[0], fruit2 = -1, total = 1, res = 1; + + for (let r = 0; r < fruits.length; r++) { + let f = fruits[r]; + if (f === fruit1) { + total++; + fruit1_lastIdx = r; + } else if (f === fruit2 || fruit2 === -1) { + total++; + fruit2_lastIdx = r; + fruit2 = f; + } else { + if (fruit2_lastIdx === Math.min(fruit1_lastIdx, fruit2_lastIdx)) { + [fruit1_lastIdx, fruit2_lastIdx] = [fruit2_lastIdx, fruit1_lastIdx]; + [fruit1, fruit2] = [fruit2, fruit1]; + } + total -= (fruit1_lastIdx - l + 1); + l = fruit1_lastIdx + 1; + fruit1 = f; + fruit1_lastIdx = r; + } + res = Math.max(res, r - l + 1); + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. \ No newline at end of file diff --git a/articles/guess-number-higher-or-lower.md b/articles/guess-number-higher-or-lower.md new file mode 100644 index 000000000..b6053e2e8 --- /dev/null +++ b/articles/guess-number-higher-or-lower.md @@ -0,0 +1,352 @@ +## 1. Linear Search + +::tabs-start + +```python +# The guess API is already defined for you. +# @param num, your guess +# @return -1 if num is higher than the picked number +# 1 if num is lower than the picked number +# otherwise return 0 +# def guess(num: int) -> int: + +class Solution: + def guessNumber(self, n: int) -> int: + for num in range(1, n + 1): + if guess(num) == 0: + return num +``` + +```java +/** + * Forward declaration of guess API. + * @param num your guess + * @return -1 if num is higher than the picked number + * 1 if num is lower than the picked number + * otherwise return 0 + * int guess(int num); + */ + +public class Solution extends GuessGame { + public int guessNumber(int n) { + for (int num = 1; num <= n; num++) { + if (guess(num) == 0) return num; + } + return n; + } +} +``` + +```cpp +/** + * Forward declaration of guess API. + * @param num your guess + * @return -1 if num is higher than the picked number + * 1 if num is lower than the picked number + * otherwise return 0 + * int guess(int num); + */ + +class Solution { +public: + int guessNumber(int n) { + for (int num = 1; num <= n; num++) { + if (guess(num) == 0) return num; + } + return n; + } +}; +``` + +```javascript +/** + * Forward declaration of guess API. + * @param {number} num your guess + * @return -1 if num is higher than the picked number + * 1 if num is lower than the picked number + * otherwise return 0 + * var guess = function(num) {} + */ + +class Solution { + /** + * @param {number} n + * @return {number} + */ + guessNumber(n) { + for (let num = 1; num <= n; num++) { + if (guess(num) === 0) return num; + } + return n; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ + +--- + +## 2. Binary Search + +::tabs-start + +```python +# The guess API is already defined for you. +# @param num, your guess +# @return -1 if num is higher than the picked number +# 1 if num is lower than the picked number +# otherwise return 0 +# def guess(num: int) -> int: + +class Solution: + def guessNumber(self, n: int) -> int: + l, r = 1, n + while True: + m = (l + r) // 2 + res = guess(m) + if res > 0: + l = m + 1 + elif res < 0: + r = m - 1 + else: + return m +``` + +```java +/** + * Forward declaration of guess API. + * @param num your guess + * @return -1 if num is higher than the picked number + * 1 if num is lower than the picked number + * otherwise return 0 + * int guess(int num); + */ + +public class Solution extends GuessGame { + public int guessNumber(int n) { + int l = 1, r = n; + while (true) { + int m = l + (r - l) / 2; + int res = guess(m); + if (res > 0) { + l = m + 1; + } else if (res < 0) { + r = m - 1; + } else { + return m; + } + } + } +} +``` + +```cpp +/** + * Forward declaration of guess API. + * @param num your guess + * @return -1 if num is higher than the picked number + * 1 if num is lower than the picked number + * otherwise return 0 + * int guess(int num); + */ + +class Solution { +public: + int guessNumber(int n) { + int l = 1, r = n; + while (true) { + int m = l + (r - l) / 2; + int res = guess(m); + if (res > 0) { + l = m + 1; + } else if (res < 0) { + r = m - 1; + } else { + return m; + } + } + } +}; +``` + +```javascript +/** + * Forward declaration of guess API. + * @param {number} num your guess + * @return -1 if num is higher than the picked number + * 1 if num is lower than the picked number + * otherwise return 0 + * var guess = function(num) {} + */ + +class Solution { + /** + * @param {number} n + * @return {number} + */ + guessNumber(n) { + let l = 1, r = n; + while (true) { + let m = Math.floor((l + r) / 2); + let res = guess(m); + if (res > 0) { + l = m + 1; + } else if (res < 0) { + r = m - 1; + } else { + return m; + } + } + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(\log n)$ +* Space complexity: $O(1)$ + +--- + +## 3. Ternary Search + +::tabs-start + +```python +# The guess API is already defined for you. +# @param num, your guess +# @return -1 if num is higher than the picked number +# 1 if num is lower than the picked number +# otherwise return 0 +# def guess(num: int) -> int: + +class Solution: + def guessNumber(self, n: int) -> int: + l, r = 1, n + while True: + m1 = l + (r - l) // 3 + m2 = r - (r - l) // 3 + if guess(m1) == 0: + return m1 + if guess(m2) == 0: + return m2 + if guess(m1) + guess(m2) == 0: + l = m1 + 1 + r = m2 - 1 + elif guess(m1) == -1: + r = m1 - 1 + else: + l = m2 + 1 +``` + +```java +/** + * Forward declaration of guess API. + * @param num your guess + * @return -1 if num is higher than the picked number + * 1 if num is lower than the picked number + * otherwise return 0 + * int guess(int num); + */ + +public class Solution extends GuessGame { + public int guessNumber(int n) { + int l = 1, r = n; + while (true) { + int m1 = l + (r - l) / 3; + int m2 = r - (r - l) / 3; + if (guess(m1) == 0) return m1; + if (guess(m2) == 0) return m2; + if (guess(m1) + guess(m2) == 0) { + l = m1 + 1; + r = m2 - 1; + } else if (guess(m1) == -1) { + r = m1 - 1; + } else { + l = m2 + 1; + } + } + } +} +``` + +```cpp +/** + * Forward declaration of guess API. + * @param num your guess + * @return -1 if num is higher than the picked number + * 1 if num is lower than the picked number + * otherwise return 0 + * int guess(int num); + */ + +class Solution { +public: + int guessNumber(int n) { + int l = 1, r = n; + while (true) { + int m1 = l + (r - l) / 3; + int m2 = r - (r - l) / 3; + if (guess(m1) == 0) return m1; + if (guess(m2) == 0) return m2; + if (guess(m1) + guess(m2) == 0) { + l = m1 + 1; + r = m2 - 1; + } else if (guess(m1) == -1) { + r = m1 - 1; + } else { + l = m2 + 1; + } + } + } +}; +``` + +```javascript +/** + * Forward declaration of guess API. + * @param {number} num your guess + * @return -1 if num is higher than the picked number + * 1 if num is lower than the picked number + * otherwise return 0 + * var guess = function(num) {} + */ + +class Solution { + /** + * @param {number} n + * @return {number} + */ + guessNumber(n) { + let l = 1, r = n; + while (true) { + let m1 = l + Math.floor((r - l) / 3); + let m2 = r - Math.floor((r - l) / 3); + if (guess(m1) === 0) return m1; + if (guess(m2) === 0) return m2; + if (guess(m1) + guess(m2) === 0) { + l = m1 + 1; + r = m2 - 1; + } else if (guess(m1) === -1) { + r = m1 - 1; + } else { + l = m2 + 1; + } + } + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(\log_3 n)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/maximum-number-of-vowels-in-a-substring-of-given-length.md b/articles/maximum-number-of-vowels-in-a-substring-of-given-length.md new file mode 100644 index 000000000..371aff2b3 --- /dev/null +++ b/articles/maximum-number-of-vowels-in-a-substring-of-given-length.md @@ -0,0 +1,385 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def maxVowels(self, s: str, k: int) -> int: + vowel = {'a', 'e', 'i', 'o', 'u'} + res = 0 + + for i in range(len(s) - k + 1): + cnt = 0 + for j in range(i, i + k): + cnt += 1 if s[j] in vowel else 0 + res = max(res, cnt) + + return res +``` + +```java +public class Solution { + public int maxVowels(String s, int k) { + Set vowels = Set.of('a', 'e', 'i', 'o', 'u'); + int res = 0; + + for (int i = 0; i <= s.length() - k; i++) { + int cnt = 0; + for (int j = i; j < i + k; j++) { + if (vowels.contains(s.charAt(j))) { + cnt++; + } + } + res = Math.max(res, cnt); + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int maxVowels(string s, int k) { + unordered_set vowels = {'a', 'e', 'i', 'o', 'u'}; + int res = 0; + + for (int i = 0; i <= s.size() - k; i++) { + int cnt = 0; + for (int j = i; j < i + k; j++) { + if (vowels.count(s[j])) { + cnt++; + } + } + res = max(res, cnt); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @param {number} k + * @return {number} + */ + maxVowels(s, k) { + const vowels = new Set(['a', 'e', 'i', 'o', 'u']); + let res = 0; + + for (let i = 0; i <= s.length - k; i++) { + let cnt = 0; + for (let j = i; j < i + k; j++) { + if (vowels.has(s[j])) { + cnt++; + } + } + res = Math.max(res, cnt); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(1)$ extra space. + +--- + +## 2. Prefix Count + +::tabs-start + +```python +class Solution: + def maxVowels(self, s: str, k: int) -> int: + vowel = {'a', 'e', 'i', 'o', 'u'} + prefix = [0] * (len(s) + 1) + for i in range(len(s)): + prefix[i + 1] = prefix[i] + (1 if s[i] in vowel else 0) + + res = 0 + for i in range(k, len(s) + 1): + res = max(res, prefix[i] - prefix[i - k]) + + return res +``` + +```java +public class Solution { + public int maxVowels(String s, int k) { + Set vowels = Set.of('a', 'e', 'i', 'o', 'u'); + int[] prefix = new int[s.length() + 1]; + + for (int i = 0; i < s.length(); i++) { + prefix[i + 1] = prefix[i] + (vowels.contains(s.charAt(i)) ? 1 : 0); + } + + int res = 0; + for (int i = k; i <= s.length(); i++) { + res = Math.max(res, prefix[i] - prefix[i - k]); + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int maxVowels(string s, int k) { + unordered_set vowels = {'a', 'e', 'i', 'o', 'u'}; + vector prefix(s.size() + 1, 0); + + for (int i = 0; i < s.size(); i++) { + prefix[i + 1] = prefix[i] + (vowels.count(s[i]) ? 1 : 0); + } + + int res = 0; + for (int i = k; i <= s.size(); i++) { + res = max(res, prefix[i] - prefix[i - k]); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @param {number} k + * @return {number} + */ + maxVowels(s, k) { + const vowels = new Set(['a', 'e', 'i', 'o', 'u']); + const prefix = new Array(s.length + 1).fill(0); + + for (let i = 0; i < s.length; i++) { + prefix[i + 1] = prefix[i] + (vowels.has(s[i]) ? 1 : 0); + } + + let res = 0; + for (let i = k; i <= s.length; i++) { + res = Math.max(res, prefix[i] - prefix[i - k]); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Sliding Window + +::tabs-start + +```python +class Solution: + def maxVowels(self, s: str, k: int) -> int: + vowel = {'a', 'e', 'i', 'o', 'u'} + + l = cnt = res = 0 + for r in range(len(s)): + cnt += 1 if s[r] in vowel else 0 + if r - l + 1 > k: + cnt -= 1 if s[l] in vowel else 0 + l += 1 + res = max(res, cnt) + return res +``` + +```java +public class Solution { + public int maxVowels(String s, int k) { + Set vowels = Set.of('a', 'e', 'i', 'o', 'u'); + + int l = 0, cnt = 0, res = 0; + for (int r = 0; r < s.length(); r++) { + cnt += (vowels.contains(s.charAt(r)) ? 1 : 0); + if (r - l + 1 > k) { + cnt -= (vowels.contains(s.charAt(l)) ? 1 : 0); + l++; + } + res = Math.max(res, cnt); + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int maxVowels(string s, int k) { + unordered_set vowels = {'a', 'e', 'i', 'o', 'u'}; + + int l = 0, cnt = 0, res = 0; + for (int r = 0; r < s.length(); r++) { + cnt += (vowels.count(s[r]) ? 1 : 0); + if (r - l + 1 > k) { + cnt -= (vowels.count(s[l++]) ? 1 : 0); + } + res = max(res, cnt); + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @param {number} k + * @return {number} + */ + maxVowels(s, k) { + const vowels = new Set(['a', 'e', 'i', 'o', 'u']); + + let l = 0, cnt = 0, res = 0; + for (let r = 0; r < s.length; r++) { + cnt += (vowels.has(s[r]) ? 1 : 0); + if (r - l + 1 > k) { + cnt -= (vowels.has(s[l++]) ? 1 : 0); + } + res = Math.max(res, cnt); + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. + +--- + +## 4. Sliding Window (Bit Mask) + +::tabs-start + +```python +class Solution: + def maxVowels(self, s: str, k: int) -> int: + def getId(c): + return ord(c) - ord('a') + + mask = (1 << getId('a')) | (1 << getId('e')) | \ + (1 << getId('i')) | (1 << getId('o')) | \ + (1 << getId('u')) + + l = cnt = res = 0 + for r in range(len(s)): + cnt += ((mask >> getId(s[r])) & 1) + if r - l + 1 > k: + cnt -= ((mask >> getId(s[l])) & 1) + l += 1 + res = max(res, cnt) + return res +``` + +```java +public class Solution { + public int maxVowels(String s, int k) { + int mask = (1 << ('a' - 'a')) | (1 << ('e' - 'a')) | + (1 << ('i' - 'a')) | (1 << ('o' - 'a')) | + (1 << ('u' - 'a')); + + int l = 0, cnt = 0, res = 0; + for (int r = 0; r < s.length(); r++) { + cnt += (mask >> (s.charAt(r) - 'a')) & 1; + if (r - l + 1 > k) { + cnt -= (mask >> (s.charAt(l) - 'a')) & 1; + l++; + } + res = Math.max(res, cnt); + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int maxVowels(string s, int k) { + int mask = (1 << ('a' - 'a')) | (1 << ('e' - 'a')) | + (1 << ('i' - 'a')) | (1 << ('o' - 'a')) | + (1 << ('u' - 'a')); + + int l = 0, cnt = 0, res = 0; + for (int r = 0; r < s.size(); r++) { + cnt += (mask >> (s[r] - 'a')) & 1; + if (r - l + 1 > k) { + cnt -= (mask >> (s[l] - 'a')) & 1; + l++; + } + res = max(res, cnt); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @param {number} k + * @return {number} + */ + maxVowels(s, k) { + const getId = (c) => { + return c.charCodeAt(0) - 'a'.charCodeAt(0); + }; + const mask = (1 << getId('a')) | (1 << getId('e')) | + (1 << getId('i')) | (1 << getId('o')) | + (1 << getId('u')); + + let l = 0, cnt = 0, res = 0; + for (let r = 0; r < s.length; r++) { + cnt += (mask >> getId(s.charAt(r))) & 1; + if (r - l + 1 > k) { + cnt -= (mask >> getId(s.charAt(l))) & 1; + l++; + } + res = Math.max(res, cnt); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. \ No newline at end of file diff --git a/articles/merge-sorted-array.md b/articles/merge-sorted-array.md new file mode 100644 index 000000000..aab35dc73 --- /dev/null +++ b/articles/merge-sorted-array.md @@ -0,0 +1,374 @@ +## 1. Sorting + +::tabs-start + +```python +class Solution: + def merge(self, nums1: List[int], m: int, nums2: List[int], n: int) -> None: + """ + Do not return anything, modify nums1 in-place instead. + """ + nums1[m:] = nums2[:n] + nums1.sort() +``` + +```java +public class Solution { + public void merge(int[] nums1, int m, int[] nums2, int n) { + for (int i = 0; i < n; i++) { + nums1[i + m] = nums2[i]; + } + Arrays.sort(nums1); + } +} +``` + +```cpp +class Solution { +public: + void merge(vector& nums1, int m, vector& nums2, int n) { + for (int i = 0; i < n; i++) { + nums1[i + m] = nums2[i]; + } + sort(nums1.begin(), nums1.end()); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums1 + * @param {number} m + * @param {number[]} nums2 + * @param {number} n + * @return {void} Do not return anything, modify nums1 in-place instead. + */ + merge(nums1, m, nums2, n) { + for (let i = 0; i < n; i++) { + nums1[i + m] = nums2[i]; + } + nums1.sort((a, b) => a - b); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O((m + n) \log (m + n))$ +* Space complexity: $O(1)$ or $O(m + n)$ depending on the sorting algorithm. + +> Where $m$ and $n$ represent the number of elements in the arrays $nums1$ and $nums2$, respectively. + +--- + +## 2. Three Pointers With Extra Space + +::tabs-start + +```python +class Solution: + def merge(self, nums1: List[int], m: int, nums2: List[int], n: int) -> None: + """ + Do not return anything, modify nums1 in-place instead. + """ + nums1_copy = nums1[:m] + idx = 0 + i = j = 0 + while idx < m + n: + if j >= n or (i < m and nums1_copy[i] <= nums2[j]): + nums1[idx] = nums1_copy[i] + i += 1 + else: + nums1[idx] = nums2[j] + j += 1 + idx += 1 +``` + +```java +public class Solution { + public void merge(int[] nums1, int m, int[] nums2, int n) { + int[] nums1Copy = Arrays.copyOf(nums1, m); + int idx = 0, i = 0, j = 0; + + while (idx < m + n) { + if (j >= n || (i < m && nums1Copy[i] <= nums2[j])) { + nums1[idx++] = nums1Copy[i++]; + } else { + nums1[idx++] = nums2[j++]; + } + } + } +} +``` + +```cpp +class Solution { +public: + void merge(vector& nums1, int m, vector& nums2, int n) { + vector nums1Copy(nums1.begin(), nums1.begin() + m); + int idx = 0, i = 0, j = 0; + + while (idx < m + n) { + if (j >= n || (i < m && nums1Copy[i] <= nums2[j])) { + nums1[idx++] = nums1Copy[i++]; + } else { + nums1[idx++] = nums2[j++]; + } + } + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums1 + * @param {number} m + * @param {number[]} nums2 + * @param {number} n + * @return {void} Do not return anything, modify nums1 in-place instead. + */ + merge(nums1, m, nums2, n) { + const nums1Copy = nums1.slice(0, m); + let idx = 0, i = 0, j = 0; + + while (idx < m + n) { + if (j >= n || (i < m && nums1Copy[i] <= nums2[j])) { + nums1[idx++] = nums1Copy[i++]; + } else { + nums1[idx++] = nums2[j++]; + } + } + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m + n)$ +* Space complexity: $O(m)$ + +> Where $m$ and $n$ represent the number of elements in the arrays $nums1$ and $nums2$, respectively. + +--- + +## 3. Three Pointers Without Extra Space - I + +::tabs-start + +```python +class Solution: + def merge(self, nums1: list[int], m: int, nums2: list[int], n: int) -> None: + """ + Do not return anything, modify nums1 in-place instead. + """ + last = m + n - 1 + + # Merge in reverse order + while m > 0 and n > 0: + if nums1[m - 1] > nums2[n - 1]: + nums1[last] = nums1[m - 1] + m -= 1 + else: + nums1[last] = nums2[n - 1] + n -= 1 + last -= 1 + + # Fill nums1 with leftover nums2 elements + while n > 0: + nums1[last] = nums2[n - 1] + n -= 1 + last -= 1 +``` + +```java +public class Solution { + public void merge(int[] nums1, int m, int[] nums2, int n) { + int last = m + n - 1; + + // Merge in reverse order + while (m > 0 && n > 0) { + if (nums1[m - 1] > nums2[n - 1]) { + nums1[last] = nums1[m - 1]; + m--; + } else { + nums1[last] = nums2[n - 1]; + n--; + } + last--; + } + + // Fill nums1 with leftover nums2 elements + while (n > 0) { + nums1[last] = nums2[n - 1]; + n--; + last--; + } + } +} +``` + +```cpp +class Solution { +public: + void merge(vector& nums1, int m, vector& nums2, int n) { + int last = m + n - 1; + + // Merge in reverse order + while (m > 0 && n > 0) { + if (nums1[m - 1] > nums2[n - 1]) { + nums1[last] = nums1[m - 1]; + m--; + } else { + nums1[last] = nums2[n - 1]; + n--; + } + last--; + } + + // Fill nums1 with leftover nums2 elements + while (n > 0) { + nums1[last] = nums2[n - 1]; + n--; + last--; + } + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums1 + * @param {number} m + * @param {number[]} nums2 + * @param {number} n + * @return {void} Do not return anything, modify nums1 in-place instead. + */ + merge(nums1, m, nums2, n) { + let last = m + n - 1; + + // Merge in reverse order + while (m > 0 && n > 0) { + if (nums1[m - 1] > nums2[n - 1]) { + nums1[last--] = nums1[m-- - 1]; + } else { + nums1[last--] = nums2[n-- - 1]; + } + } + + // Fill nums1 with leftover nums2 elements + while (n > 0) { + nums1[last--] = nums2[n-- - 1]; + } + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m + n)$ +* Space complexity: $O(1)$ extra space. + +> Where $m$ and $n$ represent the number of elements in the arrays $nums1$ and $nums2$, respectively. + +--- + +## 4. Three Pointers Without Extra Space - II + +::tabs-start + +```python +class Solution: + def merge(self, nums1: list[int], m: int, nums2: list[int], n: int) -> None: + """ + Do not return anything, modify nums1 in-place instead. + """ + last = m + n - 1 + i, j = m - 1, n - 1 + + while j >= 0: + if i >= 0 and nums1[i] > nums2[j]: + nums1[last] = nums1[i] + i -= 1 + else: + nums1[last] = nums2[j] + j -= 1 + + last -= 1 +``` + +```java +public class Solution { + public void merge(int[] nums1, int m, int[] nums2, int n) { + int last = m + n - 1; + int i = m - 1, j = n - 1; + + while (j >= 0) { + if (i >= 0 && nums1[i] > nums2[j]) { + nums1[last--] = nums1[i--]; + } else { + nums1[last--] = nums2[j--]; + } + } + } +} +``` + +```cpp +class Solution { +public: + void merge(vector& nums1, int m, vector& nums2, int n) { + int last = m + n - 1; + int i = m - 1, j = n - 1; + + while (j >= 0) { + if (i >= 0 && nums1[i] > nums2[j]) { + nums1[last--] = nums1[i--]; + } else { + nums1[last--] = nums2[j--]; + } + } + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums1 + * @param {number} m + * @param {number[]} nums2 + * @param {number} n + * @return {void} Do not return anything, modify nums1 in-place instead. + */ + merge(nums1, m, nums2, n) { + let last = m + n - 1; + let i = m - 1, j = n - 1; + + while (j >= 0) { + if (i >= 0 && nums1[i] > nums2[j]) { + nums1[last--] = nums1[i--]; + } else { + nums1[last--] = nums2[j--]; + } + } + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m + n)$ +* Space complexity: $O(1)$ extra space. + +> Where $m$ and $n$ represent the number of elements in the arrays $nums1$ and $nums2$, respectively. \ No newline at end of file diff --git a/articles/merge-strings-alternately.md b/articles/merge-strings-alternately.md new file mode 100644 index 000000000..04d95926d --- /dev/null +++ b/articles/merge-strings-alternately.md @@ -0,0 +1,250 @@ +## 1. Two Pointers - I + +::tabs-start + +```python +class Solution: + def mergeAlternately(self, word1: str, word2: str) -> str: + i, j = 0, 0 + res = [] + while i < len(word1) and j < len(word2): + res.append(word1[i]) + res.append(word2[j]) + i += 1 + j += 1 + res.append(word1[i:]) + res.append(word2[j:]) + return "".join(res) +``` + +```java +public class Solution { + public String mergeAlternately(String word1, String word2) { + StringBuilder res = new StringBuilder(); + int i = 0, j = 0; + while (i < word1.length() && j < word2.length()) { + res.append(word1.charAt(i++)); + res.append(word2.charAt(j++)); + } + res.append(word1.substring(i)); + res.append(word2.substring(j)); + return res.toString(); + } +} +``` + +```cpp +class Solution { +public: + string mergeAlternately(string word1, string word2) { + string res; + int i = 0, j = 0; + while (i < word1.size() && j < word2.size()) { + res += word1[i++]; + res += word2[j++]; + } + res += word1.substr(i); + res += word2.substr(j); + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} word1 + * @param {string} word2 + * @return {string} + */ + mergeAlternately(word1, word2) { + let res = []; + let i = 0, j = 0; + while (i < word1.length && j < word2.length) { + res.push(word1[i++], word2[j++]); + } + res.push(word1.slice(i)); + res.push(word2.slice(j)); + return res.join(""); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n + m)$ +* Space complexity: $O(n + m)$ for the output string. + +> Where $n$ and $m$ are the lengths of the strings $word1$ and $word2$ respectively. + +--- + +## 2. Two Pointers - II + +::tabs-start + +```python +class Solution: + def mergeAlternately(self, word1: str, word2: str) -> str: + n, m = len(word1), len(word2) + res = [] + i = j = 0 + while i < n or j < m: + if i < n: + res.append(word1[i]) + if j < m: + res.append(word2[j]) + i += 1 + j += 1 + return "".join(res) +``` + +```java +public class Solution { + public String mergeAlternately(String word1, String word2) { + int n = word1.length(), m = word2.length(); + StringBuilder res = new StringBuilder(); + int i = 0, j = 0; + while (i < n || j < m) { + if (i < n) res.append(word1.charAt(i++)); + if (j < m) res.append(word2.charAt(j++)); + } + return res.toString(); + } +} +``` + +```cpp +class Solution { +public: + string mergeAlternately(string word1, string word2) { + int n = word1.size(), m = word2.size(); + string res; + int i = 0, j = 0; + while (i < n || j < m) { + if (i < n) res += word1[i++]; + if (j < m) res += word2[j++]; + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} word1 + * @param {string} word2 + * @return {string} + */ + mergeAlternately(word1, word2) { + const n = word1.length, m = word2.length; + const res = []; + let i = 0, j = 0; + while (i < n || j < m) { + if (i < n) res.push(word1[i++]); + if (j < m) res.push(word2[j++]); + } + return res.join(""); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n + m)$ +* Space complexity: $O(n + m)$ for the output string. + +> Where $n$ and $m$ are the lengths of the strings $word1$ and $word2$ respectively. + +--- + +## 3. One Pointer + +::tabs-start + +```python +class Solution: + def mergeAlternately(self, word1: str, word2: str) -> str: + n, m = len(word1), len(word2) + res = [] + for i in range(max(m, n)): + if i < n: + res.append(word1[i]) + if i < m: + res.append(word2[i]) + return "".join(res) +``` + +```java +public class Solution { + public String mergeAlternately(String word1, String word2) { + int n = word1.length(), m = word2.length(); + StringBuilder res = new StringBuilder(); + for (int i = 0; i < n || i < m; i++) { + if (i < n) { + res.append(word1.charAt(i)); + } + if (i < m) { + res.append(word2.charAt(i)); + } + } + return res.toString(); + } +} +``` + +```cpp +class Solution { +public: + string mergeAlternately(string word1, string word2) { + int n = word1.size(), m = word2.size(); + string res; + for (int i = 0; i < n || i < m; i++) { + if (i < n) { + res += word1[i]; + } + if (i < m) { + res += word2[i]; + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} word1 + * @param {string} word2 + * @return {string} + */ + mergeAlternately(word1, word2) { + const n = word1.length, m = word2.length; + const res = []; + for (let i = 0; i < m || i < n; i++) { + if (i < n) { + res.push(word1.charAt(i)); + } + if (i < m) { + res.push(word2.charAt(i)); + } + } + return res.join(""); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n + m)$ +* Space complexity: $O(n + m)$ for the output string. + +> Where $n$ and $m$ are the lengths of the strings $word1$ and $word2$ respectively. \ No newline at end of file diff --git a/articles/minimum-difference-between-highest-and-lowest-of-k-scores.md b/articles/minimum-difference-between-highest-and-lowest-of-k-scores.md new file mode 100644 index 000000000..de7c35615 --- /dev/null +++ b/articles/minimum-difference-between-highest-and-lowest-of-k-scores.md @@ -0,0 +1,74 @@ +## 1. Sorting + Sliding Window + +::tabs-start + +```python +class Solution: + def minimumDifference(self, nums: List[int], k: int) -> int: + nums.sort() + l, r = 0, k - 1 + res = float("inf") + while r < len(nums): + res = min(res, nums[r] - nums[l]) + l += 1 + r += 1 + return res +``` + +```java +public class Solution { + public int minimumDifference(int[] nums, int k) { + Arrays.sort(nums); + int l = 0, r = k - 1, res = Integer.MAX_VALUE; + while (r < nums.length) { + res = Math.min(res, nums[r] - nums[l]); + l++; + r++; + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int minimumDifference(vector& nums, int k) { + sort(nums.begin(), nums.end()); + int l = 0, r = k - 1, res = INT_MAX; + while (r < nums.size()) { + res = min(res, nums[r] - nums[l]); + l++; + r++; + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {number} + */ + minimumDifference(nums, k) { + nums.sort((a, b) => a - b); + let l = 0, r = k - 1, res = Infinity; + while (r < nums.length) { + res = Math.min(res, nums[r] - nums[l]); + l++; + r++; + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(1)$ or $O(n)$ depending on the sorting algorithm. \ No newline at end of file diff --git a/articles/minimum-number-of-flips-to-make-the-binary-string-alternating.md b/articles/minimum-number-of-flips-to-make-the-binary-string-alternating.md new file mode 100644 index 000000000..b0afa6412 --- /dev/null +++ b/articles/minimum-number-of-flips-to-make-the-binary-string-alternating.md @@ -0,0 +1,701 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def minFlips(self, s: str) -> int: + res = n = len(s) + alt1, alt2 = [], [] + for i in range(n): + alt1.append("0" if i % 2 == 0 else "1") + alt2.append("1" if i % 2 == 0 else "0") + + def diff(A, B): + cnt = 0 + for i in range(n): + cnt += 1 if (A[i] != B[i]) else 0 + return cnt + + for i in range(n): + newS = s[i:] + s[:i] + res = min(res, min(diff(alt1, newS), diff(alt2, newS))) + return res +``` + +```java +public class Solution { + public int minFlips(String s) { + int n = s.length(), res = n; + StringBuilder alt1 = new StringBuilder(); + StringBuilder alt2 = new StringBuilder(); + + for (int i = 0; i < n; i++) { + alt1.append(i % 2 == 0 ? '0' : '1'); + alt2.append(i % 2 == 0 ? '1' : '0'); + } + + for (int i = 0; i < n; i++) { + String newS = s.substring(i) + s.substring(0, i); + res = Math.min(res, Math.min(diff(alt1, newS), diff(alt2, newS))); + } + + return res; + } + + private int diff(StringBuilder a, String b) { + int cnt = 0; + for (int i = 0; i < a.length(); i++) { + if (a.charAt(i) != b.charAt(i)) cnt++; + } + return cnt; + } +} +``` + +```cpp +class Solution { +public: + int minFlips(string s) { + int n = s.size(), res = n; + string alt1, alt2; + + for (int i = 0; i < n; i++) { + alt1 += (i % 2 == 0) ? '0' : '1'; + alt2 += (i % 2 == 0) ? '1' : '0'; + } + + for (int i = 0; i < n; i++) { + string newS = s.substr(i) + s.substr(0, i); + res = min(res, min(diff(alt1, newS), diff(alt2, newS))); + } + + return res; + } + +private: + int diff(const string &a, const string &b) { + int cnt = 0; + for (int i = 0; i < a.size(); i++) { + if (a[i] != b[i]) cnt++; + } + return cnt; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {number} + */ + minFlips(s) { + const n = s.length; + let res = n; + let alt1 = "", alt2 = ""; + + for (let i = 0; i < n; i++) { + alt1 += i % 2 === 0 ? '0' : '1'; + alt2 += i % 2 === 0 ? '1' : '0'; + } + + const diff = (a, b) => { + let cnt = 0; + for (let i = 0; i < a.length; i++) { + if (a[i] !== b[i]) cnt++; + } + return cnt; + }; + + for (let i = 0; i < n; i++) { + const newS = s.slice(i) + s.slice(0, i); + res = Math.min(res, Math.min(diff(alt1, newS), diff(alt2, newS))); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n)$ + +--- + +## 2. Brute Force (Space Optimized) + +::tabs-start + +```python +class Solution: + def minFlips(self, s: str) -> int: + n = res = len(s) + + for i in range(n): + start_0 = 1 if s[i] != '0' else 0 + start_1 = 1 if s[i] != '1' else 0 + c = '0' + j = (i + 1) % n + while j != i: + start_1 += 1 if s[j] != c else 0 + start_0 += 1 if s[j] == c else 0 + c = '0' if c == '1' else '1' + j = (j + 1) % n + + res = min(res, min(start_1, start_0)) + return res +``` + +```java +public class Solution { + public int minFlips(String s) { + int n = s.length(); + int res = n; + + for (int i = 0; i < n; i++) { + int start0 = s.charAt(i) != '0' ? 1 : 0; + int start1 = s.charAt(i) != '1' ? 1 : 0; + char c = '0'; + int j = (i + 1) % n; + + while (j != i) { + start1 += s.charAt(j) != c ? 1 : 0; + start0 += s.charAt(j) == c ? 1 : 0; + c = c == '1' ? '0' : '1'; + j = (j + 1) % n; + } + + res = Math.min(res, Math.min(start1, start0)); + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int minFlips(string s) { + int n = s.size(), res = n; + + for (int i = 0; i < n; i++) { + int start0 = (s[i] != '0') ? 1 : 0; + int start1 = (s[i] != '1') ? 1 : 0; + char c = '0'; + int j = (i + 1) % n; + + while (j != i) { + start1 += (s[j] != c) ? 1 : 0; + start0 += (s[j] == c) ? 1 : 0; + c = (c == '1') ? '0' : '1'; + j = (j + 1) % n; + } + + res = min(res, min(start1, start0)); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {number} + */ + minFlips(s) { + const n = s.length; + let res = n; + + for (let i = 0; i < n; i++) { + let start0 = s[i] !== '0' ? 1 : 0; + let start1 = s[i] !== '1' ? 1 : 0; + let c = '0'; + let j = (i + 1) % n; + + while (j !== i) { + start1 += s[j] !== c ? 1 : 0; + start0 += s[j] === c ? 1 : 0; + c = c === '1' ? '0' : '1'; + j = (j + 1) % n; + } + + res = Math.min(res, Math.min(start1, start0)); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(1)$ extra space. + +--- + +## 3. Sliding Window + +::tabs-start + +```python +class Solution: + def minFlips(self, s: str) -> int: + n = len(s) + s = s + s + alt1, alt2 = [], [] + for i in range(len(s)): + alt1.append("0" if i % 2 == 0 else "1") + alt2.append("1" if i % 2 == 0 else "0") + + res = len(s) + diff1, diff2 = 0, 0 + l = 0 + + for r in range(len(s)): + if s[r] != alt1[r]: + diff1 += 1 + if s[r] != alt2[r]: + diff2 += 1 + + if r - l + 1 > n: + if s[l] != alt1[l]: + diff1 -= 1 + if s[l] != alt2[l]: + diff2 -= 1 + l += 1 + + if r - l + 1 == n: + res = min(res, diff1, diff2) + + return res +``` + +```java +public class Solution { + public int minFlips(String s) { + int n = s.length(); + s = s + s; + StringBuilder alt1 = new StringBuilder(); + StringBuilder alt2 = new StringBuilder(); + + for (int i = 0; i < s.length(); i++) { + alt1.append(i % 2 == 0 ? '0' : '1'); + alt2.append(i % 2 == 0 ? '1' : '0'); + } + + int res = n, diff1 = 0, diff2 = 0, l = 0; + + for (int r = 0; r < s.length(); r++) { + if (s.charAt(r) != alt1.charAt(r)) diff1++; + if (s.charAt(r) != alt2.charAt(r)) diff2++; + + if (r - l + 1 > n) { + if (s.charAt(l) != alt1.charAt(l)) diff1--; + if (s.charAt(l) != alt2.charAt(l)) diff2--; + l++; + } + + if (r - l + 1 == n) { + res = Math.min(res, Math.min(diff1, diff2)); + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int minFlips(string s) { + int n = s.size(); + s += s; + string alt1, alt2; + for (int i = 0; i < s.size(); i++) { + alt1 += (i % 2 == 0) ? '0' : '1'; + alt2 += (i % 2 == 0) ? '1' : '0'; + } + + int res = n, diff1 = 0, diff2 = 0, l = 0; + + for (int r = 0; r < s.size(); r++) { + if (s[r] != alt1[r]) diff1++; + if (s[r] != alt2[r]) diff2++; + + if (r - l + 1 > n) { + if (s[l] != alt1[l]) diff1--; + if (s[l] != alt2[l]) diff2--; + l++; + } + + if (r - l + 1 == n) { + res = min(res, min(diff1, diff2)); + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {number} + */ + minFlips(s) { + const n = s.length; + s = s + s; + let alt1 = [], alt2 = []; + + for (let i = 0; i < s.length; i++) { + alt1.push(i % 2 === 0 ? "0" : "1"); + alt2.push(i % 2 === 0 ? "1" : "0"); + } + + let res = n, diff1 = 0, diff2 = 0, l = 0; + + for (let r = 0; r < s.length; r++) { + if (s[r] !== alt1[r]) diff1++; + if (s[r] !== alt2[r]) diff2++; + + if (r - l + 1 > n) { + if (s[l] !== alt1[l]) diff1--; + if (s[l] !== alt2[l]) diff2--; + l++; + } + + if (r - l + 1 === n) { + res = Math.min(res, diff1, diff2); + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 4. Sliding Window (Space Optimized) + +::tabs-start + +```python +class Solution: + def minFlips(self, s: str) -> int: + res = n = len(s) + diff1 = diff2 = l = 0 + + rstart_0 = lstart_0 = '0' + + for r in range(2 * n): + if s[r % n] != rstart_0: + diff1 += 1 + if s[r % n] == rstart_0: + diff2 += 1 + + if r - l + 1 > n: + if s[l] != lstart_0: + diff1 -= 1 + if s[l] == lstart_0: + diff2 -= 1 + l += 1 + lstart_0 = '1' if lstart_0 == '0' else '0' + + if r - l + 1 == n: + res = min(res, diff1, diff2) + + rstart_0 = '1' if rstart_0 == '0' else '0' + + return res +``` + +```java +public class Solution { + public int minFlips(String s) { + int n = s.length(); + int res = n, diff1 = 0, diff2 = 0, l = 0; + + char rstart_0 = '0', lstart_0 = '0'; + + for (int r = 0; r < 2 * n; r++) { + if (s.charAt(r % n) != rstart_0) diff1++; + if (s.charAt(r % n) == rstart_0) diff2++; + + if (r - l + 1 > n) { + if (s.charAt(l) != lstart_0) diff1--; + if (s.charAt(l) == lstart_0) diff2--; + l++; + lstart_0 = (lstart_0 == '0') ? '1' : '0'; + } + + if (r - l + 1 == n) { + res = Math.min(res, Math.min(diff1, diff2)); + } + + rstart_0 = (rstart_0 == '0') ? '1' : '0'; + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int minFlips(string s) { + int n = s.size(); + int res = n, diff1 = 0, diff2 = 0, l = 0; + + char rstart_0 = '0', lstart_0 = '0'; + + for (int r = 0; r < 2 * n; r++) { + if (s[r % n] != rstart_0) diff1++; + if (s[r % n] == rstart_0) diff2++; + + if (r - l + 1 > n) { + if (s[l] != lstart_0) diff1--; + if (s[l] == lstart_0) diff2--; + l++; + lstart_0 = (lstart_0 == '0') ? '1' : '0'; + } + + if (r - l + 1 == n) { + res = min(res, min(diff1, diff2)); + } + + rstart_0 = (rstart_0 == '0') ? '1' : '0'; + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {number} + */ + minFlips(s) { + const n = s.length; + let res = n, diff1 = 0, diff2 = 0, l = 0; + + let rstart_0 = '0', lstart_0 = '0'; + + for (let r = 0; r < 2 * n; r++) { + if (s[r % n] !== rstart_0) diff1++; + if (s[r % n] === rstart_0) diff2++; + + if (r - l + 1 > n) { + if (s[l] !== lstart_0) diff1--; + if (s[l] === lstart_0) diff2--; + l++; + lstart_0 = lstart_0 === '0' ? '1' : '0'; + } + + if (r - l + 1 === n) { + res = Math.min(res, Math.min(diff1, diff2)); + } + + rstart_0 = rstart_0 === '0' ? '1' : '0'; + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. + +--- + +## 5. Dynamic Programming + +::tabs-start + +```python +class Solution: + def minFlips(self, s: str) -> int: + start_1 = 0 + for i in range(len(s)): + if i & 1: + start_1 += s[i] == "1" + else: + start_1 += s[i] == "0" + + start_0 = len(s) - start_1 + ans = min(start_0, start_1) + if len(s) % 2 == 0: + return ans + + dp0, dp1 = start_0, start_1 + for c in s: + dp0, dp1 = dp1, dp0 + if c == "1": + dp0 += 1 + dp1 -= 1 + else: + dp0 -= 1 + dp1 += 1 + ans = min(dp0, dp1, ans) + return ans +``` + +```java +public class Solution { + public int minFlips(String s) { + int start_1 = 0; + int n = s.length(); + + for (int i = 0; i < n; i++) { + if ((i & 1) == 1) { + start_1 += s.charAt(i) == '1' ? 1 : 0; + } else { + start_1 += s.charAt(i) == '0' ? 1 : 0; + } + } + + int start_0 = n - start_1; + int ans = Math.min(start_0, start_1); + if (n % 2 == 0) { + return ans; + } + + int dp0 = start_0, dp1 = start_1; + for (char c : s.toCharArray()) { + int temp = dp0; + dp0 = dp1; + dp1 = temp; + if (c == '1') { + dp0++; + dp1--; + } else { + dp0--; + dp1++; + } + ans = Math.min(ans, Math.min(dp0, dp1)); + } + + return ans; + } +} +``` + +```cpp +class Solution { +public: + int minFlips(string s) { + int start_1 = 0, n = s.size(); + + for (int i = 0; i < n; i++) { + if (i & 1) { + start_1 += (s[i] == '1'); + } else { + start_1 += (s[i] == '0'); + } + } + + int start_0 = n - start_1; + int ans = min(start_0, start_1); + if (n % 2 == 0) { + return ans; + } + + int dp0 = start_0, dp1 = start_1; + for (char c : s) { + int temp = dp0; + dp0 = dp1; + dp1 = temp; + if (c == '1') { + dp0++; + dp1--; + } else { + dp0--; + dp1++; + } + ans = min({ans, dp0, dp1}); + } + + return ans; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {number} + */ + minFlips(s) { + let start_1 = 0; + const n = s.length; + + for (let i = 0; i < n; i++) { + if (i & 1) { + start_1 += s[i] === '1' ? 1 : 0; + } else { + start_1 += s[i] === '0' ? 1 : 0; + } + } + + let start_0 = n - start_1; + let ans = Math.min(start_0, start_1); + if (n % 2 === 0) { + return ans; + } + + let dp0 = start_0, dp1 = start_1; + for (const c of s) { + [dp0, dp1] = [dp1, dp0]; + if (c === '1') { + dp0++; + dp1--; + } else { + dp0--; + dp1++; + } + ans = Math.min(ans, dp0, dp1); + } + + return ans; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. \ No newline at end of file diff --git a/articles/minimum-size-subarray-sum.md b/articles/minimum-size-subarray-sum.md new file mode 100644 index 000000000..74be08d78 --- /dev/null +++ b/articles/minimum-size-subarray-sum.md @@ -0,0 +1,332 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def minSubArrayLen(self, target: int, nums: List[int]) -> int: + n = len(nums) + res = float("inf") + + for i in range(n): + curSum = 0 + for j in range(i, n): + curSum += nums[j] + if curSum >= target: + res = min(res, j - i + 1) + break + + return 0 if res == float("inf") else res +``` + +```java +public class Solution { + public int minSubArrayLen(int target, int[] nums) { + int n = nums.length; + int res = Integer.MAX_VALUE; + + for (int i = 0; i < n; i++) { + int curSum = 0, j = i; + while (j < n) { + curSum += nums[j]; + if (curSum >= target) { + res = Math.min(res, j - i + 1); + break; + } + j++; + } + } + + return res == Integer.MAX_VALUE ? 0 : res; + } +} +``` + +```cpp +class Solution { +public: + int minSubArrayLen(int target, vector& nums) { + int n = nums.size(); + int res = INT_MAX; + + for (int i = 0; i < n; i++) { + int curSum = 0, j = i; + while (j < n) { + curSum += nums[j]; + if (curSum >= target) { + res = min(res, j - i + 1); + break; + } + j++; + } + } + + return res == INT_MAX ? 0 : res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} target + * @param {number[]} nums + * @return {number} + */ + minSubArrayLen(target, nums) { + let n = nums.length; + let res = Infinity; + + for (let i = 0; i < n; i++) { + let curSum = 0, j = i; + while (j < n) { + curSum += nums[j]; + if (curSum >= target) { + res = Math.min(res, j - i + 1); + break; + } + j++; + } + } + + return res == Infinity ? 0 : res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(1)$ extra space. + +--- + +## 2. Sliding Window + +::tabs-start + +```python +class Solution: + def minSubArrayLen(self, target: int, nums: List[int]) -> int: + l, total = 0, 0 + res = float("inf") + + for r in range(len(nums)): + total += nums[r] + while total >= target: + res = min(r - l + 1, res) + total -= nums[l] + l += 1 + + return 0 if res == float("inf") else res +``` + +```java +public class Solution { + public int minSubArrayLen(int target, int[] nums) { + int l = 0, total = 0; + int res = Integer.MAX_VALUE; + + for (int r = 0; r < nums.length; r++) { + total += nums[r]; + while (total >= target) { + res = Math.min(r - l + 1, res); + total -= nums[l]; + l++; + } + } + + return res == Integer.MAX_VALUE ? 0 : res; + } +} +``` + +```cpp +class Solution { +public: + int minSubArrayLen(int target, vector& nums) { + int l = 0, total = 0, res = INT_MAX; + + for (int r = 0; r < nums.size(); r++) { + total += nums[r]; + while (total >= target) { + res = min(r - l + 1, res); + total -= nums[l]; + l++; + } + } + + return res == INT_MAX ? 0 : res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} target + * @param {number[]} nums + * @return {number} + */ + minSubArrayLen(target, nums) { + let l = 0, total = 0; + let res = Infinity; + + for (let r = 0; r < nums.length; r++) { + total += nums[r]; + while (total >= target) { + res = Math.min(r - l + 1, res); + total -= nums[l]; + l++; + } + } + + return res === Infinity ? 0 : res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. + +--- + +## 3. Prefix Sum + Binary Search + +::tabs-start + +```python +class Solution: + def minSubArrayLen(self, target: int, nums: List[int]) -> int: + n = len(nums) + prefixSum = [0] * (n + 1) + for i in range(n): + prefixSum[i + 1] = prefixSum[i] + nums[i] + + res = n + 1 + for i in range(n): + l, r = i, n + while l < r: + mid = (l + r) // 2 + curSum = prefixSum[mid + 1] - prefixSum[i] + if curSum >= target: + r = mid + else: + l = mid + 1 + if l != n: + res = min(res, l - i + 1) + + return res % (n + 1) +``` + +```java +public class Solution { + public int minSubArrayLen(int target, int[] nums) { + int n = nums.length; + int[] prefixSum = new int[n + 1]; + for (int i = 0; i < n; i++) { + prefixSum[i + 1] = prefixSum[i] + nums[i]; + } + + int res = n + 1; + for (int i = 0; i < n; i++) { + int l = i, r = n; + while (l < r) { + int mid = (l + r) / 2; + int curSum = prefixSum[mid + 1] - prefixSum[i]; + if (curSum >= target) { + r = mid; + } else { + l = mid + 1; + } + } + if (l != n) { + res = Math.min(res, l - i + 1); + } + } + + return res % (n + 1); + } +} +``` + +```cpp +class Solution { +public: + int minSubArrayLen(int target, vector& nums) { + int n = nums.size(); + vector prefixSum(n + 1, 0); + for (int i = 0; i < n; i++) { + prefixSum[i + 1] = prefixSum[i] + nums[i]; + } + + int res = n + 1; + for (int i = 0; i < n; i++) { + int l = i, r = n; + while (l < r) { + int mid = (l + r) / 2; + int curSum = prefixSum[mid + 1] - prefixSum[i]; + if (curSum >= target) { + r = mid; + } else { + l = mid + 1; + } + } + if (l != n) { + res = min(res, l - i + 1); + } + } + + return res % (n + 1); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} target + * @param {number[]} nums + * @return {number} + */ + minSubArrayLen(target, nums) { + const n = nums.length; + const prefixSum = new Array(n + 1).fill(0); + for (let i = 0; i < n; i++) { + prefixSum[i + 1] = prefixSum[i] + nums[i]; + } + + let res = n + 1; + for (let i = 0; i < n; i++) { + let l = i, r = n; + while (l < r) { + const mid = Math.floor((l + r) / 2); + const curSum = prefixSum[mid + 1] - prefixSum[i]; + if (curSum >= target) { + r = mid; + } else { + l = mid + 1; + } + } + if (l !== n) { + res = Math.min(res, l - i + 1); + } + } + + return res % (n + 1); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/move-zeroes.md b/articles/move-zeroes.md new file mode 100644 index 000000000..46a78c170 --- /dev/null +++ b/articles/move-zeroes.md @@ -0,0 +1,253 @@ +## 1. Extra Space + +::tabs-start + +```python +class Solution: + def moveZeroes(self, nums: List[int]) -> None: + """ + Do not return anything, modify nums in-place instead. + """ + tmp = [] + for num in nums: + if num != 0: + tmp.append(num) + + for i in range(len(nums)): + if i < len(tmp): + nums[i] = tmp[i] + else: + nums[i] = 0 +``` + +```java +public class Solution { + public void moveZeroes(int[] nums) { + List tmp = new ArrayList<>(); + for (int num : nums) { + if (num != 0) { + tmp.add(num); + } + } + + for (int i = 0; i < nums.length; i++) { + if (i < tmp.size()) { + nums[i] = tmp.get(i); + } else { + nums[i] = 0; + } + } + } +} +``` + +```cpp +class Solution { +public: + void moveZeroes(vector& nums) { + vector tmp; + for (int num : nums) { + if (num != 0) { + tmp.push_back(num); + } + } + + for (int i = 0; i < nums.size(); ++i) { + if (i < tmp.size()) { + nums[i] = tmp[i]; + } else { + nums[i] = 0; + } + } + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {void} Do not return anything, modify nums in-place instead. + */ + moveZeroes(nums) { + let tmp = []; + for (let num of nums) { + if (num !== 0) { + tmp.push(num); + } + } + + for (let i = 0; i < nums.length; i++) { + if (i < tmp.length) { + nums[i] = tmp[i]; + } else { + nums[i] = 0; + } + } + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Two Pointers (Two Pass) +::tabs-start + +```python +class Solution: + def moveZeroes(self, nums: List[int]) -> None: + """ + Do not return anything, modify nums in-place instead. + """ + l = 0 + for r in range(len(nums)): + if nums[r] != 0: + nums[l] = nums[r] + l += 1 + + while l < len(nums): + nums[l] = 0 + l += 1 +``` + +```java +public class Solution { + public void moveZeroes(int[] nums) { + int l = 0; + for (int r = 0; r < nums.length; r++) { + if (nums[r] != 0) { + nums[l++] = nums[r]; + } + } + + while (l < nums.length) { + nums[l++] = 0; + } + } +} +``` + +```cpp +class Solution { +public: + void moveZeroes(vector& nums) { + int l = 0; + for (int r = 0; r < nums.size(); r++) { + if (nums[r] != 0) { + nums[l++] = nums[r]; + } + } + + while (l < nums.size()) { + nums[l++] = 0; + } + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {void} Do not return anything, modify nums in-place instead. + */ + moveZeroes(nums) { + let l = 0; + for (let r = 0; r < nums.length; r++) { + if (nums[r] != 0) { + nums[l++] = nums[r]; + } + } + + while (l < nums.length) { + nums[l++] = 0; + } + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ + +--- + +## 3. Two Pointers (One Pass) + +::tabs-start + +```python +class Solution: + def moveZeroes(self, nums: List[int]) -> None: + """ + Do not return anything, modify nums in-place instead. + """ + l = 0 + for r in range(len(nums)): + if nums[r]: + nums[l], nums[r] = nums[r], nums[l] + l += 1 +``` + +```java +public class Solution { + public void moveZeroes(int[] nums) { + int l = 0; + for (int r = 0; r < nums.length; r++) { + if (nums[r] != 0) { + int temp = nums[l]; + nums[l] = nums[r]; + nums[r] = temp; + l++; + } + } + } +} +``` + +```cpp +class Solution { +public: + void moveZeroes(vector& nums) { + for (int l = 0, r = 0; r < nums.size(); r++) { + if (nums[r]) { + swap(nums[l++], nums[r]); + } + } + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {void} Do not return anything, modify nums in-place instead. + */ + moveZeroes(nums) { + for (let l = 0, r = 0; r < nums.length; r++) { + if (nums[r] !== 0) { + [nums[l], nums[r]] = [nums[r], nums[l]]; + l++; + } + } + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/number-of-sub-arrays-of-size-k-and-average-greater-than-or-equal-to-threshold.md b/articles/number-of-sub-arrays-of-size-k-and-average-greater-than-or-equal-to-threshold.md new file mode 100644 index 000000000..f99cc6f13 --- /dev/null +++ b/articles/number-of-sub-arrays-of-size-k-and-average-greater-than-or-equal-to-threshold.md @@ -0,0 +1,398 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def numOfSubarrays(self, arr: List[int], k: int, threshold: int) -> int: + res = 0 + l = 0 + + for r in range(k - 1, len(arr)): + sum_ = 0 + for i in range(l, r + 1): + sum_ += arr[i] + + if sum_ / k >= threshold: + res += 1 + l += 1 + + return res +``` + +```java +public class Solution { + public int numOfSubarrays(int[] arr, int k, int threshold) { + int res = 0, l = 0; + + for (int r = k - 1; r < arr.length; r++) { + int sum = 0; + for (int i = l; i <= r; i++) { + sum += arr[i]; + } + if (sum / k >= threshold) { + res++; + } + l++; + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int numOfSubarrays(vector& arr, int k, int threshold) { + int res = 0, l = 0; + + for (int r = k - 1; r < arr.size(); r++) { + int sum = 0; + for (int i = l; i <= r; i++) { + sum += arr[i]; + } + if (sum / k >= threshold) { + res++; + } + l++; + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} arr + * @param {number} k + * @param {number} threshold + * @return {number} + */ + numOfSubarrays(arr, k, threshold) { + let res = 0, l = 0; + + for (let r = k - 1; r < arr.length; r++) { + let sum = 0; + for (let i = l; i <= r; i++) { + sum += arr[i]; + } + if (sum / k >= threshold) { + res++; + } + l++; + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * k)$ +* Space complexity: $O(1)$ + +> Where $n$ is the size of the array $arr$ and $k$ is the size of the sub-array. + +--- + +## 2. Prefix Sum + +::tabs-start + +```python +class Solution: + def numOfSubarrays(self, arr: List[int], k: int, threshold: int) -> int: + prefix_sum = [0] * (len(arr) + 1) + for i in range(len(arr)): + prefix_sum[i + 1] += prefix_sum[i] + arr[i] + + res = l = 0 + for r in range(k - 1, len(arr)): + sum_ = prefix_sum[r + 1] - prefix_sum[l] + if sum_ / k >= threshold: + res += 1 + l += 1 + + return res +``` + +```java +public class Solution { + public int numOfSubarrays(int[] arr, int k, int threshold) { + int[] prefixSum = new int[arr.length + 1]; + for (int i = 0; i < arr.length; i++) { + prefixSum[i + 1] += prefixSum[i] + arr[i]; + } + + int res = 0, l = 0; + for (int r = k - 1; r < arr.length; r++) { + int sum = prefixSum[r + 1] - prefixSum[l]; + if (sum / k >= threshold) { + res++; + } + l++; + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int numOfSubarrays(vector& arr, int k, int threshold) { + vector prefixSum(arr.size() + 1); + for (int i = 0; i < arr.size(); i++) { + prefixSum[i + 1] += prefixSum[i] + arr[i]; + } + + int res = 0, l = 0; + for (int r = k - 1; r < arr.size(); r++) { + int sum = prefixSum[r + 1] - prefixSum[l]; + if (sum / k >= threshold) { + res++; + } + l++; + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} arr + * @param {number} k + * @param {number} threshold + * @return {number} + */ + numOfSubarrays(arr, k, threshold) { + const prefixSum = new Int32Array(arr.length + 1); + for (let i = 0; i < arr.length; i++) { + prefixSum[i + 1] += prefixSum[i] + arr[i]; + } + + let res = 0, l = 0; + for (let r = k - 1; r < arr.length; r++) { + const sum = prefixSum[r + 1] - prefixSum[l]; + if (sum / k >= threshold) { + res++; + } + l++; + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +> Where $n$ is the size of the array $arr$ and $k$ is the size of the sub-array. + +--- + +## 3. Sliding Window - I + +::tabs-start + +```python +class Solution: + def numOfSubarrays(self, arr: List[int], k: int, threshold: int) -> int: + res = 0 + curSum = sum(arr[:k - 1]) + + for L in range(len(arr) - k + 1): + curSum += arr[L + k - 1] + if (curSum / k) >= threshold: + res += 1 + curSum -= arr[L] + return res +``` + +```java +public class Solution { + public int numOfSubarrays(int[] arr, int k, int threshold) { + int res = 0; + int curSum = 0; + + for (int i = 0; i < k - 1; i++) { + curSum += arr[i]; + } + + for (int L = 0; L <= arr.length - k; L++) { + curSum += arr[L + k - 1]; + if ((curSum / k) >= threshold) { + res++; + } + curSum -= arr[L]; + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int numOfSubarrays(vector& arr, int k, int threshold) { + int res = 0, curSum = 0; + + for (int i = 0; i < k - 1; i++) { + curSum += arr[i]; + } + + for (int L = 0; L <= arr.size() - k; L++) { + curSum += arr[L + k - 1]; + if ((curSum / k) >= threshold) { + res++; + } + curSum -= arr[L]; + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} arr + * @param {number} k + * @param {number} threshold + * @return {number} + */ + numOfSubarrays(arr, k, threshold) { + let res = 0; + let curSum = 0; + + for (let i = 0; i < k - 1; i++) { + curSum += arr[i]; + } + + for (let L = 0; L <= arr.length - k; L++) { + curSum += arr[L + k - 1]; + if ((curSum / k) >= threshold) { + res++; + } + curSum -= arr[L]; + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. + +> Where $n$ is the size of the array $arr$ and $k$ is the size of the sub-array. + +--- + +## 4. Sliding Window - II + +::tabs-start + +```python +class Solution: + def numOfSubarrays(self, arr: List[int], k: int, threshold: int) -> int: + threshold *= k + res = curSum = 0 + for R in range(len(arr)): + curSum += arr[R] + if R >= k - 1: + res += curSum >= threshold + curSum -= arr[R - k + 1] + return res +``` + +```java +public class Solution { + public int numOfSubarrays(int[] arr, int k, int threshold) { + threshold *= k; + int res = 0, curSum = 0; + + for (int R = 0; R < arr.length; R++) { + curSum += arr[R]; + if (R >= k - 1) { + if (curSum >= threshold) { + res++; + } + curSum -= arr[R - k + 1]; + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int numOfSubarrays(vector& arr, int k, int threshold) { + threshold *= k; + int res = 0, curSum = 0; + + for (int R = 0; R < arr.size(); R++) { + curSum += arr[R]; + if (R >= k - 1) { + if (curSum >= threshold) { + res++; + } + curSum -= arr[R - k + 1]; + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} arr + * @param {number} k + * @param {number} threshold + * @return {number} + */ + numOfSubarrays(arr, k, threshold) { + threshold *= k; + let res = 0, curSum = 0; + + for (let R = 0; R < arr.length; R++) { + curSum += arr[R]; + if (R >= k - 1) { + if (curSum >= threshold) { + res++; + } + curSum -= arr[R - k + 1]; + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. + +> Where $n$ is the size of the array $arr$ and $k$ is the size of the sub-array. \ No newline at end of file diff --git a/articles/remove-duplicates-from-sorted-array.md b/articles/remove-duplicates-from-sorted-array.md new file mode 100644 index 000000000..57bb96cdc --- /dev/null +++ b/articles/remove-duplicates-from-sorted-array.md @@ -0,0 +1,214 @@ +## 1. Sorted Set + +::tabs-start + +```python +class Solution: + def removeDuplicates(self, nums: list[int]) -> int: + unique = sorted(set(nums)) + nums[:len(unique)] = unique + return len(unique) +``` + +```java +public class Solution { + public int removeDuplicates(int[] nums) { + TreeSet unique = new TreeSet<>(); + for (int num : nums) { + unique.add(num); + } + int i = 0; + for (int num : unique) { + nums[i++] = num; + } + return unique.size(); + } +} +``` + +```cpp +class Solution { +public: + int removeDuplicates(vector& nums) { + set unique(nums.begin(), nums.end()); + int i = 0; + for (int num : unique) { + nums[i++] = num; + } + return unique.size(); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + removeDuplicates(nums) { + const unique = Array.from(new Set(nums)).sort((a, b) => a - b); + for (let i = 0; i < unique.length; i++) { + nums[i] = unique[i]; + } + return unique.length; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Two Pointers - I + +::tabs-start + +```python +class Solution: + def removeDuplicates(self, nums: list[int]) -> int: + n = len(nums) + l = r = 0 + while r < n: + nums[l] = nums[r] + while r < n and nums[r] == nums[l]: + r += 1 + l += 1 + return l +``` + +```java +public class Solution { + public int removeDuplicates(int[] nums) { + int n = nums.length, l = 0, r = 0; + while (r < n) { + nums[l] = nums[r]; + while (r < n && nums[r] == nums[l]) { + r++; + } + l++; + } + return l; + } +} +``` + +```cpp +class Solution { +public: + int removeDuplicates(vector& nums) { + int n = nums.size(), l = 0, r = 0; + while (r < n) { + nums[l] = nums[r]; + while (r < n && nums[r] == nums[l]) { + r++; + } + l++; + } + return l; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + removeDuplicates(nums) { + let n = nums.length, l = 0, r = 0; + while (r < n) { + nums[l] = nums[r]; + while (r < n && nums[r] === nums[l]) { + r++; + } + l++; + } + return l; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ + +--- + +## 3. Two Pointers - II + +::tabs-start + +```python +class Solution: + def removeDuplicates(self, nums: list[int]) -> int: + l = 1 + for r in range(1, len(nums)): + if nums[r] != nums[r - 1]: + nums[l] = nums[r] + l += 1 + return l +``` + +```java +public class Solution { + public int removeDuplicates(int[] nums) { + int l = 1; + for (int r = 1; r < nums.length; r++) { + if (nums[r] != nums[r - 1]) { + nums[l++] = nums[r]; + } + } + return l; + } +} +``` + +```cpp +class Solution { +public: + int removeDuplicates(vector& nums) { + int l = 1; + for (int r = 1; r < nums.size(); r++) { + if (nums[r] != nums[r - 1]) { + nums[l++] = nums[r]; + } + } + return l; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + removeDuplicates(nums) { + let l = 1; + for (let r = 1; r < nums.length; r++) { + if (nums[r] !== nums[r - 1]) { + nums[l++] = nums[r]; + } + } + return l; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/reverse-string.md b/articles/reverse-string.md new file mode 100644 index 000000000..ac60401c5 --- /dev/null +++ b/articles/reverse-string.md @@ -0,0 +1,357 @@ +## 1. Array + +::tabs-start + +```python +class Solution: + def reverseString(self, s: List[str]) -> None: + """ + Do not return anything, modify s in-place instead. + """ + tmp = [] + for i in range(len(s) - 1, -1, -1): + tmp.append(s[i]) + for i in range(len(s)): + s[i] = tmp[i] +``` + +```java +public class Solution { + public void reverseString(char[] s) { + char[] tmp = new char[s.length]; + for (int i = s.length - 1, j = 0; i >= 0; i--, j++) { + tmp[j] = s[i]; + } + for (int i = 0; i < s.length; i++) { + s[i] = tmp[i]; + } + } +} +``` + +```cpp +class Solution { +public: + void reverseString(vector& s) { + vector tmp; + for (int i = s.size() - 1; i >= 0; i--) { + tmp.push_back(s[i]); + } + for (int i = 0; i < s.size(); i++) { + s[i] = tmp[i]; + } + } +}; +``` + +```javascript +class Solution { + /** + * @param {character[]} s + * @return {void} Do not return anything, modify s in-place instead. + */ + reverseString(s) { + const tmp = []; + for (let i = s.length - 1; i >= 0; i--) { + tmp.push(s[i]); + } + for (let i = 0; i < s.length; i++) { + s[i] = tmp[i]; + } + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Recursion + +::tabs-start + +```python +class Solution: + def reverseString(self, s: List[str]) -> None: + """ + Do not return anything, modify s in-place instead. + """ + def reverse(l, r): + if l < r: + reverse(l + 1, r - 1) + s[l], s[r] = s[r], s[l] + + reverse(0, len(s) - 1) +``` + +```java +public class Solution { + public void reverseString(char[] s) { + reverse(s, 0, s.length - 1); + } + + private void reverse(char[] s, int l, int r) { + if (l < r) { + reverse(s, l + 1, r - 1); + char temp = s[l]; + s[l] = s[r]; + s[r] = temp; + } + } +} +``` + +```cpp +class Solution { +public: + void reverseString(vector& s) { + reverse(s, 0, s.size() - 1); + } + +private: + void reverse(vector& s, int l, int r) { + if (l < r) { + reverse(s, l + 1, r - 1); + swap(s[l], s[r]); + } + } +}; +``` + +```javascript +class Solution { + /** + * @param {character[]} s + * @return {void} Do not return anything, modify s in-place instead. + */ + reverseString(s) { + const reverse = (l, r) => { + if (l < r) { + reverse(l + 1, r - 1); + [s[l], s[r]] = [s[r], s[l]]; + } + }; + reverse(0, s.length - 1); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ for recursion stack. + +--- + +## 3. Stack + +::tabs-start + +```python +class Solution: + def reverseString(self, s: List[str]) -> None: + """ + Do not return anything, modify s in-place instead. + """ + stack = [] + for c in s: + stack.append(c) + i = 0 + while stack: + s[i] = stack.pop() + i += 1 +``` + +```java +public class Solution { + public void reverseString(char[] s) { + Stack stack = new Stack<>(); + for (char c : s) { + stack.push(c); + } + int i = 0; + while (!stack.isEmpty()) { + s[i++] = stack.pop(); + } + } +} +``` + +```cpp +class Solution { +public: + void reverseString(vector& s) { + stack stk; + for (char& c : s) { + stk.push(c); + } + int i = 0; + while (!stk.empty()) { + s[i++] = stk.top(); + stk.pop(); + } + } +}; +``` + +```javascript +class Solution { + /** + * @param {character[]} s + * @return {void} Do not return anything, modify s in-place instead. + */ + reverseString(s) { + const stack = []; + for (const c of s) { + stack.push(c); + } + let i = 0; + while (stack.length) { + s[i++] = stack.pop(); + } + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 4. Built-In Function + +::tabs-start + +```python +class Solution: + def reverseString(self, s: List[str]) -> None: + """ + Do not return anything, modify s in-place instead. + """ + s.reverse() +``` + +```java +public class Solution { + public void reverseString(char[] s) { + List list = new ArrayList<>(); + for (char c : s) { + list.add(c); + } + Collections.reverse(list); + + for (int i = 0; i < s.length; i++) { + s[i] = list.get(i); + } + } +} +``` + +```cpp +class Solution { +public: + void reverseString(vector& s) { + reverse(s.begin(), s.end()); + } +}; +``` + +```javascript +class Solution { + /** + * @param {character[]} s + * @return {void} Do not return anything, modify s in-place instead. + */ + reverseString(s) { + s.reverse(); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ + +--- + +## 5. Two Pointers + +::tabs-start + +```python +class Solution: + def reverseString(self, s: List[str]) -> None: + """ + Do not return anything, modify s in-place instead. + """ + l, r = 0, len(s) - 1 + while l < r: + s[l], s[r] = s[r], s[l] + l += 1 + r -= 1 +``` + +```java +public class Solution { + public void reverseString(char[] s) { + int l = 0, r = s.length - 1; + while (l < r) { + char temp = s[l]; + s[l] = s[r]; + s[r] = temp; + l++; + r--; + } + } +} +``` + +```cpp +class Solution { +public: + void reverseString(vector& s) { + int l = 0, r = s.size() - 1; + while (l < r) { + swap(s[l++], s[r--]); + } + } +}; +``` + +```javascript +class Solution { + /** + * @param {character[]} s + * @return {void} Do not return anything, modify s in-place instead. + */ + reverseString(s) { + let l = 0, r = s.length - 1; + while (l < r) { + [s[l], s[r]] = [s[r], s[l]]; + l++; + r--; + } + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/reverse-words-in-a-string-iii.md b/articles/reverse-words-in-a-string-iii.md new file mode 100644 index 000000000..b9fb3b3cb --- /dev/null +++ b/articles/reverse-words-in-a-string-iii.md @@ -0,0 +1,387 @@ +## 1. Convert To String Array + +::tabs-start + +```python +class Solution: + def reverseWords(self, s: str) -> str: + return ' '.join([w[::-1] for w in s.split(' ')]) +``` + +```java +public class Solution { + public String reverseWords(String s) { + String[] words = s.split(" "); + for (int i = 0; i < words.length; i++) { + words[i] = new StringBuilder(words[i]).reverse().toString(); + } + return String.join(" ", words); + } +} +``` + +```cpp +class Solution { +public: + string reverseWords(string s) { + stringstream ss(s); + string word, res; + bool first = true; + + while (ss >> word) { + reverse(word.begin(), word.end()); + if (first) { + res += word; + first = false; + } else { + res += " " + word; + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {string} + */ + reverseWords(s) { + return s.split(' ').map(w => w.split('').reverse().join('')).join(' '); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 2. String Manipulation + +::tabs-start + +```python +class Solution: + def reverseWords(self, s: str) -> str: + tmp_str = "" + res = "" + + for r in range(len(s) + 1): + if r == len(s) or s[r] == ' ': + res += tmp_str + tmp_str = "" + if r != len(s): + res += " " + else: + tmp_str = s[r] + tmp_str + return res +``` + +```java +public class Solution { + public String reverseWords(String s) { + String tmpStr = ""; + StringBuilder res = new StringBuilder(); + + for (int r = 0; r <= s.length(); r++) { + if (r == s.length() || s.charAt(r) == ' ') { + res.append(tmpStr); + tmpStr = ""; + if (r != s.length()) { + res.append(" "); + } + } else { + tmpStr = s.charAt(r) + tmpStr; + } + } + return res.toString(); + } +} +``` + +```cpp +class Solution { +public: + string reverseWords(string s) { + string tmpStr = ""; + string res = ""; + + for (int r = 0; r <= s.size(); r++) { + if (r == s.size() || s[r] == ' ') { + res += tmpStr; + tmpStr = ""; + if (r != s.size()) { + res += " "; + } + } else { + tmpStr = s[r] + tmpStr; + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {string} + */ + reverseWords(s) { + let tmpStr = ""; + let res = ""; + + for (let r = 0; r <= s.length; r++) { + if (r === s.length || s[r] === ' ') { + res += tmpStr; + tmpStr = ""; + if (r !== s.length) { + res += " "; + } + } else { + tmpStr = s[r] + tmpStr; + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n)$ + +--- + +## 3. Two Pointers - I + +::tabs-start + +```python +class Solution: + def reverseWords(self, s: str) -> str: + s = list(s) + l = 0 + for r in range(len(s)): + if s[r] == " " or r == len(s) - 1: + temp_l, temp_r = l, r - 1 if s[r] == " " else r + while temp_l < temp_r: + s[temp_l], s[temp_r] = s[temp_r], s[temp_l] + temp_l += 1 + temp_r -= 1 + l = r + 1 + return "".join(s) +``` + +```java +public class Solution { + public String reverseWords(String s) { + char[] chars = s.toCharArray(); + int l = 0; + for (int r = 0; r < chars.length; r++) { + if (chars[r] == ' ' || r == chars.length - 1) { + int tempL = l, tempR = (chars[r] == ' ') ? r - 1 : r; + while (tempL < tempR) { + char temp = chars[tempL]; + chars[tempL] = chars[tempR]; + chars[tempR] = temp; + tempL++; + tempR--; + } + l = r + 1; + } + } + return new String(chars); + } +} +``` + +```cpp +class Solution { +public: + string reverseWords(string s) { + int l = 0; + for (int r = 0; r < s.size(); r++) { + if (r == s.size() - 1 || s[r] == ' ') { + int tempL = l, tempR = s[r] == ' ' ? r - 1 : r; + while (tempL < tempR) { + swap(s[tempL], s[tempR]); + tempL++; + tempR--; + } + l = r + 1; + } + } + return s; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {string} + */ + reverseWords(s) { + let chars = s.split(''); + let l = 0; + for (let r = 0; r <= chars.length; r++) { + if (r === chars.length || chars[r] === ' ') { + let tempL = l, tempR = r - 1; + while (tempL < tempR) { + [chars[tempL], chars[tempR]] = [chars[tempR], chars[tempL]]; + tempL++; + tempR--; + } + l = r + 1; + } + } + return chars.join(''); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 4. Two Pointers - II + +::tabs-start + +```python +class Solution: + def reverseWords(self, s: str) -> str: + def reverse(i, j): + while i < j: + s[i], s[j] = s[j], s[i] + i += 1 + j -= 1 + + s = list(s) + i = 0 + while i < len(s): + if s[i] != ' ': + j = i + while j < len(s) and s[j] != ' ': + j += 1 + reverse(i, j - 1) + i = j + 1 + return ''.join(s) +``` + +```java +public class Solution { + public String reverseWords(String s) { + char[] arr = s.toCharArray(); + int n = arr.length; + + for (int i = 0; i < n; i++) { + if (arr[i] != ' ') { + int j = i; + while (j < n && arr[j] != ' ') { + j++; + } + reverse(arr, i, j - 1); + i = j; + } + } + return new String(arr); + } + + private void reverse(char[] arr, int i, int j) { + while (i < j) { + char temp = arr[i]; + arr[i] = arr[j]; + arr[j] = temp; + i++; + j--; + } + } +} +``` + +```cpp +class Solution { +public: + string reverseWords(string s) { + int n = s.size(); + for (int i = 0; i < n; i++) { + if (s[i] != ' ') { + int j = i; + while (j < n && s[j] != ' ') { + j++; + } + reverse(s, i, j - 1); + i = j; + } + } + return s; + } + +private: + void reverse(string& s, int i, int j) { + while (i < j) { + swap(s[i], s[j]); + i++; + j--; + } + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {string} + */ + reverseWords(s) { + const arr = s.split(''); + const reverse = (i, j) => { + while (i < j) { + [arr[i], arr[j]] = [arr[j], arr[i]]; + i++; + j--; + } + }; + + for (let i = 0; i < arr.length; i++) { + if (arr[i] !== ' ') { + let j = i; + while (j < arr.length && arr[j] !== ' ') { + j++; + } + reverse(i, j - 1); + i = j; + } + } + return arr.join(''); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/search-insert-position.md b/articles/search-insert-position.md new file mode 100644 index 000000000..225a7e0a1 --- /dev/null +++ b/articles/search-insert-position.md @@ -0,0 +1,396 @@ +## 1. Linear Search + +::tabs-start + +```python +class Solution: + def searchInsert(self, nums: List[int], target: int) -> int: + for i in range(len(nums)): + if nums[i] >= target: + return i + return len(nums) +``` + +```java +public class Solution { + public int searchInsert(int[] nums, int target) { + for (int i = 0; i < nums.length; i++) { + if (nums[i] >= target) { + return i; + } + } + return nums.length; + } +} +``` + +```cpp +class Solution { +public: + int searchInsert(vector& nums, int target) { + for (int i = 0; i < nums.size(); i++) { + if (nums[i] >= target) { + return i; + } + } + return nums.size(); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} target + * @return {number} + */ + searchInsert(nums, target) { + for (let i = 0; i < nums.length; i++) { + if (nums[i] >= target) { + return i; + } + } + return nums.length; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. + +--- + +## 2. Binary Search - I + +::tabs-start + +```python +class Solution: + def searchInsert(self, nums: List[int], target: int) -> int: + res = len(nums) + l, r = 0, len(nums) - 1 + while l <= r: + mid = (l + r) // 2 + if nums[mid] == target: + return mid + if nums[mid] > target: + res = mid + r = mid - 1 + else: + l = mid + 1 + return res +``` + +```java +public class Solution { + public int searchInsert(int[] nums, int target) { + int res = nums.length; + int l = 0, r = nums.length - 1; + while (l <= r) { + int mid = (l + r) / 2; + if (nums[mid] == target) { + return mid; + } + if (nums[mid] > target) { + res = mid; + r = mid - 1; + } else { + l = mid + 1; + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int searchInsert(vector& nums, int target) { + int res = nums.size(); + int l = 0, r = nums.size() - 1; + while (l <= r) { + int mid = (l + r) / 2; + if (nums[mid] == target) { + return mid; + } + if (nums[mid] > target) { + res = mid; + r = mid - 1; + } else { + l = mid + 1; + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} target + * @return {number} + */ + searchInsert(nums, target) { + let res = nums.length; + let l = 0, r = nums.length - 1; + while (l <= r) { + const mid = Math.floor((l + r) / 2); + if (nums[mid] === target) { + return mid; + } + if (nums[mid] > target) { + res = mid; + r = mid - 1; + } else { + l = mid + 1; + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(\log n)$ +* Space complexity: $O(1)$ extra space. + +--- + +## 3. Binary Search - II + +::tabs-start + +```python +class Solution: + def searchInsert(self, nums: List[int], target: int) -> int: + l, r = 0, len(nums) - 1 + while l <= r: + mid = (l + r) // 2 + if nums[mid] == target: + return mid + if nums[mid] > target: + r = mid - 1 + else: + l = mid + 1 + return l +``` + +```java +public class Solution { + public int searchInsert(int[] nums, int target) { + int l = 0, r = nums.length - 1; + while (l <= r) { + int mid = (l + r) / 2; + if (nums[mid] == target) { + return mid; + } + if (nums[mid] > target) { + r = mid - 1; + } else { + l = mid + 1; + } + } + return l; + } +} +``` + +```cpp +class Solution { +public: + int searchInsert(vector& nums, int target) { + int l = 0, r = nums.size() - 1; + while (l <= r) { + int mid = (l + r) / 2; + if (nums[mid] == target) { + return mid; + } + if (nums[mid] > target) { + r = mid - 1; + } else { + l = mid + 1; + } + } + return l; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} target + * @return {number} + */ + searchInsert(nums, target) { + let l = 0, r = nums.length - 1; + while (l <= r) { + const mid = Math.floor((l + r) / 2); + if (nums[mid] === target) { + return mid; + } + if (nums[mid] > target) { + r = mid - 1; + } else { + l = mid + 1; + } + } + return l; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(\log n)$ +* Space complexity: $O(1)$ extra space. + +--- + +## 4. Binary Search (Lower Bound) + +::tabs-start + +```python +class Solution: + def searchInsert(self, nums: List[int], target: int) -> int: + l, r = 0, len(nums) + while l < r: + m = l + ((r - l) // 2) + if nums[m] >= target: + r = m + elif nums[m] < target: + l = m + 1 + return l +``` + +```java +public class Solution { + public int searchInsert(int[] nums, int target) { + int l = 0, r = nums.length; + while (l < r) { + int m = l + (r - l) / 2; + if (nums[m] >= target) { + r = m; + } else { + l = m + 1; + } + } + return l; + } +} +``` + +```cpp +class Solution { +public: + int searchInsert(vector& nums, int target) { + int l = 0, r = nums.size(); + while (l < r) { + int m = l + (r - l) / 2; + if (nums[m] >= target) { + r = m; + } else { + l = m + 1; + } + } + return l; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} target + * @return {number} + */ + searchInsert(nums, target) { + let l = 0, r = nums.length; + while (l < r) { + let m = l + Math.floor((r - l) / 2); + if (nums[m] >= target) { + r = m; + } else { + l = m + 1; + } + } + return l; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(\log n)$ +* Space complexity: $O(1)$ + +--- + +## 5. Built-In Binary Search Function + +::tabs-start + +```python +import bisect +class Solution: + def searchInsert(self, nums: List[int], target: int) -> int: + return bisect.bisect_left(nums, target) +``` + +```java +public class Solution { + public int searchInsert(int[] nums, int target) { + int index = Arrays.binarySearch(nums, target); + return index >= 0 ? index : -index - 1; + } +} +``` + +```cpp +class Solution { +public: + int searchInsert(vector& nums, int target) { + return lower_bound(nums.begin(), nums.end(), target) - nums.begin(); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} target + * @return {number} + */ + searchInsert(nums, target) { + // There is no built in Binary Search function for JS. + let index = nums.findIndex(x => x >= target); + return index !== -1 ? index : nums.length + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(\log n)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/sort-array-by-parity.md b/articles/sort-array-by-parity.md new file mode 100644 index 000000000..b282d0cea --- /dev/null +++ b/articles/sort-array-by-parity.md @@ -0,0 +1,325 @@ +## 1. Sorting + +::tabs-start + +```python +class Solution: + def sortArrayByParity(self, nums: List[int]) -> List[int]: + nums.sort(key = lambda x: x & 1) + return nums +``` + +```java +public class Solution { + public int[] sortArrayByParity(int[] nums) { + Integer[] A = Arrays.stream(nums).boxed().toArray(Integer[]::new); + Arrays.sort(A, (a, b) -> (a & 1) - (b & 1)); + return Arrays.stream(A).mapToInt(Integer::intValue).toArray(); + } +} +``` + +```cpp +class Solution { +public: + vector sortArrayByParity(vector& nums) { + sort(nums.begin(), nums.end(), [&](int& a, int& b) { + return (a & 1) < (b & 1); + }); + return nums; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number[]} + */ + sortArrayByParity(nums) { + return nums.sort((a, b) => (a & 1) - (b & 1)); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(1)$ or $O(n)$ depending on the sorting algorithm. + +--- + +## 2. Array + +::tabs-start + +```python +class Solution: + def sortArrayByParity(self, nums: List[int]) -> List[int]: + even, odd = [], [] + for num in nums: + if num & 1: + odd.append(num) + else: + even.append(num) + + idx = 0 + for e in even: + nums[idx] = e + idx += 1 + for o in odd: + nums[idx] = o + idx += 1 + return nums +``` + +```java +public class Solution { + public int[] sortArrayByParity(int[] nums) { + List even = new ArrayList<>(); + List odd = new ArrayList<>(); + + for (int num : nums) { + if ((num & 1) == 1) { + odd.add(num); + } else { + even.add(num); + } + } + + int idx = 0; + for (int e : even) { + nums[idx++] = e; + } + for (int o : odd) { + nums[idx++] = o; + } + + return nums; + } +} +``` + +```cpp +class Solution { +public: + vector sortArrayByParity(vector& nums) { + vector even, odd; + + for (int& num : nums) { + if (num & 1) { + odd.push_back(num); + } else { + even.push_back(num); + } + } + + int idx = 0; + for (int& e : even) { + nums[idx++] = e; + } + for (int& o : odd) { + nums[idx++] = o; + } + + return nums; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number[]} + */ + sortArrayByParity(nums) { + const even = []; + const odd = []; + + for (let num of nums) { + if (num % 2) { + odd.push(num); + } else { + even.push(num); + } + } + + let idx = 0; + for (let e of even) { + nums[idx++] = e; + } + for (let o of odd) { + nums[idx++] = o; + } + + return nums; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Two Pointers - I + +::tabs-start + +```python +class Solution: + def sortArrayByParity(self, nums: List[int]) -> List[int]: + i, j = 0, len(nums) - 1 + while i < j: + if nums[i] & 1: + nums[i], nums[j] = nums[j], nums[i] + j -= 1 + else: + i += 1 + return nums +``` + +```java +public class Solution { + public int[] sortArrayByParity(int[] nums) { + int i = 0, j = nums.length - 1; + while (i < j) { + if ((nums[i] & 1) == 1) { + int temp = nums[i]; + nums[i] = nums[j]; + nums[j--] = temp; + } else { + i++; + } + } + return nums; + } +} +``` + +```cpp +class Solution { +public: + vector sortArrayByParity(vector& nums) { + int i = 0, j = nums.size() - 1; + while (i < j) { + if ((nums[i] & 1) == 1) { + swap(nums[i], nums[j]); + j--; + } else { + i++; + } + } + return nums; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number[]} + */ + sortArrayByParity(nums) { + let i = 0, j = nums.length - 1; + while (i < j) { + if ((nums[i] & 1) == 1) { + [nums[i], nums[j]] = [nums[j], nums[i]]; + j--; + } else { + i++; + } + } + return nums; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. + +--- + +## 4. Two Pointers - II + +::tabs-start + +```python +class Solution: + def sortArrayByParity(self, nums: List[int]) -> List[int]: + l = 0 + for r in range(len(nums)): + if nums[r] % 2 == 0: + nums[l], nums[r] = nums[r], nums[l] + l += 1 + return nums +``` + +```java +public class Solution { + public int[] sortArrayByParity(int[] nums) { + for (int l = 0, r = 0; r < nums.length; r++) { + if (nums[r] % 2 == 0) { + int temp = nums[l]; + nums[l] = nums[r]; + nums[r] = temp; + l++; + } + } + return nums; + } +} +``` + +```cpp +class Solution { +public: + vector sortArrayByParity(vector& nums) { + for (int l = 0, r = 0; r < nums.size(); r++) { + if (nums[r] % 2 == 0) { + swap(nums[l], nums[r]); + l++; + } + } + return nums; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number[]} + */ + sortArrayByParity(nums) { + for (let l = 0, r = 0; r < nums.length; r++) { + if (nums[r] % 2 == 0) { + [nums[l], nums[r]] = [nums[r], nums[l]]; + l++; + } + } + return nums; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. \ No newline at end of file diff --git a/articles/squares-of-a-sorted-array.md b/articles/squares-of-a-sorted-array.md new file mode 100644 index 000000000..c4d76f6b3 --- /dev/null +++ b/articles/squares-of-a-sorted-array.md @@ -0,0 +1,266 @@ +## 1. Sorting + +::tabs-start + +```python +class Solution: + def sortedSquares(self, nums: List[int]) -> List[int]: + for i in range(len(nums)): + nums[i] *= nums[i] + nums.sort() + return nums +``` + +```java +public class Solution { + public int[] sortedSquares(int[] nums) { + for (int i = 0; i < nums.length; i++) { + nums[i] *= nums[i]; + } + Arrays.sort(nums); + return nums; + } +} +``` + +```cpp +class Solution { +public: + vector sortedSquares(vector& nums) { + for (int i = 0; i < nums.size(); i++) { + nums[i] *= nums[i]; + } + sort(nums.begin(), nums.end()); + return nums; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number[]} + */ + sortedSquares(nums) { + for (let i = 0; i < nums.length; i++) { + nums[i] *= nums[i]; + } + nums.sort((a, b) => a - b); + return nums; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(1)$ or $O(n)$ depending on the sorting algorithm. + +--- + +## 2. Two Pointers - I + +::tabs-start + +```python +class Solution: + def sortedSquares(self, nums: List[int]) -> List[int]: + l, r, res = 0, len(nums) - 1, [] + + while l <= r: + if (nums[l] * nums[l]) > (nums[r] * nums[r]): + res.append(nums[l] * nums[l]) + l += 1 + else: + res.append(nums[r] * nums[r]) + r -= 1 + + return res[::-1] +``` + +```java +public class Solution { + public int[] sortedSquares(int[] nums) { + int l = 0, r = nums.length - 1; + ArrayList res = new ArrayList<>(); + + while (l <= r) { + if (nums[l] * nums[l] > nums[r] * nums[r]) { + res.add(nums[l] * nums[l]); + l++; + } else { + res.add(nums[r] * nums[r]); + r--; + } + } + + Collections.reverse(res); + return res.stream().mapToInt(i -> i).toArray(); + } +} +``` + +```cpp +class Solution { +public: + vector sortedSquares(vector& nums) { + int l = 0, r = nums.size() - 1; + vector res; + + while (l <= r) { + if (nums[l] * nums[l] > nums[r] * nums[r]) { + res.push_back(nums[l] * nums[l]); + l++; + } else { + res.push_back(nums[r] * nums[r]); + r--; + } + } + + reverse(res.begin(), res.end()); + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number[]} + */ + sortedSquares(nums) { + let l = 0, r = nums.length - 1; + const res = []; + + while (l <= r) { + if (nums[l] * nums[l] > nums[r] * nums[r]) { + res.push(nums[l] * nums[l]); + l++; + } else { + res.push(nums[r] * nums[r]); + r--; + } + } + + return res.reverse(); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ for the output array. + +--- + +## 3. Two Pointers - II + +::tabs-start + +```python +class Solution: + def sortedSquares(self, nums: List[int]) -> List[int]: + n = len(nums) + res = [0] * n + l, r = 0, n - 1 + res_index = n - 1 + + while l <= r: + if abs(nums[l]) > abs(nums[r]): + res[res_index] = nums[l] * nums[l] + l += 1 + else: + res[res_index] = nums[r] * nums[r] + r -= 1 + res_index -= 1 + + return res +``` + +```java +public class Solution { + public int[] sortedSquares(int[] nums) { + int n = nums.length; + int[] res = new int[n]; + int l = 0, r = n - 1, resIndex = n - 1; + + while (l <= r) { + if (Math.abs(nums[l]) > Math.abs(nums[r])) { + res[resIndex] = nums[l] * nums[l]; + l++; + } else { + res[resIndex] = nums[r] * nums[r]; + r--; + } + resIndex--; + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector sortedSquares(vector& nums) { + int n = nums.size(); + vector res(n); + int l = 0, r = n - 1, resIndex = n - 1; + + while (l <= r) { + if (abs(nums[l]) > abs(nums[r])) { + res[resIndex] = nums[l] * nums[l]; + l++; + } else { + res[resIndex] = nums[r] * nums[r]; + r--; + } + resIndex--; + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number[]} + */ + sortedSquares(nums) { + const n = nums.length; + const res = new Array(n); + let l = 0, r = n - 1, resIndex = n - 1; + + while (l <= r) { + if (Math.abs(nums[l]) > Math.abs(nums[r])) { + res[resIndex] = nums[l] * nums[l]; + l++; + } else { + res[resIndex] = nums[r] * nums[r]; + r--; + } + resIndex--; + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ for the output array. \ No newline at end of file diff --git a/articles/valid-palindrome-ii.md b/articles/valid-palindrome-ii.md new file mode 100644 index 000000000..05367b687 --- /dev/null +++ b/articles/valid-palindrome-ii.md @@ -0,0 +1,395 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def validPalindrome(self, s: str) -> bool: + if s == s[::-1]: + return True + + for i in range(len(s)): + newS = s[:i] + s[i + 1:] + if newS == newS[::-1]: + return True + + return False +``` + +```java +public class Solution { + public boolean validPalindrome(String s) { + if (isPalindrome(s)) { + return true; + } + + for (int i = 0; i < s.length(); i++) { + String newS = s.substring(0, i) + s.substring(i + 1); + if (isPalindrome(newS)) { + return true; + } + } + + return false; + } + + private boolean isPalindrome(String s) { + int left = 0, right = s.length() - 1; + while (left < right) { + if (s.charAt(left) != s.charAt(right)) { + return false; + } + left++; + right--; + } + return true; + } +} +``` + +```cpp +class Solution { +public: + bool validPalindrome(string s) { + if (isPalindrome(s)) { + return true; + } + + for (int i = 0; i < s.size(); i++) { + string newS = s.substr(0, i) + s.substr(i + 1); + if (isPalindrome(newS)) { + return true; + } + } + + return false; + } + +private: + bool isPalindrome(const string& s) { + int left = 0, right = s.size() - 1; + while (left < right) { + if (s[left] != s[right]) { + return false; + } + left++; + right--; + } + return true; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {boolean} + */ + validPalindrome(s) { + if (this.isPalindrome(s)) { + return true; + } + + for (let i = 0; i < s.length; i++) { + const newS = s.slice(0, i) + s.slice(i + 1); + if (this.isPalindrome(newS)) { + return true; + } + } + + return false; + } + + /** + * @param {string} s + * @return {boolean} + */ + isPalindrome(s) { + let left = 0, right = s.length - 1; + while (left < right) { + if (s[left] !== s[right]) { + return false; + } + left++; + right--; + } + return true; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n)$ + +--- + +## 2. Two Pointers + +::tabs-start + +```python +class Solution: + def validPalindrome(self, s: str) -> bool: + l, r = 0, len(s) - 1 + + while l < r: + if s[l] != s[r]: + skipL = s[l + 1 : r + 1] + skipR = s[l : r] + return skipL == skipL[::-1] or skipR == skipR[::-1] + l, r = l + 1, r - 1 + + return True +``` + +```java +public class Solution { + public boolean validPalindrome(String s) { + int l = 0, r = s.length() - 1; + + while (l < r) { + if (s.charAt(l) != s.charAt(r)) { + return isPalindrome(s.substring(0, l) + s.substring(l + 1)) || + isPalindrome(s.substring(0, r) + s.substring(r + 1)); + } + l++; + r--; + } + + return true; + } + + private boolean isPalindrome(String s) { + int l = 0, r = s.length() - 1; + while (l < r) { + if (s.charAt(l) != s.charAt(r)) { + return false; + } + l++; + r--; + } + return true; + } +} +``` + +```cpp +class Solution { +public: + bool validPalindrome(string s) { + int l = 0, r = s.size() - 1; + + while (l < r) { + if (s[l] != s[r]) { + return isPalindrome(s.substr(0, l) + s.substr(l + 1)) || + isPalindrome(s.substr(0, r) + s.substr(r + 1)); + } + l++; + r--; + } + + return true; + } + +private: + bool isPalindrome(string s) { + int l = 0, r = s.length() - 1; + while (l < r) { + if (s[l] != s[r]) { + return false; + } + l++; + r--; + } + return true; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {boolean} + */ + validPalindrome(s) { + let l = 0, r = s.length - 1; + + while (l < r) { + if (s[l] !== s[r]) { + return this.isPalindrome(s.slice(0, l) + s.slice(l + 1)) || + this.isPalindrome(s.slice(0, r) + s.slice(r + 1)); + } + l++; + r--; + } + + return true; + } + + /** + * @param {string} s + * @return {boolean} + */ + isPalindrome(s) { + let left = 0, right = s.length - 1; + while (left < right) { + if (s[left] !== s[right]) { + return false; + } + left++; + right--; + } + return true; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Two Pointers (Optimal) + +::tabs-start + +```python +class Solution: + def validPalindrome(self, s: str) -> bool: + def is_palindrome(l, r): + while l < r: + if s[l] != s[r]: + return False + l += 1 + r -= 1 + return True + + l, r = 0, len(s) - 1 + while l < r: + if s[l] != s[r]: + return (is_palindrome(l + 1, r) or + is_palindrome(l, r - 1)) + l += 1 + r -= 1 + + return True +``` + +```java +public class Solution { + public boolean validPalindrome(String s) { + int l = 0, r = s.length() - 1; + + while (l < r) { + if (s.charAt(l) != s.charAt(r)) { + return isPalindrome(s, l + 1, r) || + isPalindrome(s, l, r - 1); + } + l++; + r--; + } + + return true; + } + + private boolean isPalindrome(String s, int l, int r) { + while (l < r) { + if (s.charAt(l) != s.charAt(r)) { + return false; + } + l++; + r--; + } + return true; + } +} +``` + +```cpp +class Solution { +public: + bool validPalindrome(string s) { + int l = 0, r = s.size() - 1; + + while (l < r) { + if (s[l] != s[r]) { + return isPalindrome(s, l + 1, r) || + isPalindrome(s, l, r - 1); + } + l++; + r--; + } + + return true; + } + +private: + bool isPalindrome(const string& s, int l, int r) { + while (l < r) { + if (s[l] != s[r]) { + return false; + } + l++; + r--; + } + return true; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {boolean} + */ + validPalindrome(s) { + let l = 0, r = s.length - 1; + + while (l < r) { + if (s[l] !== s[r]) { + return this.isPalindrome(s, l + 1, r) || + this.isPalindrome(s, l, r - 1); + } + l++; + r--; + } + + return true; + } + + /** + * @param {string} s + * @param {number} l + * @param {number} r + * @return {boolean} + */ + isPalindrome(s, l, r) { + while (l < r) { + if (s[l] !== s[r]) { + return false; + } + l++; + r--; + } + return true; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ \ No newline at end of file From e3af24d80718b7524c5f13302ed573455b6af173 Mon Sep 17 00:00:00 2001 From: Sri Hari <94112314+Srihari2222@users.noreply.github.com> Date: Wed, 18 Dec 2024 09:37:29 +0530 Subject: [PATCH 17/45] Sri Hari: Batch-4/Neetcode-All/Added-articles (#3767) * Batch-4/Neetcode-All/Added-articles * Batch-4/Neetcode-All/Added-articles --- articles/buy-two-chocolates.md | 211 ++++ articles/combinations.md | 456 +++++++ articles/count-sub-islands.md | 564 +++++++++ articles/course-schedule-ii.md | 3 +- articles/course-schedule-iv.md | 705 +++++++++++ articles/extra-characters-in-a-string.md | 1062 +++++++++++++++++ articles/find-eventual-safe-states.md | 308 +++++ articles/find-the-town-judge.md | 199 +++ articles/intersection-of-two-linked-lists.md | 554 +++++++++ articles/island-perimeter.md | 516 ++++++++ articles/maximum-odd-binary-number.md | 264 ++++ articles/merge-in-between-linked-lists.md | 460 +++++++ articles/middle-of-the-linked-list.md | 339 ++++++ articles/open-the-lock.md | 501 ++++++++ articles/palindrome-linked-list.md | 600 ++++++++++ articles/permutations-ii.md | 712 +++++++++++ articles/permutations.md | 10 +- .../remove-duplicates-from-sorted-list.md | 312 +++++ articles/remove-linked-list-elements.md | 514 ++++++++ articles/remove-nodes-from-linked-list.md | 574 +++++++++ ...to-make-all-paths-lead-to-the-city-zero.md | 385 ++++++ articles/restore-ip-addresses.md | 278 +++++ articles/snakes-and-ladders.md | 572 +++++++++ articles/sum-of-all-subset-xor-totals.md | 341 ++++++ articles/verifying-an-alien-dictionary.md | 211 ++++ 25 files changed, 10644 insertions(+), 7 deletions(-) create mode 100644 articles/buy-two-chocolates.md create mode 100644 articles/combinations.md create mode 100644 articles/count-sub-islands.md create mode 100644 articles/course-schedule-iv.md create mode 100644 articles/extra-characters-in-a-string.md create mode 100644 articles/find-eventual-safe-states.md create mode 100644 articles/find-the-town-judge.md create mode 100644 articles/intersection-of-two-linked-lists.md create mode 100644 articles/island-perimeter.md create mode 100644 articles/maximum-odd-binary-number.md create mode 100644 articles/merge-in-between-linked-lists.md create mode 100644 articles/middle-of-the-linked-list.md create mode 100644 articles/open-the-lock.md create mode 100644 articles/palindrome-linked-list.md create mode 100644 articles/permutations-ii.md create mode 100644 articles/remove-duplicates-from-sorted-list.md create mode 100644 articles/remove-linked-list-elements.md create mode 100644 articles/remove-nodes-from-linked-list.md create mode 100644 articles/reorder-routes-to-make-all-paths-lead-to-the-city-zero.md create mode 100644 articles/restore-ip-addresses.md create mode 100644 articles/snakes-and-ladders.md create mode 100644 articles/sum-of-all-subset-xor-totals.md create mode 100644 articles/verifying-an-alien-dictionary.md diff --git a/articles/buy-two-chocolates.md b/articles/buy-two-chocolates.md new file mode 100644 index 000000000..0b6edc79d --- /dev/null +++ b/articles/buy-two-chocolates.md @@ -0,0 +1,211 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def buyChoco(self, prices: List[int], money: int) -> int: + res = -1 + for i in range(len(prices)): + for j in range(i + 1, len(prices)): + if prices[i] + prices[j] <= money: + res = max(res, money - prices[i] - prices[j]) + return res if res != -1 else money +``` + +```java +public class Solution { + public int buyChoco(int[] prices, int money) { + int res = -1; + for (int i = 0; i < prices.length; i++) { + for (int j = i + 1; j < prices.length; j++) { + if (prices[i] + prices[j] <= money) { + res = Math.max(res, money - prices[i] - prices[j]); + } + } + } + return res == -1 ? money : res; + } +} +``` + +```cpp +class Solution { +public: + int buyChoco(vector& prices, int money) { + int res = -1; + for (int i = 0; i < prices.size(); i++) { + for (int j = i + 1; j < prices.size(); j++) { + if (prices[i] + prices[j] <= money) { + res = max(res, money - prices[i] - prices[j]); + } + } + } + return res == -1 ? money : res; + } +}; +``` + +```javascript +class Solution { + buyChoco(prices, money) { + let res = -1; + for (let i = 0; i < prices.length; i++) { + for (let j = i + 1; j < prices.length; j++) { + if (prices[i] + prices[j] <= money) { + res = Math.max(res, money - prices[i] - prices[j]); + } + } + } + return res === -1 ? money : res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(1)$ extra space. + +--- + +## 2. Sorting + +::tabs-start + +```python +class Solution: + def buyChoco(self, prices: List[int], money: int) -> int: + prices.sort() + buy = prices[0] + prices[1] + return money if buy > money else money - buy +``` + +```java +public class Solution { + public int buyChoco(int[] prices, int money) { + Arrays.sort(prices); + int buy = prices[0] + prices[1]; + return buy > money ? money : money - buy; + } +} +``` + +```cpp +class Solution { +public: + int buyChoco(vector& prices, int money) { + sort(prices.begin(), prices.end()); + int buy = prices[0] + prices[1]; + return buy > money ? money : money - buy; + } +}; +``` + +```javascript +class Solution { + buyChoco(prices, money) { + prices.sort((a, b) => a - b); + let buy = prices[0] + prices[1]; + return buy > money ? money : money - buy; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(1)$ or $O(n)$ depending on the sorting algorithm. + +--- + +## 3. Greedy + +::tabs-start + +```python +class Solution: + def buyChoco(self, prices: list[int], money: int) -> int: + min1 = min2 = float('inf') + + for p in prices: + if p < min1: + min1, min2 = p, min1 + elif p < min2: + min2 = p + + leftover = money - min1 - min2 + return leftover if leftover >= 0 else money +``` + +```java +public class Solution { + public int buyChoco(int[] prices, int money) { + int min1 = Integer.MAX_VALUE, min2 = Integer.MAX_VALUE; + + for (int p : prices) { + if (p < min1) { + min2 = min1; + min1 = p; + } else if (p < min2) { + min2 = p; + } + } + + int leftover = money - min1 - min2; + return leftover >= 0 ? leftover : money; + } +} +``` + +```cpp +class Solution { +public: + int buyChoco(vector& prices, int money) { + int min1 = INT_MAX, min2 = INT_MAX; + + for (int p : prices) { + if (p < min1) { + min2 = min1; + min1 = p; + } else if (p < min2) { + min2 = p; + } + } + + int leftover = money - min1 - min2; + return leftover >= 0 ? leftover : money; + } +}; +``` + +```javascript +class Solution { + buyChoco(prices, money) { + let min1 = Infinity, min2 = Infinity; + + for (const p of prices) { + if (p < min1) { + min2 = min1; + min1 = p; + } else if (p < min2) { + min2 = p; + } + } + + const leftover = money - min1 - min2; + return leftover >= 0 ? leftover : money; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. \ No newline at end of file diff --git a/articles/combinations.md b/articles/combinations.md new file mode 100644 index 000000000..d5e838baf --- /dev/null +++ b/articles/combinations.md @@ -0,0 +1,456 @@ +## 1. Backtracking - I + +::tabs-start + +```python +class Solution: + def combine(self, n: int, k: int) -> List[List[int]]: + res = [] + + def backtrack(i, comb): + if i > n: + if len(comb) == k: + res.append(comb.copy()) + return + + comb.append(i) + backtrack(i + 1, comb) + comb.pop() + backtrack(i + 1, comb) + + backtrack(1, []) + return res +``` + +```java +public class Solution { + private List> res; + + public List> combine(int n, int k) { + res = new ArrayList<>(); + backtrack(1, n, k, new ArrayList<>()); + return res; + } + + private void backtrack(int i, int n, int k, List comb) { + if (i > n) { + if (comb.size() == k) { + res.add(new ArrayList<>(comb)); + } + return; + } + + comb.add(i); + backtrack(i + 1, n, k, comb); + comb.remove(comb.size() - 1); + backtrack(i + 1, n, k, comb); + } +} +``` + +```cpp +class Solution { + vector> res; +public: + vector> combine(int n, int k) { + vector comb; + backtrack(1, n, k, comb); + return res; + } + +private: + void backtrack(int i, int n, int k, vector& comb) { + if (i > n) { + if (comb.size() == k) { + res.push_back(comb); + } + return; + } + + comb.push_back(i); + backtrack(i + 1, n, k, comb); + comb.pop_back(); + backtrack(i + 1, n, k, comb); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number} k + * @return {number[][]} + */ + combine(n, k) { + const res = []; + + const backtrack = (i, comb) => { + if (i > n) { + if (comb.length === k) { + res.push([...comb]); + } + return; + } + + comb.push(i); + backtrack(i + 1, comb); + comb.pop(); + backtrack(i + 1, comb); + }; + + backtrack(1, []); + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(k * \frac {n!}{(n - k)! * k!})$ +* Space complexity: $O(k * \frac {n!}{(n - k)! * k!})$ for the output array. + +> Where $n$ is the number of elements and $k$ is the number of elements to be picked. + +--- + +## 2. Backtracking - II + +::tabs-start + +```python +class Solution: + def combine(self, n: int, k: int) -> List[List[int]]: + res = [] + + def backtrack(start, comb): + if len(comb) == k: + res.append(comb.copy()) + return + + for i in range(start, n + 1): + comb.append(i) + backtrack(i + 1, comb) + comb.pop() + + backtrack(1, []) + return res +``` + +```java +public class Solution { + private List> res; + + public List> combine(int n, int k) { + res = new ArrayList<>(); + backtrack(1, n, k, new ArrayList<>()); + return res; + } + + private void backtrack(int start, int n, int k, List comb) { + if (comb.size() == k) { + res.add(new ArrayList<>(comb)); + return; + } + + for (int i = start; i <= n; i++) { + comb.add(i); + backtrack(i + 1, n, k, comb); + comb.remove(comb.size() - 1); + } + } +} +``` + +```cpp +class Solution { +public: + vector> res; + + vector> combine(int n, int k) { + res.clear(); + vector comb; + backtrack(1, n, k, comb); + return res; + } + + void backtrack(int start, int n, int k, vector& comb) { + if (comb.size() == k) { + res.push_back(comb); + return; + } + + for (int i = start; i <= n; i++) { + comb.push_back(i); + backtrack(i + 1, n, k, comb); + comb.pop_back(); + } + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number} k + * @return {number[][]} + */ + combine(n, k) { + const res = []; + + const backtrack = (start, comb) => { + if (comb.length === k) { + res.push([...comb]); + return; + } + + for (let i = start; i <= n; i++) { + comb.push(i); + backtrack(i + 1, comb); + comb.pop(); + } + }; + + backtrack(1, []); + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(k * \frac {n!}{(n - k)! * k!})$ +* Space complexity: $O(k * \frac {n!}{(n - k)! * k!})$ for the output array. + +> Where $n$ is the number of elements and $k$ is the number of elements to be picked. + +--- + +## 3. Iteration + +::tabs-start + +```python +class Solution: + def combine(self, n: int, k: int) -> List[List[int]]: + res = [] + i = 0 + comb = [0] * k + + while i >= 0: + comb[i] += 1 + if comb[i] > n: + i -= 1 + continue + + if i == k - 1: + res.append(comb.copy()) + else: + i += 1 + comb[i] = comb[i - 1] + + return res +``` + +```java +public class Solution { + public List> combine(int n, int k) { + List> res = new ArrayList<>(); + int[] comb = new int[k]; + int i = 0; + + while (i >= 0) { + comb[i]++; + if (comb[i] > n) { + i--; + continue; + } + + if (i == k - 1) { + List current = new ArrayList<>(); + for (int num : comb) { + current.add(num); + } + res.add(current); + } else { + i++; + comb[i] = comb[i - 1]; + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector> combine(int n, int k) { + vector> res; + vector comb(k, 0); + int i = 0; + + while (i >= 0) { + comb[i]++; + if (comb[i] > n) { + i--; + continue; + } + + if (i == k - 1) { + res.push_back(comb); + } else { + i++; + comb[i] = comb[i - 1]; + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number} k + * @return {number[][]} + */ + combine(n, k) { + const res = []; + const comb = Array(k).fill(0); + let i = 0; + + while (i >= 0) { + comb[i]++; + if (comb[i] > n) { + i--; + continue; + } + + if (i === k - 1) { + res.push([...comb]); + } else { + i++; + comb[i] = comb[i - 1]; + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(k * \frac {n!}{(n - k)! * k!})$ +* Space complexity: $O(k * \frac {n!}{(n - k)! * k!})$ for the output array. + +> Where $n$ is the number of elements and $k$ is the number of elements to be picked. + +--- + +## 4. Bit Manipulation + +::tabs-start + +```python +class Solution: + def combine(self, n: int, k: int) -> List[List[int]]: + res = [] + for mask in range(1 << n): + comb = [] + for bit in range(n): + if mask & (1 << bit): + comb.append(bit + 1) + + if len(comb) == k: + res.append(comb) + return res +``` + +```java +public class Solution { + public List> combine(int n, int k) { + List> res = new ArrayList<>(); + for (int mask = 0; mask < (1 << n); mask++) { + List comb = new ArrayList<>(); + for (int bit = 0; bit < n; bit++) { + if ((mask & (1 << bit)) != 0) { + comb.add(bit + 1); + } + } + if (comb.size() == k) { + res.add(comb); + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + vector> combine(int n, int k) { + vector> res; + for (int mask = 0; mask < (1 << n); ++mask) { + vector comb; + for (int bit = 0; bit < n; ++bit) { + if (mask & (1 << bit)) { + comb.push_back(bit + 1); + } + } + if (comb.size() == k) { + res.push_back(comb); + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number} k + * @return {number[][]} + */ + combine(n, k) { + const res = []; + for (let mask = 0; mask < (1 << n); mask++) { + if (mask.toString(2).split("1").length - 1 !== k) { + continue; + } + + const comb = []; + for (let bit = 0; bit < n; bit++) { + if (mask & (1 << bit)) { + comb.push(bit + 1); + } + } + res.push(comb); + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * 2 ^ n)$ +* Space complexity: $O(k * \frac {n!}{(n - k)! * k!})$ for the output array. + +> Where $n$ is the number of elements and $k$ is the number of elements to be picked. \ No newline at end of file diff --git a/articles/count-sub-islands.md b/articles/count-sub-islands.md new file mode 100644 index 000000000..22a7598f7 --- /dev/null +++ b/articles/count-sub-islands.md @@ -0,0 +1,564 @@ +## 1. Depth First Search + +::tabs-start + +```python +class Solution: + def countSubIslands(self, grid1: List[List[int]], grid2: List[List[int]]) -> int: + ROWS, COLS = len(grid1), len(grid1[0]) + visit = set() + + def dfs(r, c): + if (min(r, c) < 0 or r == ROWS or c == COLS or + grid2[r][c] == 0 or (r, c) in visit): + return True + + visit.add((r, c)) + res = grid1[r][c] + + res &= dfs(r - 1, c) + res &= dfs(r + 1, c) + res &= dfs(r, c - 1) + res &= dfs(r, c + 1) + return res + + count = 0 + for r in range(ROWS): + for c in range(COLS): + if grid2[r][c] and (r, c) not in visit: + count += dfs(r, c) + return count +``` + +```java +public class Solution { + private boolean[][] visit; + + public int countSubIslands(int[][] grid1, int[][] grid2) { + int ROWS = grid1.length, COLS = grid1[0].length; + visit = new boolean[ROWS][COLS]; + int count = 0; + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + if (grid2[r][c] == 1 && !visit[r][c] && dfs(r, c, grid1, grid2)) { + count++; + } + } + } + return count; + } + + private boolean dfs(int r, int c, int[][] grid1, int[][] grid2) { + if (r < 0 || c < 0 || r >= grid1.length || c >= grid1[0].length || + grid2[r][c] == 0 || visit[r][c]) { + return true; + } + visit[r][c] = true; + boolean res = grid1[r][c] == 1; + res &= dfs(r - 1, c, grid1, grid2); + res &= dfs(r + 1, c, grid1, grid2); + res &= dfs(r, c - 1, grid1, grid2); + res &= dfs(r, c + 1, grid1, grid2); + return res; + } + +} +``` + +```cpp +class Solution { + vector> visit; + +public: + int countSubIslands(vector>& grid1, vector>& grid2) { + int ROWS = grid1.size(), COLS = grid1[0].size(); + visit.assign(ROWS, vector(COLS, false)); + + int count = 0; + for (int r = 0; r < ROWS; ++r) { + for (int c = 0; c < COLS; ++c) { + if (grid2[r][c] && !visit[r][c]) { + count += dfs(r, c, grid1, grid2); + } + } + } + return count; + } + +private: + bool dfs(int r, int c, vector>& grid1, vector>& grid2) { + if (r < 0 || c < 0 || r >= grid1.size() || c >= grid1[0].size() || + grid2[r][c] == 0 || visit[r][c]) { + return true; + } + + visit[r][c] = true; + bool res = grid1[r][c] == 1; + res &= dfs(r - 1, c, grid1, grid2); + res &= dfs(r + 1, c, grid1, grid2); + res &= dfs(r, c - 1, grid1, grid2); + res &= dfs(r, c + 1, grid1, grid2); + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} grid1 + * @param {number[][]} grid2 + * @return {number} + */ + countSubIslands(grid1, grid2) { + const ROWS = grid1.length, COLS = grid1[0].length; + const visit = Array.from({ length: ROWS }, () => Array(COLS).fill(false)); + + const dfs = (r, c) => { + if (r < 0 || c < 0 || r >= ROWS || c >= COLS || grid2[r][c] === 0 || visit[r][c]) + return true; + visit[r][c] = true; + let res = grid1[r][c] === 1; + res &= dfs(r - 1, c); + res &= dfs(r + 1, c); + res &= dfs(r, c - 1); + res &= dfs(r, c + 1); + return res; + }; + + let count = 0; + for (let r = 0; r < ROWS; r++) { + for (let c = 0; c < COLS; c++) { + if (grid2[r][c] === 1 && !visit[r][c]) { + if (dfs(r, c)) count++; + } + } + } + return count; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n)$ +* Space complexity: $O(m * n)$ + +> Where $m$ is the number of rows and $n$ is the number of columns. + +--- + +## 2. Breadth First Search + +::tabs-start + +```python +class Solution: + def countSubIslands(self, grid1: List[List[int]], grid2: List[List[int]]) -> int: + ROWS, COLS = len(grid1), len(grid1[0]) + visit = [[False] * COLS for _ in range(ROWS)] + directions = [1, 0, -1, 0, 1] + + def bfs(r, c): + queue = deque([(r, c)]) + visit[r][c] = True + res = True + + while queue: + r, c = queue.popleft() + if grid1[r][c] == 0: + res = False + + for i in range(4): + nr, nc = r + directions[i], c + directions[i + 1] + if 0 <= nr < ROWS and 0 <= nc < COLS and not visit[nr][nc] and grid2[nr][nc]: + visit[nr][nc] = True + queue.append((nr, nc)) + return res + + count = 0 + for r in range(ROWS): + for c in range(COLS): + if grid2[r][c] == 1 and not visit[r][c]: + if bfs(r, c): + count += 1 + return count +``` + +```java +public class Solution { + private boolean[][] visit; + private int[] directions = {1, 0, -1, 0, 1}; + + public int countSubIslands(int[][] grid1, int[][] grid2) { + int ROWS = grid1.length, COLS = grid1[0].length; + visit = new boolean[ROWS][COLS]; + int count = 0; + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + if (grid2[r][c] == 1 && !visit[r][c]) { + if (bfs(r, c, grid1, grid2)) { + count++; + } + } + } + } + return count; + } + + private boolean bfs(int r, int c, int[][] grid1, int[][] grid2) { + Queue queue = new LinkedList<>(); + queue.add(new int[]{r, c}); + visit[r][c] = true; + boolean res = true; + + while (!queue.isEmpty()) { + int[] cell = queue.poll(); + int cr = cell[0], cc = cell[1]; + if (grid1[cr][cc] == 0) { + res = false; + } + + for (int i = 0; i < 4; i++) { + int nr = cr + directions[i], nc = cc + directions[i + 1]; + if (nr >= 0 && nr < grid1.length && nc >= 0 && nc < grid1[0].length && + grid2[nr][nc] == 1 && !visit[nr][nc]) { + visit[nr][nc] = true; + queue.add(new int[]{nr, nc}); + } + } + } + return res; + } +} +``` + +```cpp +class Solution { + vector> visit; + vector directions = {1, 0, -1, 0, 1}; + +public: + int countSubIslands(vector>& grid1, vector>& grid2) { + int ROWS = grid1.size(), COLS = grid1[0].size(); + visit.assign(ROWS, vector(COLS, false)); + int count = 0; + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + if (grid2[r][c] == 1 && !visit[r][c]) { + if (bfs(r, c, grid1, grid2)) { + count++; + } + } + } + } + return count; + } + +private: + bool bfs(int r, int c, vector>& grid1, vector>& grid2) { + queue> q; + q.push({r, c}); + visit[r][c] = true; + bool res = true; + + while (!q.empty()) { + auto [cr, cc] = q.front(); q.pop(); + + if (grid1[cr][cc] == 0) res = false; + + for (int i = 0; i < 4; i++) { + int nr = cr + directions[i], nc = cc + directions[i + 1]; + if (nr >= 0 && nr < grid1.size() && nc >= 0 && nc < grid1[0].size() && + grid2[nr][nc] == 1 && !visit[nr][nc]) { + visit[nr][nc] = true; + q.push({nr, nc}); + } + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} grid1 + * @param {number[][]} grid2 + * @return {number} + */ + countSubIslands(grid1, grid2) { + const ROWS = grid1.length, COLS = grid1[0].length; + const visit = Array.from({ length: ROWS }, () => Array(COLS).fill(false)); + const directions = [1, 0, -1, 0, 1]; + let count = 0; + + const bfs = (sr, sc) => { + const queue = new Queue([[sr, sc]]); + visit[sr][sc] = true; + let res = true; + + while (!queue.isEmpty()) { + const [r, c] = queue.pop(); + if (grid1[r][c] === 0) res = false; + + for (let i = 0; i < 4; i++) { + const nr = r + directions[i], nc = c + directions[i + 1]; + if (nr >= 0 && nr < ROWS && nc >= 0 && nc < COLS && + grid2[nr][nc] === 1 && !visit[nr][nc]) { + visit[nr][nc] = true; + queue.push([nr, nc]); + } + } + } + return res; + }; + + for (let r = 0; r < ROWS; r++) { + for (let c = 0; c < COLS; c++) { + if (grid2[r][c] === 1 && !visit[r][c]) { + if (bfs(r, c)) count++; + } + } + } + return count; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n)$ +* Space complexity: $O(m * n)$ + +> Where $m$ is the number of rows and $n$ is the number of columns. + +--- + +## 3. Disjoint Set Union + +::tabs-start + +```python +class DSU: + def __init__(self, n): + self.Parent = list(range(n + 1)) + self.Size = [1] * (n + 1) + + def find(self, node): + if self.Parent[node] != node: + self.Parent[node] = self.find(self.Parent[node]) + return self.Parent[node] + + def union(self, u, v): + pu = self.find(u) + pv = self.find(v) + if pu == pv: + return False + if self.Size[pu] < self.Size[pv]: + pu, pv = pv, pu + self.Size[pu] += self.Size[pv] + self.Parent[pv] = pu + return True + +class Solution: + def countSubIslands(self, grid1, grid2): + ROWS, COLS = len(grid1), len(grid1[0]) + N = ROWS * COLS + dsu = DSU(N) + + def getId(r, c): + return r * COLS + c + + land = unions = 0 + for r in range(ROWS): + for c in range(COLS): + if not grid2[r][c]: + continue + land += 1 + if r + 1 < ROWS and grid2[r + 1][c]: + unions += dsu.union(getId(r, c), getId(r + 1, c)) + if c + 1 < COLS and grid2[r][c + 1]: + unions += dsu.union(getId(r, c), getId(r, c + 1)) + if not grid1[r][c]: + unions += dsu.union(getId(r, c), N) + + return land - unions +``` + +```java +class DSU { + int[] Parent, Size; + + DSU(int n) { + Parent = new int[n + 1]; + Size = new int[n + 1]; + for (int i = 0; i <= n; i++) Parent[i] = i; + } + + int find(int node) { + if (Parent[node] != node) Parent[node] = find(Parent[node]); + return Parent[node]; + } + + int union(int u, int v) { + int pu = find(u), pv = find(v); + if (pu == pv) return 0; + if (Size[pu] < Size[pv]) { + int temp = pu; pu = pv; pv = temp; + } + Size[pu] += Size[pv]; + Parent[pv] = pu; + return 1; + } +} + +public class Solution { + public int countSubIslands(int[][] grid1, int[][] grid2) { + int ROWS = grid1.length, COLS = grid1[0].length, N = ROWS * COLS; + DSU dsu = new DSU(N); + + int land = 0, unions = 0; + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + if (grid2[r][c] == 0) continue; + land++; + if (r + 1 < ROWS && grid2[r + 1][c] == 1) + unions += dsu.union(r * COLS + c, (r + 1) * COLS + c); + if (c + 1 < COLS && grid2[r][c + 1] == 1) + unions += dsu.union(r * COLS + c, r * COLS + c + 1); + if (grid1[r][c] == 0) + unions += dsu.union(r * COLS + c, N); + } + } + return land - unions; + } +} +``` + +```cpp +class DSU { + vector Parent, Size; + +public: + DSU(int n) { + Parent.resize(n + 1); + Size.assign(n + 1, 1); + for (int i = 0; i <= n; i++) Parent[i] = i; + } + + int find(int node) { + if (Parent[node] != node) Parent[node] = find(Parent[node]); + return Parent[node]; + } + + bool unionSets(int u, int v) { + int pu = find(u), pv = find(v); + if (pu == pv) return false; + if (Size[pu] < Size[pv]) swap(pu, pv); + Size[pu] += Size[pv]; + Parent[pv] = pu; + return true; + } +}; + +class Solution { +public: + int countSubIslands(vector>& grid1, vector>& grid2) { + int ROWS = grid1.size(), COLS = grid1[0].size(), N = ROWS * COLS; + DSU dsu(N); + + int land = 0, unions = 0; + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + if (!grid2[r][c]) continue; + land++; + if (r + 1 < ROWS && grid2[r + 1][c]) + unions += dsu.unionSets(r * COLS + c, (r + 1) * COLS + c); + if (c + 1 < COLS && grid2[r][c + 1]) + unions += dsu.unionSets(r * COLS + c, r * COLS + c + 1); + if (!grid1[r][c]) + unions += dsu.unionSets(r * COLS + c, N); + } + } + return land - unions; + } +}; +``` + +```javascript +class DSU { + constructor(n) { + this.Parent = Array.from({ length: n + 1 }, (_, i) => i); + this.Size = Array(n + 1).fill(1); + } + + /** + * @param {number} node + * @return {number} + */ + find(node) { + if (this.Parent[node] !== node) { + this.Parent[node] = this.find(this.Parent[node]); + } + return this.Parent[node]; + } + + /** + * @param {number} u + * @param {number} v + * @return {boolean} + */ + union(u, v) { + let pu = this.find(u), pv = this.find(v); + if (pu === pv) return false; + if (this.Size[pu] < this.Size[pv]) [pu, pv] = [pv, pu]; + this.Size[pu] += this.Size[pv]; + this.Parent[pv] = pu; + return true; + } +} + +class Solution { + /** + * @param {number[][]} grid1 + * @param {number[][]} grid2 + * @return {number} + */ + countSubIslands(grid1, grid2) { + const ROWS = grid1.length, COLS = grid1[0].length, N = ROWS * COLS; + const dsu = new DSU(N); + + const getId = (r, c) => r * COLS + c; + + let land = 0, unions = 0; + for (let r = 0; r < ROWS; r++) { + for (let c = 0; c < COLS; c++) { + if (grid2[r][c] === 0) continue; + land++; + if (r + 1 < ROWS && grid2[r + 1][c] === 1) + unions += dsu.union(getId(r, c), getId(r + 1, c)); + if (c + 1 < COLS && grid2[r][c + 1] === 1) + unions += dsu.union(getId(r, c), getId(r, c + 1)); + if (grid1[r][c] === 0) + unions += dsu.union(getId(r, c), N); + } + } + return land - unions; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n)$ +* Space complexity: $O(m * n)$ + +> Where $m$ is the number of rows and $n$ is the number of columns. \ No newline at end of file diff --git a/articles/course-schedule-ii.md b/articles/course-schedule-ii.md index 2773ec9ae..7980e9e5e 100644 --- a/articles/course-schedule-ii.md +++ b/articles/course-schedule-ii.md @@ -71,8 +71,7 @@ public class Solution { } cycle.add(course); - for (int pre : prereq.getOrDefault(course, - Collections.emptyList())) { + for (int pre : prereq.getOrDefault(course, Collections.emptyList())) { if (!dfs(pre, prereq, visit, cycle, output)) { return false; } diff --git a/articles/course-schedule-iv.md b/articles/course-schedule-iv.md new file mode 100644 index 000000000..0768d9038 --- /dev/null +++ b/articles/course-schedule-iv.md @@ -0,0 +1,705 @@ +## 1. Brute Force (DFS) + +::tabs-start + +```python +class Solution: + def checkIfPrerequisite(self, numCourses: int, prerequisites: List[List[int]], queries: List[List[int]]) -> List[bool]: + adj = [[] for _ in range(numCourses)] + for u, v in prerequisites: + adj[u].append(v) + + def dfs(node, target): + if node == target: + return True + for nei in adj[node]: + if dfs(nei, target): + return True + return False + + res = [] + for u, v in queries: + res.append(dfs(u, v)) + return res +``` + +```java +public class Solution { + private List[] adj; + + public List checkIfPrerequisite(int numCourses, int[][] prerequisites, int[][] queries) { + adj = new ArrayList[numCourses]; + for (int i = 0; i < numCourses; i++) adj[i] = new ArrayList<>(); + for (int[] pre : prerequisites) adj[pre[0]].add(pre[1]); + + List res = new ArrayList<>(); + for (int[] query : queries) { + res.add(dfs(query[0], query[1])); + } + return res; + } + + private boolean dfs(int node, int target) { + if (node == target) return true; + for (int nei : adj[node]) { + if (dfs(nei, target)) return true; + } + return false; + } +} +``` + +```cpp +class Solution { + vector> adj; + +public: + vector checkIfPrerequisite(int numCourses, vector>& prerequisites, vector>& queries) { + adj.assign(numCourses, vector()); + for (auto& pre : prerequisites) { + adj[pre[0]].push_back(pre[1]); + } + + vector res; + for (auto& query : queries) { + res.push_back(dfs(query[0], query[1])); + } + return res; + } + +private: + bool dfs(int node, int target) { + if (node == target) return true; + for (int nei : adj[node]) { + if (dfs(nei, target)) return true; + } + return false; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} numCourses + * @param {number[][]} prerequisites + * @param {number[][]} queries + * @return {boolean[]} + */ + checkIfPrerequisite(numCourses, prerequisites, queries) { + const adj = Array.from({ length: numCourses }, () => []); + for (const [u, v] of prerequisites) { + adj[u].push(v); + } + + const dfs = (node, target) => { + if (node === target) return true; + for (const nei of adj[node]) { + if (dfs(nei, target)) return true; + } + return false; + }; + + const res = []; + for (const [u, v] of queries) { + res.push(dfs(u, v)); + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O((V + E) * m)$ +* Space complexity: $O(V + E + m)$ + +> Where $m$ is the number of queries, $V$ is the number of courses, and $E$ is the number of prerequisites. + +--- + +## 2. Depth First Search (Hash Set) + +::tabs-start + +```python +class Solution: + def checkIfPrerequisite(self, numCourses: int, prerequisites: List[List[int]], queries: List[List[int]]) -> List[bool]: + adj = defaultdict(list) + for prereq, crs in prerequisites: + adj[crs].append(prereq) + + def dfs(crs): + if crs not in prereqMap: + prereqMap[crs] = set() + for prereq in adj[crs]: + prereqMap[crs] |= dfs(prereq) + prereqMap[crs].add(crs) + return prereqMap[crs] + + prereqMap = {} + for crs in range(numCourses): + dfs(crs) + + res = [] + for u, v in queries: + res.append(u in prereqMap[v]) + return res +``` + +```java +public class Solution { + private List[] adj; + private Map> prereqMap; + + public List checkIfPrerequisite(int numCourses, int[][] prerequisites, int[][] queries) { + adj = new ArrayList[numCourses]; + prereqMap = new HashMap<>(); + for (int i = 0; i < numCourses; i++) adj[i] = new ArrayList<>(); + for (int[] pre : prerequisites) adj[pre[1]].add(pre[0]); + + for (int crs = 0; crs < numCourses; crs++) dfs(crs); + + List res = new ArrayList<>(); + for (int[] query : queries) { + res.add(prereqMap.get(query[1]).contains(query[0])); + } + return res; + } + + private Set dfs(int crs) { + if (prereqMap.containsKey(crs)) return prereqMap.get(crs); + Set prereqs = new HashSet<>(); + for (int pre : adj[crs]) { + prereqs.addAll(dfs(pre)); + } + prereqs.add(crs); + prereqMap.put(crs, prereqs); + return prereqs; + } +} +``` + +```cpp + +class Solution { + vector> adj; + unordered_map> prereqMap; + +public: + vector checkIfPrerequisite(int numCourses, vector>& prerequisites, vector>& queries) { + adj.assign(numCourses, vector()); + for (auto& pre : prerequisites) { + adj[pre[1]].push_back(pre[0]); + } + for (int crs = 0; crs < numCourses; crs++) { + dfs(crs); + } + + vector res; + for (auto& query : queries) { + res.push_back(prereqMap[query[1]].count(query[0])); + } + return res; + } + +private: + unordered_set& dfs(int crs) { + if (prereqMap.count(crs)) { + return prereqMap[crs]; + } + prereqMap[crs] = unordered_set(); + for (int pre : adj[crs]) { + auto& cur = dfs(pre); + prereqMap[crs].insert(cur.begin(), cur.end()); + } + prereqMap[crs].insert(crs); + return prereqMap[crs]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} numCourses + * @param {number[][]} prerequisites + * @param {number[][]} queries + * @return {boolean[]} + */ + checkIfPrerequisite(numCourses, prerequisites, queries) { + const adj = Array.from({ length: numCourses }, () => []); + const prereqMap = new Map(); + + for (const [pre, crs] of prerequisites) { + adj[crs].push(pre); + } + + + const dfs = (crs) => { + if (prereqMap.has(crs)) { + return prereqMap.get(crs); + } + const prereqs = new Set(); + for (const pre of adj[crs]) { + for (const p of dfs(pre)) prereqs.add(p); + } + prereqs.add(crs); + prereqMap.set(crs, prereqs); + return prereqs; + }; + + for (let crs = 0; crs < numCourses; crs++) { + dfs(crs); + } + return queries.map(([u, v]) => prereqMap.get(v).has(u)); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(V * (V + E) + m)$ +* Space complexity: $O(V ^ 2 + E + m)$ + +> Where $m$ is the number of queries, $V$ is the number of courses, and $E$ is the number of prerequisites. + +--- + +## 3. Depth First Search (Memoization) + +::tabs-start + +```python +class Solution: + def checkIfPrerequisite(self, numCourses: int, prerequisites: List[List[int]], queries: List[List[int]]) -> List[bool]: + adj = [[] for _ in range(numCourses)] + isPrereq = [[-1] * numCourses for _ in range(numCourses)] + for prereq, crs in prerequisites: + adj[crs].append(prereq) + isPrereq[crs][prereq] = True + + def dfs(crs, prereq): + if isPrereq[crs][prereq] != -1: + return isPrereq[crs][prereq] == 1 + + for pre in adj[crs]: + if pre == prereq or dfs(pre, prereq): + isPrereq[crs][prereq] = 1 + return True + + isPrereq[crs][prereq] = 0 + return False + + res = [] + for u, v in queries: + res.append(dfs(v, u)) + return res +``` + +```java +public class Solution { + private List[] adj; + private int[][] isPrereq; + + public List checkIfPrerequisite(int numCourses, int[][] prerequisites, int[][] queries) { + adj = new ArrayList[numCourses]; + isPrereq = new int[numCourses][numCourses]; + for (int i = 0; i < numCourses; i++) { + adj[i] = new ArrayList<>(); + Arrays.fill(isPrereq[i], -1); + } + + for (int[] pre : prerequisites) { + adj[pre[1]].add(pre[0]); + isPrereq[pre[1]][pre[0]] = 1; + } + + List res = new ArrayList<>(); + for (int[] query : queries) { + res.add(dfs(query[1], query[0])); + } + return res; + } + + private boolean dfs(int crs, int prereq) { + if (isPrereq[crs][prereq] != -1) { + return isPrereq[crs][prereq] == 1; + } + for (int pre : adj[crs]) { + if (pre == prereq || dfs(pre, prereq)) { + isPrereq[crs][prereq] = 1; + return true; + } + } + isPrereq[crs][prereq] = 0; + return false; + } +} +``` + +```cpp +class Solution { + vector> adj; + vector> isPrereq; + +public: + vector checkIfPrerequisite(int numCourses, vector>& prerequisites, vector>& queries) { + adj.assign(numCourses, vector()); + isPrereq.assign(numCourses, vector(numCourses, -1)); + + for (auto& pre : prerequisites) { + adj[pre[1]].push_back(pre[0]); + isPrereq[pre[1]][pre[0]] = 1; + } + + vector res; + for (auto& query : queries) { + res.push_back(dfs(query[1], query[0])); + } + return res; + } + +private: + bool dfs(int crs, int prereq) { + if (isPrereq[crs][prereq] != -1) { + return isPrereq[crs][prereq] == 1; + } + for (int pre : adj[crs]) { + if (pre == prereq || dfs(pre, prereq)) { + isPrereq[crs][prereq] = 1; + return true; + } + } + isPrereq[crs][prereq] = 0; + return false; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} numCourses + * @param {number[][]} prerequisites + * @param {number[][]} queries + * @return {boolean[]} + */ + checkIfPrerequisite(numCourses, prerequisites, queries) { + const adj = Array.from({ length: numCourses }, () => []); + const isPrereq = Array.from({ length: numCourses }, () => Array(numCourses).fill(-1)); + for (const [prereq, crs] of prerequisites) { + adj[crs].push(prereq); + isPrereq[crs][prereq] = 1; + } + + const dfs = (crs, prereq) => { + if (isPrereq[crs][prereq] !== -1) { + return isPrereq[crs][prereq] === 1; + } + for (const pre of adj[crs]) { + if (pre === prereq || dfs(pre, prereq)) { + isPrereq[crs][prereq] = 1; + return true; + } + } + isPrereq[crs][prereq] = 0; + return false; + }; + + return queries.map(([u, v]) => dfs(v, u)); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(V * (V + E) + m)$ +* Space complexity: $O(V ^ 2 + E + m)$ + +> Where $m$ is the number of queries, $V$ is the number of courses, and $E$ is the number of prerequisites. + +--- + +## 4. Topological Sort (Kahn's Algorithm) + +::tabs-start + +```python +class Solution: + def checkIfPrerequisite(self, numCourses: int, prerequisites: List[List[int]], queries: List[List[int]]) -> List[bool]: + adj = [set() for _ in range(numCourses)] + indegree = [0] * numCourses + isPrereq = [set() for _ in range(numCourses)] + + for pre, crs in prerequisites: + adj[pre].add(crs) + indegree[crs] += 1 + + q = deque([i for i in range(numCourses) if indegree[i] == 0]) + + while q: + node = q.popleft() + for neighbor in adj[node]: + isPrereq[neighbor].add(node) + isPrereq[neighbor].update(isPrereq[node]) + indegree[neighbor] -= 1 + if indegree[neighbor] == 0: + q.append(neighbor) + + return [u in isPrereq[v] for u, v in queries] +``` + +```java +public class Solution { + public List checkIfPrerequisite(int numCourses, int[][] prerequisites, int[][] queries) { + List> adj = new ArrayList<>(); + List> isPrereq = new ArrayList<>(); + int[] indegree = new int[numCourses]; + for (int i = 0; i < numCourses; i++) { + adj.add(new HashSet<>()); + isPrereq.add(new HashSet<>()); + } + + for (int[] pre : prerequisites) { + adj.get(pre[0]).add(pre[1]); + indegree[pre[1]]++; + } + + Queue q = new LinkedList<>(); + for (int i = 0; i < numCourses; i++) { + if (indegree[i] == 0) q.offer(i); + } + + while (!q.isEmpty()) { + int node = q.poll(); + for (int neighbor : adj.get(node)) { + isPrereq.get(neighbor).add(node); + isPrereq.get(neighbor).addAll(isPrereq.get(node)); + indegree[neighbor]--; + if (indegree[neighbor] == 0) q.offer(neighbor); + } + } + + List res = new ArrayList<>(); + for (int[] query : queries) { + res.add(isPrereq.get(query[1]).contains(query[0])); + } + return res; + } +} +``` + +```cpp +class Solution { +public: + vector checkIfPrerequisite(int numCourses, vector>& prerequisites, vector>& queries) { + vector> adj(numCourses), isPrereq(numCourses); + vector indegree(numCourses, 0); + + for (auto& pre : prerequisites) { + adj[pre[0]].insert(pre[1]); + indegree[pre[1]]++; + } + + queue q; + for (int i = 0; i < numCourses; i++) { + if (indegree[i] == 0) q.push(i); + } + + while (!q.empty()) { + int node = q.front(); q.pop(); + for (int neighbor : adj[node]) { + isPrereq[neighbor].insert(node); + isPrereq[neighbor].insert(isPrereq[node].begin(), isPrereq[node].end()); + indegree[neighbor]--; + if (indegree[neighbor] == 0) q.push(neighbor); + } + } + + vector res; + for (auto& query : queries) { + res.push_back(isPrereq[query[1]].count(query[0])); + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} numCourses + * @param {number[][]} prerequisites + * @param {number[][]} queries + * @return {boolean[]} + */ + checkIfPrerequisite(numCourses, prerequisites, queries) { + const adj = Array.from({ length: numCourses }, () => new Set()); + const isPrereq = Array.from({ length: numCourses }, () => new Set()); + const indegree = Array(numCourses).fill(0); + + for (const [pre, crs] of prerequisites) { + adj[pre].add(crs); + indegree[crs]++; + } + + const q = new Queue(); + for (let i = 0; i < numCourses; i++) { + if (indegree[i] === 0) q.push(i); + } + + while (!q.isEmpty()) { + const node = q.pop(); + for (const neighbor of adj[node]) { + isPrereq[neighbor].add(node); + for (const it of isPrereq[node]) { + isPrereq[neighbor].add(it); + } + indegree[neighbor]--; + if (indegree[neighbor] === 0) q.push(neighbor); + } + } + + return queries.map(([u, v]) => isPrereq[v].has(u)); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(V * (V + E) + m)$ +* Space complexity: $O(V ^ 2 + E + m)$ + +> Where $m$ is the number of queries, $V$ is the number of courses, and $E$ is the number of prerequisites. + +--- + +## 5. Floyd Warshall Algorithm + +::tabs-start + +```python +class Solution: + def checkIfPrerequisite(self, numCourses: int, prerequisites: List[List[int]], queries: List[List[int]]) -> List[bool]: + res = [] + adj = [[False] * numCourses for _ in range(numCourses)] + + for pre, crs in prerequisites: + adj[pre][crs] = True + + for k in range(numCourses): + for i in range(numCourses): + for j in range(numCourses): + adj[i][j] = adj[i][j] or (adj[i][k] and adj[k][j]) + + for u, v in queries: + res.append(adj[u][v]) + + return res +``` + +```java +public class Solution { + public List checkIfPrerequisite(int numCourses, int[][] prerequisites, int[][] queries) { + boolean[][] adj = new boolean[numCourses][numCourses]; + List res = new ArrayList<>(); + + for (int[] pre : prerequisites) { + adj[pre[0]][pre[1]] = true; + } + + for (int k = 0; k < numCourses; k++) { + for (int i = 0; i < numCourses; i++) { + for (int j = 0; j < numCourses; j++) { + adj[i][j] = adj[i][j] || (adj[i][k] && adj[k][j]); + } + } + } + + for (int[] q : queries) { + res.add(adj[q[0]][q[1]]); + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector checkIfPrerequisite(int numCourses, vector>& prerequisites, vector>& queries) { + vector> adj(numCourses, vector(numCourses, false)); + vector res; + + for (auto& pre : prerequisites) { + adj[pre[0]][pre[1]] = true; + } + + for (int k = 0; k < numCourses; k++) { + for (int i = 0; i < numCourses; i++) { + for (int j = 0; j < numCourses; j++) { + adj[i][j] = adj[i][j] || (adj[i][k] && adj[k][j]); + } + } + } + + for (auto& q : queries) { + res.push_back(adj[q[0]][q[1]]); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} numCourses + * @param {number[][]} prerequisites + * @param {number[][]} queries + * @return {boolean[]} + */ + checkIfPrerequisite(numCourses, prerequisites, queries) { + let adj = Array.from({ length: numCourses }, () => Array(numCourses).fill(false)); + let res = []; + + for (let [pre, crs] of prerequisites) { + adj[pre][crs] = true; + } + + for (let k = 0; k < numCourses; k++) { + for (let i = 0; i < numCourses; i++) { + for (let j = 0; j < numCourses; j++) { + adj[i][j] = adj[i][j] || (adj[i][k] && adj[k][j]); + } + } + } + + for (let [u, v] of queries) { + res.push(adj[u][v]); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(V ^ 3 + E + m)$ +* Space complexity: $O(V ^ 2 + E + m)$ + +> Where $m$ is the number of queries, $V$ is the number of courses, and $E$ is the number of prerequisites. \ No newline at end of file diff --git a/articles/extra-characters-in-a-string.md b/articles/extra-characters-in-a-string.md new file mode 100644 index 000000000..b41af1f45 --- /dev/null +++ b/articles/extra-characters-in-a-string.md @@ -0,0 +1,1062 @@ +## 1. Recursion + +::tabs-start + +```python +class Solution: + def minExtraChar(self, s: str, dictionary: List[str]) -> int: + words = set(dictionary) + + def dfs(i): + if i == len(s): + return 0 + + res = 1 + dfs(i + 1) + for j in range(i, len(s)): + if s[i:j + 1] in words: + res = min(res, dfs(j + 1)) + + return res + + return dfs(0) +``` + +```java +public class Solution { + public int minExtraChar(String s, String[] dictionary) { + Set words = new HashSet<>(); + for (String word : dictionary) { + words.add(word); + } + return dfs(0, s, words); + } + + private int dfs(int i, String s, Set words) { + if (i == s.length()) { + return 0; + } + + int res = 1 + dfs(i + 1, s, words); + for (int j = i; j < s.length(); j++) { + if (words.contains(s.substring(i, j + 1))) { + res = Math.min(res, dfs(j + 1, s, words)); + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int minExtraChar(string s, vector& dictionary) { + unordered_set words(dictionary.begin(), dictionary.end()); + return dfs(0, s, words); + } + +private: + int dfs(int i, const string& s, unordered_set& words) { + if (i == s.size()) { + return 0; + } + + int res = 1 + dfs(i + 1, s, words); + for (int j = i; j < s.size(); j++) { + if (words.count(s.substr(i, j - i + 1))) { + res = min(res, dfs(j + 1, s, words)); + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @param {string[]} dictionary + * @return {number} + */ + minExtraChar(s, dictionary) { + const words = new Set(dictionary); + + const dfs = (i) => { + if (i === s.length) { + return 0; + } + + let res = 1 + dfs(i + 1); + for (let j = i; j < s.length; j++) { + if (words.has(s.slice(i, j + 1))) { + res = Math.min(res, dfs(j + 1)); + } + } + + return res; + }; + + return dfs(0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * 2 ^ n + m * k)$ +* Space complexity: $O(n + m * k)$ + +> Where $n$ is the length of the string $s$, $m$ is the number of words in the dictionary, and $k$ is the average length of a word in the dictionary. + +--- + +## 2. Dynamic Programming (Top-Down) Using Hash Set + +::tabs-start + +```python +class Solution: + def minExtraChar(self, s: str, dictionary: List[str]) -> int: + words = set(dictionary) + dp = {len(s): 0} + + def dfs(i): + if i in dp: + return dp[i] + res = 1 + dfs(i + 1) + for j in range(i, len(s)): + if s[i:j + 1] in words: + res = min(res, dfs(j + 1)) + dp[i] = res + return res + + return dfs(0) +``` + +```java +public class Solution { + public int minExtraChar(String s, String[] dictionary) { + Set words = new HashSet<>(Arrays.asList(dictionary)); + int n = s.length(); + int[] dp = new int[n + 1]; + Arrays.fill(dp, -1); + dp[n] = 0; + + return dfs(0, s, words, dp); + } + + private int dfs(int i, String s, Set words, int[] dp) { + if (dp[i] != -1) return dp[i]; + int res = 1 + dfs(i + 1, s, words, dp); + for (int j = i; j < s.length(); j++) { + if (words.contains(s.substring(i, j + 1))) { + res = Math.min(res, dfs(j + 1, s, words, dp)); + } + } + dp[i] = res; + return res; + } +} +``` + +```cpp +class Solution { +public: + int minExtraChar(string s, vector& dictionary) { + unordered_set words(dictionary.begin(), dictionary.end()); + int n = s.size(); + vector dp(n + 1, -1); + dp[n] = 0; + return dfs(0, s, words, dp); + } + +private: + int dfs(int i, string& s, unordered_set& words, vector& dp) { + if (dp[i] != -1) return dp[i]; + int res = 1 + dfs(i + 1, s, words, dp); + for (int j = i; j < s.size(); j++) { + if (words.count(s.substr(i, j - i + 1))) { + res = min(res, dfs(j + 1, s, words, dp)); + } + } + dp[i] = res; + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @param {string[]} dictionary + * @return {number} + */ + minExtraChar(s, dictionary) { + const words = new Set(dictionary); + const n = s.length; + const dp = new Array(n + 1).fill(-1); + dp[n] = 0; + + const dfs = (i) => { + if (dp[i] !== -1) return dp[i]; + let res = 1 + dfs(i + 1); + for (let j = i; j < n; j++) { + if (words.has(s.slice(i, j + 1))) { + res = Math.min(res, dfs(j + 1)); + } + } + dp[i] = res; + return res; + }; + + return dfs(0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 3 + m * k)$ +* Space complexity: $O(n + m * k)$ + +> Where $n$ is the length of the string $s$, $m$ is the number of words in the dictionary, and $k$ is the average length of a word in the dictionary. + +--- + +## 3. Dynamic Programming (Bottom-Up) Using Hash Set + +::tabs-start + +```python +class Solution: + def minExtraChar(self, s: str, dictionary: List[str]) -> int: + words = set(dictionary) + n = len(s) + dp = [0] * (n + 1) + + for i in range(n - 1, -1, -1): + dp[i] = 1 + dp[i + 1] + for j in range(i, n): + if s[i:j + 1] in words: + dp[i] = min(dp[i], dp[j + 1]) + return dp[0] +``` + +```java +public class Solution { + public int minExtraChar(String s, String[] dictionary) { + Set words = new HashSet<>(Arrays.asList(dictionary)); + int n = s.length(); + int[] dp = new int[n + 1]; + + for (int i = n - 1; i >= 0; i--) { + dp[i] = 1 + dp[i + 1]; + for (int j = i; j < n; j++) { + if (words.contains(s.substring(i, j + 1))) { + dp[i] = Math.min(dp[i], dp[j + 1]); + } + } + } + return dp[0]; + } +} +``` + +```cpp +class Solution { +public: + int minExtraChar(string s, vector& dictionary) { + unordered_set words(dictionary.begin(), dictionary.end()); + int n = s.size(); + vector dp(n + 1, 0); + + for (int i = n - 1; i >= 0; i--) { + dp[i] = 1 + dp[i + 1]; + for (int j = i; j < n; j++) { + if (words.count(s.substr(i, j - i + 1))) { + dp[i] = min(dp[i], dp[j + 1]); + } + } + } + return dp[0]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @param {string[]} dictionary + * @return {number} + */ + minExtraChar(s, dictionary) { + const words = new Set(dictionary); + const n = s.length; + const dp = new Array(n + 1).fill(0); + + for (let i = n - 1; i >= 0; i--) { + dp[i] = 1 + dp[i + 1]; + for (let j = i; j < n; j++) { + if (words.has(s.slice(i, j + 1))) { + dp[i] = Math.min(dp[i], dp[j + 1]); + } + } + } + return dp[0]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 3 + m * k)$ +* Space complexity: $O(n + m * k)$ + +> Where $n$ is the length of the string $s$, $m$ is the number of words in the dictionary, and $k$ is the average length of a word in the dictionary. + +--- + +## 4. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def minExtraChar(self, s: str, dictionary: List[str]) -> int: + dp = {len(s) : 0} + + def dfs(i): + if i in dp: + return dp[i] + + res = 1 + dfs(i + 1) + for word in dictionary: + if i + len(word) > len(s): + continue + + flag = True + for j in range(len(word)): + if s[i + j] != word[j]: + flag = False + break + if flag: + res = min(res, dfs(i + len(word))) + + dp[i] = res + return res + + return dfs(0) +``` + +```java +public class Solution { + public int minExtraChar(String s, String[] dictionary) { + Map dp = new HashMap<>(); + dp.put(s.length(), 0); + return dfs(0, s, dictionary, dp); + } + + private int dfs(int i, String s, String[] dictionary, Map dp) { + if (dp.containsKey(i)) { + return dp.get(i); + } + + int res = 1 + dfs(i + 1, s, dictionary, dp); + for (String word : dictionary) { + if (i + word.length() > s.length()) continue; + + boolean flag = true; + for (int j = 0; j < word.length(); j++) { + if (s.charAt(i + j) != word.charAt(j)) { + flag = false; + break; + } + } + if (flag) { + res = Math.min(res, dfs(i + word.length(), s, dictionary, dp)); + } + } + dp.put(i, res); + return res; + } +} +``` + +```cpp +class Solution { + unordered_map dp; + +public: + int minExtraChar(string s, vector& dictionary) { + dp[s.size()] = 0; + return dfs(0, s, dictionary); + } + +private: + int dfs(int i, string& s, vector& dictionary) { + if (dp.count(i)) { + return dp[i]; + } + + int res = 1 + dfs(i + 1, s, dictionary); + for (const string& word : dictionary) { + if (i + word.size() > s.size()) continue; + + bool flag = true; + for (int j = 0; j < word.size(); j++) { + if (s[i + j] != word[j]) { + flag = false; + break; + } + } + if (flag) { + res = min(res, dfs(i + word.size(), s, dictionary)); + } + } + return dp[i] = res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @param {string[]} dictionary + * @return {number} + */ + minExtraChar(s, dictionary) { + const dp = new Map(); + dp.set(s.length, 0); + + const dfs = (i) => { + if (dp.has(i)) return dp.get(i); + + let res = 1 + dfs(i + 1); + for (const word of dictionary) { + if (i + word.length > s.length) continue; + + let flag = true; + for (let j = 0; j < word.length; j++) { + if (s[i + j] !== word[j]) { + flag = false; + break; + } + } + if (flag) { + res = Math.min(res, dfs(i + word.length)); + } + } + dp.set(i, res); + return res; + }; + + return dfs(0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * m * k)$ +* Space complexity: $O(n)$ + +> Where $n$ is the length of the string $s$, $m$ is the number of words in the dictionary, and $k$ is the average length of a word in the dictionary. + +--- + +## 5. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def minExtraChar(self, s: str, dictionary: List[str]) -> int: + n = len(s) + dp = [0] * (n + 1) + + for i in range(n - 1, -1, -1): + dp[i] = 1 + dp[i + 1] + for word in dictionary: + if i + len(word) <= n and s[i:i + len(word)] == word: + dp[i] = min(dp[i], dp[i + len(word)]) + + return dp[0] +``` + +```java +public class Solution { + public int minExtraChar(String s, String[] dictionary) { + int n = s.length(); + int[] dp = new int[n + 1]; + + for (int i = n - 1; i >= 0; i--) { + dp[i] = 1 + dp[i + 1]; + for (String word : dictionary) { + if (i + word.length() <= n && s.startsWith(word, i)) { + dp[i] = Math.min(dp[i], dp[i + word.length()]); + } + } + } + + return dp[0]; + } +} +``` + +```cpp +class Solution { +public: + int minExtraChar(string s, vector& dictionary) { + int n = s.size(); + vector dp(n + 1, 0); + + for (int i = n - 1; i >= 0; i--) { + dp[i] = 1 + dp[i + 1]; + for (const string& word : dictionary) { + if (i + word.size() <= n && s.substr(i, word.size()) == word) { + dp[i] = min(dp[i], dp[i + word.size()]); + } + } + } + + return dp[0]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @param {string[]} dictionary + * @return {number} + */ + minExtraChar(s, dictionary) { + const n = s.length; + const dp = new Array(n + 1).fill(0); + + for (let i = n - 1; i >= 0; i--) { + dp[i] = 1 + dp[i + 1]; + for (const word of dictionary) { + if (i + word.length <= n && s.slice(i, i + word.length) === word) { + dp[i] = Math.min(dp[i], dp[i + word.length]); + } + } + } + + return dp[0]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * m * k)$ +* Space complexity: $O(n)$ + +> Where $n$ is the length of the string $s$, $m$ is the number of words in the dictionary, and $k$ is the average length of a word in the dictionary. + +--- + +## 6. Dynamic Programming (Top-Down) Using Trie + +::tabs-start + +```python +class TrieNode: + def __init__(self): + self.children = {} + self.isWord = False + +class Trie: + def __init__(self): + self.root = TrieNode() + + def addWord(self, word): + curr = self.root + for c in word: + if c not in curr.children: + curr.children[c] = TrieNode() + curr = curr.children[c] + curr.isWord = True + +class Solution: + def minExtraChar(self, s: str, dictionary: List[str]) -> int: + trie = Trie() + for w in dictionary: + trie.addWord(w) + + dp = {len(s): 0} + + def dfs(i): + if i in dp: + return dp[i] + res = 1 + dfs(i + 1) + curr = trie.root + for j in range(i, len(s)): + if s[j] not in curr.children: + break + curr = curr.children[s[j]] + if curr.isWord: + res = min(res, dfs(j + 1)) + + dp[i] = res + return res + + return dfs(0) +``` + +```java +class TrieNode { + TrieNode[] children; + boolean isWord; + + TrieNode() { + children = new TrieNode[26]; + isWord = false; + } +} + +class Trie { + TrieNode root; + + Trie() { + root = new TrieNode(); + } + + void addWord(String word) { + TrieNode curr = root; + for (char c : word.toCharArray()) { + if (curr.children[c - 'a'] == null) { + curr.children[c - 'a'] = new TrieNode(); + } + curr = curr.children[c - 'a']; + } + curr.isWord = true; + } +} + +public class Solution { + public int minExtraChar(String s, String[] dictionary) { + Trie trie = new Trie(); + for (String word : dictionary) { + trie.addWord(word); + } + + int[] dp = new int[s.length() + 1]; + Arrays.fill(dp, -1); + + return dfs(0, s, trie, dp); + } + + private int dfs(int i, String s, Trie trie, int[] dp) { + if (i == s.length()) return 0; + if (dp[i] != -1) return dp[i]; + + int res = 1 + dfs(i + 1, s, trie, dp); + TrieNode curr = trie.root; + + for (int j = i; j < s.length(); j++) { + if (curr.children[s.charAt(j) - 'a'] == null) break; + curr = curr.children[s.charAt(j) - 'a']; + if (curr.isWord) { + res = Math.min(res, dfs(j + 1, s, trie, dp)); + } + } + + dp[i] = res; + return res; + } +} +``` + +```cpp +class TrieNode { +public: + TrieNode* children[26]; + bool isWord; + + TrieNode() { + for (int i = 0; i < 26; ++i) children[i] = nullptr; + isWord = false; + } +}; + +class Trie { +public: + TrieNode* root; + + Trie() { + root = new TrieNode(); + } + + void addWord(const string& word) { + TrieNode* curr = root; + for (char c : word) { + if (!curr->children[c - 'a']) { + curr->children[c - 'a'] = new TrieNode(); + } + curr = curr->children[c - 'a']; + } + curr->isWord = true; + } +}; + +class Solution { +public: + int minExtraChar(string s, vector& dictionary) { + Trie trie; + for (const string& word : dictionary) { + trie.addWord(word); + } + + vector dp(s.size() + 1, -1); + return dfs(0, s, trie, dp); + } + +private: + int dfs(int i, const string& s, Trie& trie, vector& dp) { + if (i == s.size()) return 0; + if (dp[i] != -1) return dp[i]; + + int res = 1 + dfs(i + 1, s, trie, dp); + TrieNode* curr = trie.root; + + for (int j = i; j < s.size(); ++j) { + if (!curr->children[s[j] - 'a']) break; + curr = curr->children[s[j] - 'a']; + if (curr->isWord) { + res = min(res, dfs(j + 1, s, trie, dp)); + } + } + + dp[i] = res; + return res; + } +}; +``` + +```javascript +class TrieNode { + constructor() { + this.children = {}; + this.isWord = false; + } +} + +class Trie { + constructor() { + this.root = new TrieNode(); + } + + /** + * @param {string} word + * @return {void} + */ + addWord(word) { + let curr = this.root; + for (const c of word) { + if (!curr.children[c]) { + curr.children[c] = new TrieNode(); + } + curr = curr.children[c]; + } + curr.isWord = true; + } +} + +class Solution { + /** + * @param {string} s + * @param {string[]} dictionary + * @return {number} + */ + minExtraChar(s, dictionary) { + const trie = new Trie(); + for (const word of dictionary) { + trie.addWord(word); + } + + const dp = Array(s.length + 1).fill(-1); + + const dfs = (i) => { + if (i === s.length) return 0; + if (dp[i] !== -1) return dp[i]; + + let res = 1 + dfs(i + 1); + let curr = trie.root; + + for (let j = i; j < s.length; j++) { + if (!curr.children[s[j]]) break; + curr = curr.children[s[j]]; + if (curr.isWord) { + res = Math.min(res, dfs(j + 1)); + } + } + + dp[i] = res; + return res; + }; + + return dfs(0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2 + m * k)$ +* Space complexity: $O(n + m * k)$ + +> Where $n$ is the length of the string $s$, $m$ is the number of words in the dictionary, and $k$ is the average length of a word in the dictionary. + +--- + +## 7. Dynamic Programming (Bottom-Up) Using Trie + +::tabs-start + +```python +class TrieNode: + def __init__(self): + self.children = {} + self.isWord = False + +class Trie: + def __init__(self): + self.root = TrieNode() + + def addWord(self, word): + curr = self.root + for c in word: + if c not in curr.children: + curr.children[c] = TrieNode() + curr = curr.children[c] + curr.isWord = True + +class Solution: + def minExtraChar(self, s: str, dictionary: List[str]) -> int: + trie = Trie() + for w in dictionary: + trie.addWord(w) + + n = len(s) + dp = [0] * (n + 1) + + for i in range(n - 1, -1, -1): + dp[i] = 1 + dp[i + 1] + curr = trie.root + for j in range(i, n): + if s[j] not in curr.children: + break + curr = curr.children[s[j]] + if curr.isWord: + dp[i] = min(dp[i], dp[j + 1]) + + return dp[0] +``` + +```java +class TrieNode { + TrieNode[] children; + boolean isWord; + + TrieNode() { + children = new TrieNode[26]; + isWord = false; + } +} + +class Trie { + TrieNode root; + + Trie() { + root = new TrieNode(); + } + + void addWord(String word) { + TrieNode curr = root; + for (char c : word.toCharArray()) { + if (curr.children[c - 'a'] == null) { + curr.children[c - 'a'] = new TrieNode(); + } + curr = curr.children[c - 'a']; + } + curr.isWord = true; + } +} + +public class Solution { + public int minExtraChar(String s, String[] dictionary) { + Trie trie = new Trie(); + for (String word : dictionary) { + trie.addWord(word); + } + + int n = s.length(); + int[] dp = new int[n + 1]; + for (int i = n - 1; i >= 0; i--) { + dp[i] = 1 + dp[i + 1]; + TrieNode curr = trie.root; + + for (int j = i; j < n; j++) { + if (curr.children[s.charAt(j) - 'a'] == null) break; + curr = curr.children[s.charAt(j) - 'a']; + if (curr.isWord) { + dp[i] = Math.min(dp[i], dp[j + 1]); + } + } + } + + return dp[0]; + } +} +``` + +```cpp +class TrieNode { +public: + TrieNode* children[26]; + bool isWord; + + TrieNode() { + for (int i = 0; i < 26; ++i) children[i] = nullptr; + isWord = false; + } +}; + +class Trie { +public: + TrieNode* root; + + Trie() { + root = new TrieNode(); + } + + void addWord(const string& word) { + TrieNode* curr = root; + for (char c : word) { + if (!curr->children[c - 'a']) { + curr->children[c - 'a'] = new TrieNode(); + } + curr = curr->children[c - 'a']; + } + curr->isWord = true; + } +}; + +class Solution { +public: + int minExtraChar(string s, vector& dictionary) { + Trie trie; + for (const string& word : dictionary) { + trie.addWord(word); + } + + int n = s.size(); + vector dp(n + 1); + for (int i = n - 1; i >= 0; --i) { + dp[i] = 1 + dp[i + 1]; + TrieNode* curr = trie.root; + + for (int j = i; j < n; ++j) { + if (!curr->children[s[j] - 'a']) break; + curr = curr->children[s[j] - 'a']; + if (curr->isWord) { + dp[i] = min(dp[i], dp[j + 1]); + } + } + } + + return dp[0]; + } +}; +``` + +```javascript +class TrieNode { + constructor() { + this.children = {}; + this.isWord = false; + } +} + +class Trie { + constructor() { + this.root = new TrieNode(); + } + + /** + * @param {string} word + * @return {void} + */ + addWord(word) { + let curr = this.root; + for (const c of word) { + if (!curr.children[c]) { + curr.children[c] = new TrieNode(); + } + curr = curr.children[c]; + } + curr.isWord = true; + } +} + +class Solution { + /** + * @param {string} s + * @param {string[]} dictionary + * @return {number} + */ + minExtraChar(s, dictionary) { + const trie = new Trie(); + for (const word of dictionary) { + trie.addWord(word); + } + + const n = s.length; + const dp = new Int32Array(n + 1); + for (let i = n - 1; i >= 0; i--) { + dp[i] = 1 + dp[i + 1]; + let curr = trie.root; + + for (let j = i; j < n; j++) { + if (!curr.children[s[j]]) break; + curr = curr.children[s[j]]; + if (curr.isWord) { + dp[i] = Math.min(dp[i], dp[j + 1]); + } + } + } + + return dp[0]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2 + m * k)$ +* Space complexity: $O(n + m * k)$ + +> Where $n$ is the length of the string $s$, $m$ is the number of words in the dictionary, and $k$ is the average length of a word in the dictionary. \ No newline at end of file diff --git a/articles/find-eventual-safe-states.md b/articles/find-eventual-safe-states.md new file mode 100644 index 000000000..df245c8a2 --- /dev/null +++ b/articles/find-eventual-safe-states.md @@ -0,0 +1,308 @@ +## 1. Depth First Search + +::tabs-start + +```python +class Solution: + def eventualSafeNodes(self, graph: List[List[int]]) -> List[int]: + n = len(graph) + safe = {} + + def dfs(node): + if node in safe: + return safe[node] + safe[node] = False + for nei in graph[node]: + if not dfs(nei): + return safe[node] + safe[node] = True + return safe[node] + + res = [] + for node in range(n): + if dfs(node): + res.append(node) + return res +``` + +```java +public class Solution { + private Boolean[] safe; + + public List eventualSafeNodes(int[][] graph) { + int n = graph.length; + safe = new Boolean[n]; + List res = new ArrayList<>(); + for (int node = 0; node < n; node++) { + if (dfs(graph, node)) { + res.add(node); + } + } + return res; + } + + private boolean dfs(int[][] graph, int node) { + if (safe[node] != null) { + return safe[node]; + } + + safe[node] = false; + for (int nei : graph[node]) { + if (!dfs(graph, nei)) { + return false; + } + } + safe[node] = true; + return true; + } +} +``` + +```cpp +class Solution { + vector safe; + +public: + vector eventualSafeNodes(vector>& graph) { + int n = graph.size(); + vector res; + safe.assign(n, -1); + for (int node = 0; node < n; node++) { + if (dfs(graph, node)) { + res.push_back(node); + } + } + return res; + } + +private: + bool dfs(vector>& graph, int node) { + if (safe[node] != -1) { + return safe[node]; + } + safe[node] = 0; + for (int nei : graph[node]) { + if (!dfs(graph, nei)) { + return false; + } + } + safe[node] = 1; + return true; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} graph + * @return {number[]} + */ + eventualSafeNodes(graph) { + const n = graph.length; + const safe = Array(n).fill(undefined); + const res = []; + + const dfs = (node) => { + if (safe[node] !== undefined) { + return safe[node]; + } + safe[node] = false; + for (let nei of graph[node]) { + if (!dfs(nei)) { + return false; + } + } + safe[node] = true; + return true; + }; + + for (let node = 0; node < n; node++) { + if (dfs(node)) { + res.push(node); + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(V + E)$ +* Space complexity: $O(V + E)$ + +> Where $V$ is the number of vertices and $E$ is the number of edges in the given graph. + +--- + +## 2. Topological Sort (Kahn's Algorithm) + +::tabs-start + +```python +class Solution: + def eventualSafeNodes(self, graph: List[List[int]]) -> List[int]: + n = len(graph) + outdegree = [0] * n + parents = [[] for _ in range(n)] + queue = deque() + + for node in range(n): + outdegree[node] = len(graph[node]) + if outdegree[node] == 0: + queue.append(node) + for nei in graph[node]: + parents[nei].append(node) + + while queue: + node = queue.popleft() + for parent in parents[node]: + outdegree[parent] -= 1 + if outdegree[parent] == 0: + queue.append(parent) + + res = [] + for node in range(n): + if outdegree[node] <= 0: + res.append(node) + return res +``` + +```java +public class Solution { + public List eventualSafeNodes(int[][] graph) { + int n = graph.length; + int[] outdegree = new int[n]; + List[] parents = new ArrayList[n]; + Queue queue = new LinkedList<>(); + + for (int i = 0; i < n; i++) { + parents[i] = new ArrayList<>(); + } + + for (int node = 0; node < n; node++) { + outdegree[node] = graph[node].length; + if (outdegree[node] == 0) { + queue.add(node); + } + for (int nei : graph[node]) { + parents[nei].add(node); + } + } + + while (!queue.isEmpty()) { + int node = queue.poll(); + for (int parent : parents[node]) { + outdegree[parent]--; + if (outdegree[parent] == 0) { + queue.add(parent); + } + } + } + + List res = new ArrayList<>(); + for (int node = 0; node < n; node++) { + if (outdegree[node] <= 0) { + res.add(node); + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + vector eventualSafeNodes(vector>& graph) { + int n = graph.size(); + vector outdegree(n, 0); + vector> parents(n); + queue q; + + for (int node = 0; node < n; node++) { + outdegree[node] = graph[node].size(); + if (outdegree[node] == 0) { + q.push(node); + } + for (int nei : graph[node]) { + parents[nei].push_back(node); + } + } + + while (!q.empty()) { + int node = q.front(); + q.pop(); + for (int parent : parents[node]) { + outdegree[parent]--; + if (outdegree[parent] == 0) { + q.push(parent); + } + } + } + + vector res; + for (int node = 0; node < n; node++) { + if (outdegree[node] <= 0) { + res.push_back(node); + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} graph + * @return {number[]} + */ + eventualSafeNodes(graph) { + const n = graph.length; + const outdegree = Array(n).fill(0); + const parents = Array.from({ length: n }, () => []); + const queue = new Queue(); + + for (let node = 0; node < n; node++) { + outdegree[node] = graph[node].length; + if (outdegree[node] === 0) { + queue.push(node); + } + for (let nei of graph[node]) { + parents[nei].push(node); + } + } + + while (!queue.isEmpty()) { + const node = queue.pop(); + for (let parent of parents[node]) { + outdegree[parent]--; + if (outdegree[parent] === 0) { + queue.push(parent); + } + } + } + + const res = []; + for (let node = 0; node < n; node++) { + if (outdegree[node] <= 0) { + res.push(node); + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(V + E)$ +* Space complexity: $O(V + E)$ + +> Where $V$ is the number of vertices and $E$ is the number of edges in the given graph. \ No newline at end of file diff --git a/articles/find-the-town-judge.md b/articles/find-the-town-judge.md new file mode 100644 index 000000000..393448cd8 --- /dev/null +++ b/articles/find-the-town-judge.md @@ -0,0 +1,199 @@ +## 1. Indegree & Outdegree + +::tabs-start + +```python +class Solution: + def findJudge(self, n: int, trust: List[List[int]]) -> int: + incoming = defaultdict(int) + outgoing = defaultdict(int) + + for src, dst in trust: + outgoing[src] += 1 + incoming[dst] += 1 + + for i in range(1, n + 1): + if outgoing[i] == 0 and incoming[i] == n - 1: + return i + + return -1 +``` + +```java +public class Solution { + public int findJudge(int n, int[][] trust) { + int[] incoming = new int[n + 1]; + int[] outgoing = new int[n + 1]; + + for (int[] t : trust) { + outgoing[t[0]]++; + incoming[t[1]]++; + } + + for (int i = 1; i <= n; i++) { + if (outgoing[i] == 0 && incoming[i] == n - 1) { + return i; + } + } + + return -1; + } +} +``` + +```cpp +class Solution { +public: + int findJudge(int n, vector>& trust) { + vector incoming(n + 1, 0), outgoing(n + 1, 0); + + for (auto& t : trust) { + outgoing[t[0]]++; + incoming[t[1]]++; + } + + for (int i = 1; i <= n; i++) { + if (outgoing[i] == 0 && incoming[i] == n - 1) + return i; + } + + return -1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number[][]} trust + * @return {number} + */ + findJudge(n, trust) { + let incoming = new Array(n + 1).fill(0); + let outgoing = new Array(n + 1).fill(0); + + for (let [src, dst] of trust) { + outgoing[src]++; + incoming[dst]++; + } + + for (let i = 1; i <= n; i++) { + if (outgoing[i] === 0 && incoming[i] === n - 1) { + return i; + } + } + + return -1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(V + E)$ +* Space complexity: $O(V)$ + +> Where $V$ is the number of vertices and $E$ is the number of edges. + +--- + +## 2. Indegree & Outdegree (Optimal) + +::tabs-start + +```python +class Solution: + def findJudge(self, n: int, trust: List[List[int]]) -> int: + delta = defaultdict(int) + + for src, dst in trust: + delta[src] -= 1 + delta[dst] += 1 + + for i in range(1, n + 1): + if delta[i] == n - 1: + return i + + return -1 +``` + +```java +public class Solution { + public int findJudge(int n, int[][] trust) { + int[] delta = new int[n + 1]; + + for (int[] t : trust) { + delta[t[0]] -= 1; + delta[t[1]] += 1; + } + + for (int i = 1; i <= n; i++) { + if (delta[i] == n - 1) { + return i; + } + } + + return -1; + } +} +``` + +```cpp +class Solution { +public: + int findJudge(int n, vector>& trust) { + vector delta(n + 1, 0); + + for (auto& t : trust) { + delta[t[0]]--; + delta[t[1]]++; + } + + for (int i = 1; i <= n; i++) { + if (delta[i] == n - 1) { + return i; + } + } + + return -1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number[][]} trust + * @return {number} + */ + findJudge(n, trust) { + let delta = new Array(n + 1).fill(0); + + for (let [src, dst] of trust) { + delta[src]--; + delta[dst]++; + } + + for (let i = 1; i <= n; i++) { + if (delta[i] === n - 1) { + return i; + } + } + + return -1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(V + E)$ +* Space complexity: $O(V)$ + +> Where $V$ is the number of vertices and $E$ is the number of edges. \ No newline at end of file diff --git a/articles/intersection-of-two-linked-lists.md b/articles/intersection-of-two-linked-lists.md new file mode 100644 index 000000000..855f2a43f --- /dev/null +++ b/articles/intersection-of-two-linked-lists.md @@ -0,0 +1,554 @@ +## 1. Brute Force + +::tabs-start + +```python +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, x): +# self.val = x +# self.next = None +class Solution: + def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> Optional[ListNode]: + while headA: + cur = headB + while cur: + if headA == cur: + return headA + cur = cur.next + headA = headA.next + return None +``` + +```java +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode(int x) { + * val = x; + * next = null; + * } + * } + */ +public class Solution { + public ListNode getIntersectionNode(ListNode headA, ListNode headB) { + while (headA != null) { + ListNode cur = headB; + while (cur != null) { + if (headA == cur) { + return headA; + } + cur = cur.next; + } + headA = headA.next; + } + return null; + } +} +``` + +```cpp +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode(int x) : val(x), next(NULL) {} + * }; + */ +class Solution { +public: + ListNode* getIntersectionNode(ListNode* headA, ListNode* headB) { + while (headA) { + ListNode* cur = headB; + while (cur) { + if (headA == cur) { + return headA; + } + cur = cur->next; + } + headA = headA->next; + } + return nullptr; + } +}; +``` + +```javascript +/** + * Definition for singly-linked list. + * class ListNode { + * constructor(val = 0, next = null) { + * this.val = val; + * this.next = next; + * } + * } + */ +class Solution { + /** + * @param {ListNode} headA + * @param {ListNode} headB + * @return {ListNode} + */ + getIntersectionNode(headA, headB) { + while (headA) { + let cur = headB; + while (cur) { + if (headA === cur) { + return headA; + } + cur = cur.next; + } + headA = headA.next; + } + return null; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n)$ +* Space complexity: $O(1)$ extra space. + +> Where $m$ is the length of the first list and $n$ is the length of the second list. + +--- + +## 2. Hash Set + +::tabs-start + +```python +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, x): +# self.val = x +# self.next = None +class Solution: + def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> Optional[ListNode]: + nodeSet = set() + cur = headA + while cur: + nodeSet.add(cur) + cur = cur.next + + cur = headB + while cur: + if cur in nodeSet: + return cur + cur = cur.next + + return None +``` + +```java +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode(int x) { + * val = x; + * next = null; + * } + * } + */ +public class Solution { + public ListNode getIntersectionNode(ListNode headA, ListNode headB) { + HashSet nodeSet = new HashSet<>(); + + ListNode cur = headA; + while (cur != null) { + nodeSet.add(cur); + cur = cur.next; + } + + cur = headB; + while (cur != null) { + if (nodeSet.contains(cur)) { + return cur; + } + cur = cur.next; + } + + return null; + } +} +``` + +```cpp +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode(int x) : val(x), next(NULL) {} + * }; + */ +class Solution { +public: + ListNode* getIntersectionNode(ListNode* headA, ListNode* headB) { + unordered_set nodeSet; + + ListNode* cur = headA; + while (cur) { + nodeSet.insert(cur); + cur = cur->next; + } + + cur = headB; + while (cur) { + if (nodeSet.find(cur) != nodeSet.end()) { + return cur; + } + cur = cur->next; + } + + return nullptr; + } +}; +``` + +```javascript +/** + * Definition for singly-linked list. + * class ListNode { + * constructor(val = 0, next = null) { + * this.val = val; + * this.next = next; + * } + * } + */ +class Solution { + /** + * @param {ListNode} headA + * @param {ListNode} headB + * @return {ListNode} + */ + getIntersectionNode(headA, headB) { + const nodeSet = new Set(); + + let cur = headA; + while (cur) { + nodeSet.add(cur); + cur = cur.next; + } + + cur = headB; + while (cur) { + if (nodeSet.has(cur)) { + return cur; + } + cur = cur.next; + } + + return null; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m + n)$ +* Space complexity: $O(m)$ + +> Where $m$ is the length of the first list and $n$ is the length of the second list. + +--- + +## 3. Two Pointers - I + +::tabs-start + +```python +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, x): +# self.val = x +# self.next = None +class Solution: + def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> Optional[ListNode]: + def getLength(head): + length, cur = 0, head + while cur: + length += 1 + cur = cur.next + return length + + m = getLength(headA) + n = getLength(headB) + l1, l2 = headA, headB + + if m < n: + m, n = n, m + l1, l2 = headB, headA + + while m - n: + m -= 1 + l1 = l1.next + + while l1 != l2: + l1 = l1.next + l2 = l2.next + + return l1 +``` + +```java +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode(int x) { + * val = x; + * next = null; + * } + * } + */ +public class Solution { + private int getLength(ListNode head) { + int length = 0; + while (head != null) { + length++; + head = head.next; + } + return length; + } + + public ListNode getIntersectionNode(ListNode headA, ListNode headB) { + int m = getLength(headA); + int n = getLength(headB); + ListNode l1 = headA, l2 = headB; + + if (m < n) { + int temp = m; m = n; n = temp; + ListNode tempNode = l1; l1 = l2; l2 = tempNode; + } + + while (m > n) { + l1 = l1.next; + m--; + } + + while (l1 != null && l1 != l2) { + l1 = l1.next; + l2 = l2.next; + } + + return l1; + } +} +``` + +```cpp +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode(int x) : val(x), next(NULL) {} + * }; + */ +class Solution { + int getLength(ListNode* head) { + int length = 0; + while (head) { + length++; + head = head->next; + } + return length; + } + +public: + ListNode* getIntersectionNode(ListNode* headA, ListNode* headB) { + int m = getLength(headA), n = getLength(headB); + ListNode* l1 = headA, *l2 = headB; + + if (m < n) { + swap(m, n); + swap(l1, l2); + } + + while (m-- > n) { + l1 = l1->next; + } + + while (l1 && l1 != l2) { + l1 = l1->next; + l2 = l2->next; + } + + return l1; + } +}; +``` + +```javascript +/** + * Definition for singly-linked list. + * class ListNode { + * constructor(val = 0, next = null) { + * this.val = val; + * this.next = next; + * } + * } + */ +class Solution { + /** + * @param {ListNode} headA + * @param {ListNode} headB + * @return {ListNode} + */ + getIntersectionNode(headA, headB) { + const getLength = (head) => { + let length = 0, cur = head; + while (cur) { + length++; + cur = cur.next; + } + return length; + }; + + let m = getLength(headA); + let n = getLength(headB); + let l1 = headA, l2 = headB; + + if (m < n) { + [m, n] = [n, m]; + [l1, l2] = [l2, l1]; + } + + while (m-- > n) { + l1 = l1.next; + } + + while (l1 && l1 !== l2) { + l1 = l1.next; + l2 = l2.next; + } + + return l1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m + n)$ +* Space complexity: $O(1)$ extra space. + +> Where $m$ is the length of the first list and $n$ is the length of the second list. + +--- + +## 4. Two Pointers - II + +::tabs-start + +```python +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, x): +# self.val = x +# self.next = None +class Solution: + def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> Optional[ListNode]: + l1, l2 = headA, headB + while l1 != l2: + l1 = l1.next if l1 else headB + l2 = l2.next if l2 else headA + return l1 +``` + +```java +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode(int x) { + * val = x; + * next = null; + * } + * } + */ +public class Solution { + public ListNode getIntersectionNode(ListNode headA, ListNode headB) { + ListNode l1 = headA, l2 = headB; + while (l1 != l2) { + l1 = (l1 != null) ? l1.next : headB; + l2 = (l2 != null) ? l2.next : headA; + } + return l1; + } +} +``` + +```cpp +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode(int x) : val(x), next(NULL) {} + * }; + */ +class Solution { +public: + ListNode* getIntersectionNode(ListNode* headA, ListNode* headB) { + ListNode* l1 = headA; + ListNode* l2 = headB; + while (l1 != l2) { + l1 = l1 ? l1->next : headB; + l2 = l2 ? l2->next : headA; + } + return l1; + } +}; +``` + +```javascript +/** + * Definition for singly-linked list. + * class ListNode { + * constructor(val = 0, next = null) { + * this.val = val; + * this.next = next; + * } + * } + */ +class Solution { + /** + * @param {ListNode} headA + * @param {ListNode} headB + * @return {ListNode} + */ + getIntersectionNode(headA, headB) { + let l1 = headA, l2 = headB; + while (l1 !== l2) { + l1 = l1 ? l1.next : headB; + l2 = l2 ? l2.next : headA; + } + return l1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m + n)$ +* Space complexity: $O(1)$ extra space. + +> Where $m$ is the length of the first list and $n$ is the length of the second list. \ No newline at end of file diff --git a/articles/island-perimeter.md b/articles/island-perimeter.md new file mode 100644 index 000000000..c4bb78ca5 --- /dev/null +++ b/articles/island-perimeter.md @@ -0,0 +1,516 @@ +## 1. Depth First Search + +::tabs-start + +```python +class Solution: + def islandPerimeter(self, grid: List[List[int]]) -> int: + rows, cols = len(grid), len(grid[0]) + visit = set() + + def dfs(i, j): + if i < 0 or j < 0 or i >= rows or j >= cols or grid[i][j] == 0: + return 1 + if (i, j) in visit: + return 0 + + visit.add((i, j)) + perim = dfs(i, j + 1) + dfs(i + 1, j) + dfs(i, j - 1) + dfs(i - 1, j) + return perim + + for i in range(rows): + for j in range(cols): + if grid[i][j]: + return dfs(i, j) + return 0 +``` + +```java +public class Solution { + private int[][] grid; + private boolean[][] visited; + private int rows, cols; + + public int islandPerimeter(int[][] grid) { + this.grid = grid; + this.rows = grid.length; + this.cols = grid[0].length; + this.visited = new boolean[rows][cols]; + + for (int i = 0; i < rows; i++) { + for (int j = 0; j < cols; j++) { + if (grid[i][j] == 1) { + return dfs(i, j); + } + } + } + return 0; + } + + private int dfs(int i, int j) { + if (i < 0 || j < 0 || i >= rows || + j >= cols || grid[i][j] == 0) { + return 1; + } + if (visited[i][j]) { + return 0; + } + + visited[i][j] = true; + return dfs(i, j + 1) + dfs(i + 1, j) + + dfs(i, j - 1) + dfs(i - 1, j); + } +} +``` + +```cpp +class Solution { +private: + vector> grid; + vector> visited; + int rows, cols; + + int dfs(int i, int j) { + if (i < 0 || j < 0 || i >= rows || + j >= cols || grid[i][j] == 0) { + return 1; + } + if (visited[i][j]) { + return 0; + } + + visited[i][j] = true; + return dfs(i, j + 1) + dfs(i + 1, j) + + dfs(i, j - 1) + dfs(i - 1, j); + } + +public: + int islandPerimeter(vector>& grid) { + this->grid = grid; + rows = grid.size(); + cols = grid[0].size(); + visited = vector>(rows, vector(cols, false)); + + for (int i = 0; i < rows; ++i) { + for (int j = 0; j < cols; ++j) { + if (grid[i][j] == 1) { + return dfs(i, j); + } + } + } + return 0; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} grid + * @return {number} + */ + islandPerimeter(grid) { + const rows = grid.length, cols = grid[0].length; + const visited = Array.from({ length: rows }, () => Array(cols).fill(false)); + + const dfs = (i, j) => { + if (i < 0 || j < 0 || i >= rows || j >= cols || grid[i][j] === 0) { + return 1; + } + if (visited[i][j]) { + return 0; + } + + visited[i][j] = true; + return dfs(i, j + 1) + dfs(i + 1, j) + + dfs(i, j - 1) + dfs(i - 1, j); + }; + + for (let i = 0; i < rows; i++) { + for (let j = 0; j < cols; j++) { + if (grid[i][j] === 1) { + return dfs(i, j); + } + } + } + return 0; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n)$ +* Space complexity: $O(m * n)$ + +> Where $m$ is the number of rows and $n$ is the number of columns in the grid. + +--- + +## 2. Breadth First Search + +::tabs-start + +```python +class Solution: + def islandPerimeter(self, grid: List[List[int]]) -> int: + rows, cols = len(grid), len(grid[0]) + visited = set() + directions = [(0, 1), (1, 0), (0, -1), (-1, 0)] + + def bfs(r, c): + queue = deque([(r, c)]) + visited.add((r, c)) + perimeter = 0 + + while queue: + x, y = queue.popleft() + for dx, dy in directions: + nx, ny = x + dx, y + dy + if (nx < 0 or ny < 0 or nx >= rows or + ny >= cols or grid[nx][ny] == 0 + ): + perimeter += 1 + elif (nx, ny) not in visited: + visited.add((nx, ny)) + queue.append((nx, ny)) + return perimeter + + for i in range(rows): + for j in range(cols): + if grid[i][j] == 1: + return bfs(i, j) + return 0 +``` + +```java +public class Solution { + public int islandPerimeter(int[][] grid) { + int rows = grid.length, cols = grid[0].length; + boolean[][] visited = new boolean[rows][cols]; + int[][] directions = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}}; + + for (int i = 0; i < rows; i++) { + for (int j = 0; j < cols; j++) { + if (grid[i][j] == 1) { + Queue queue = new LinkedList<>(); + queue.offer(new int[]{i, j}); + visited[i][j] = true; + int perimeter = 0; + + while (!queue.isEmpty()) { + int[] cell = queue.poll(); + int x = cell[0], y = cell[1]; + + for (int[] dir : directions) { + int nx = x + dir[0], ny = y + dir[1]; + if (nx < 0 || ny < 0 || nx >= rows || + ny >= cols || grid[nx][ny] == 0) { + perimeter++; + } else if (!visited[nx][ny]) { + visited[nx][ny] = true; + queue.offer(new int[]{nx, ny}); + } + } + } + return perimeter; + } + } + } + return 0; + } +} +``` + +```cpp +class Solution { +public: + int islandPerimeter(vector>& grid) { + int rows = grid.size(), cols = grid[0].size(); + vector> visited(rows, vector(cols, false)); + int directions[4][2] = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}}; + + for (int i = 0; i < rows; ++i) { + for (int j = 0; j < cols; ++j) { + if (grid[i][j] == 1) { + queue> q; + q.push({i, j}); + visited[i][j] = true; + int perimeter = 0; + + while (!q.empty()) { + auto [x, y] = q.front(); + q.pop(); + + for (auto& dir : directions) { + int nx = x + dir[0], ny = y + dir[1]; + if (nx < 0 || ny < 0 || nx >= rows || + ny >= cols || grid[nx][ny] == 0) { + perimeter++; + } else if (!visited[nx][ny]) { + visited[nx][ny] = true; + q.push({nx, ny}); + } + } + } + return perimeter; + } + } + } + return 0; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} grid + * @return {number} + */ + islandPerimeter(grid) { + const rows = grid.length, cols = grid[0].length; + const visited = Array.from({ length: rows }, () => Array(cols).fill(false)); + const directions = [[0, 1], [1, 0], [0, -1], [-1, 0]]; + + const bfs = (r, c) => { + const queue = new Queue([[r, c]]); + visited[r][c] = true; + let perimeter = 0; + while (!queue.isEmpty()) { + const [x, y] = queue.pop(); + + for (const [dx, dy] of directions) { + const nx = x + dx, ny = y + dy; + + if (nx < 0 || ny < 0 || nx >= rows || + ny >= cols || grid[nx][ny] === 0) { + perimeter++; + } else if (!visited[nx][ny]) { + visited[nx][ny] = true; + queue.push([nx, ny]); + } + } + } + return perimeter; + }; + + for (let i = 0; i < rows; i++) { + for (let j = 0; j < cols; j++) { + if (grid[i][j] === 1) { + return bfs(i, j); + } + } + } + return 0; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n)$ +* Space complexity: $O(m * n)$ + +> Where $m$ is the number of rows and $n$ is the number of columns in the grid. + +--- + +## 3. Iteration - I + +::tabs-start + +```python +class Solution: + def islandPerimeter(self, grid: List[List[int]]) -> int: + m, n, res = len(grid), len(grid[0]), 0 + for i in range(m): + for j in range(n): + if grid[i][j] == 1: + res += (i + 1 >= m or grid[i + 1][j] == 0) + res += (j + 1 >= n or grid[i][j + 1] == 0) + res += (i - 1 < 0 or grid[i - 1][j] == 0) + res += (j - 1 < 0 or grid[i][j - 1] == 0) + return res +``` + +```java +public class Solution { + public int islandPerimeter(int[][] grid) { + int m = grid.length, n = grid[0].length, res = 0; + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + if (grid[i][j] == 1) { + res += (i + 1 >= m || grid[i + 1][j] == 0) ? 1 : 0; + res += (j + 1 >= n || grid[i][j + 1] == 0) ? 1 : 0; + res += (i - 1 < 0 || grid[i - 1][j] == 0) ? 1 : 0; + res += (j - 1 < 0 || grid[i][j - 1] == 0) ? 1 : 0; + } + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int islandPerimeter(vector>& grid) { + int m = grid.size(), n = grid[0].size(), res = 0; + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + if (grid[i][j] == 1) { + res += (i + 1 >= m || grid[i + 1][j] == 0) ? 1 : 0; + res += (j + 1 >= n || grid[i][j + 1] == 0) ? 1 : 0; + res += (i - 1 < 0 || grid[i - 1][j] == 0) ? 1 : 0; + res += (j - 1 < 0 || grid[i][j - 1] == 0) ? 1 : 0; + } + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} grid + * @return {number} + */ + islandPerimeter(grid) { + const m = grid.length, n = grid[0].length; + let res = 0; + for (let i = 0; i < m; i++) { + for (let j = 0; j < n; j++) { + if (grid[i][j] === 1) { + res += (i + 1 >= m || grid[i + 1][j] === 0) ? 1 : 0; + res += (j + 1 >= n || grid[i][j + 1] === 0) ? 1 : 0; + res += (i - 1 < 0 || grid[i - 1][j] === 0) ? 1 : 0; + res += (j - 1 < 0 || grid[i][j - 1] === 0) ? 1 : 0; + } + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n)$ +* Space complexity: $O(1)$ extra space. + +> Where $m$ is the number of rows and $n$ is the number of columns in the grid. + +--- + +## 4. Iteration - II + +::tabs-start + +```python +class Solution: + def islandPerimeter(self, grid: List[List[int]]) -> int: + m, n = len(grid), len(grid[0]) + res = 0 + for r in range(m): + for c in range(n): + if grid[r][c] == 1: + res += 4 + if r and grid[r - 1][c]: + res -= 2 + if c and grid[r][c - 1] == 1: + res -= 2 + return res +``` + +```java +public class Solution { + public int islandPerimeter(int[][] grid) { + int m = grid.length, n = grid[0].length; + int res = 0;; + for (int r = 0; r < m; r++) { + for (int c = 0; c < n; c++) { + if (grid[r][c] == 1) { + res += 4; + if (r > 0 && grid[r - 1][c] == 1) { + res -= 2; + } + if (c > 0 && grid[r][c - 1] == 1) { + res -= 2; + } + } + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int islandPerimeter(vector>& grid) { + int m = grid.size(), n = grid[0].size(); + int res = 0; + for (int r = 0; r < m; r++) { + for (int c = 0; c < n; c++) { + if (grid[r][c]) { + res += 4; + if (r && grid[r - 1][c]) { + res -= 2; + } + if (c && grid[r][c - 1]) { + res -= 2; + } + } + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} grid + * @return {number} + */ + islandPerimeter(grid) { + const m = grid.length, n = grid[0].length; + let res = 0; + for (let r = 0; r < m; r++) { + for (let c = 0; c < n; c++) { + if (grid[r][c] == 1) { + res += 4; + if (r > 0 && grid[r - 1][c] == 1) { + res -= 2; + } + if (c > 0 && grid[r][c - 1] == 1) { + res -= 2; + } + } + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n)$ +* Space complexity: $O(1)$ extra space. + +> Where $m$ is the number of rows and $n$ is the number of columns in the grid. \ No newline at end of file diff --git a/articles/maximum-odd-binary-number.md b/articles/maximum-odd-binary-number.md new file mode 100644 index 000000000..41846d673 --- /dev/null +++ b/articles/maximum-odd-binary-number.md @@ -0,0 +1,264 @@ +## 1. Sorting + +::tabs-start + +```python +class Solution: + def maximumOddBinaryNumber(self, s: str) -> str: + s = sorted(s) + s.reverse() + i = len(s) - 1 + while i >= 0 and s[i] == "0": + i -= 1 + + s[i], s[len(s) - 1] = s[len(s) - 1], s[i] + return ''.join(s) +``` + +```java +public class Solution { + public String maximumOddBinaryNumber(String s) { + char[] arr = s.toCharArray(); + Arrays.sort(arr); + reverse(arr); + + int n = arr.length; + int i = n - 1; + + while (i >= 0 && arr[i] == '0') { + i--; + } + + char temp = arr[i]; + arr[i] = arr[n - 1]; + arr[n - 1] = temp; + + return new String(arr); + } + + private void reverse(char[] arr) { + int left = 0, right = arr.length - 1; + while (left < right) { + char temp = arr[left]; + arr[left++] = arr[right]; + arr[right--] = temp; + } + } +} +``` + +```cpp +class Solution { +public: + string maximumOddBinaryNumber(string s) { + sort(s.begin(), s.end(), greater()); + + int n = s.size(), i = n - 1; + while (i >= 0 && s[i] == '0') { + i--; + } + + swap(s[i], s[n - 1]); + return s; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {string} + */ + maximumOddBinaryNumber(s) { + let arr = s.split(''); + arr.sort().reverse(); + + let i = arr.length - 1; + while (i >= 0 && arr[i] === '0') { + i--; + } + + [arr[i], arr[arr.length - 1]] = [arr[arr.length - 1], arr[i]]; + return arr.join(''); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Greedy + +::tabs-start + +```python +class Solution: + def maximumOddBinaryNumber(self, s: str) -> str: + count = 0 + for c in s: + if c == "1": + count += 1 + + return (count - 1) * "1" + (len(s) - count) * "0" + "1" +``` + +```java +public class Solution { + public String maximumOddBinaryNumber(String s) { + int count = 0; + for (char c : s.toCharArray()) { + if (c == '1') count++; + } + + StringBuilder result = new StringBuilder(); + for (int i = 0; i < count - 1; i++) result.append('1'); + for (int i = 0; i < s.length() - count; i++) result.append('0'); + result.append('1'); + + return result.toString(); + } +} +``` + +```cpp +class Solution { +public: + string maximumOddBinaryNumber(string s) { + int count = 0; + for (char c : s) { + if (c == '1') count++; + } + + string result((count - 1), '1'); + result += string(s.length() - count, '0'); + result += '1'; + + return result; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {string} + */ + maximumOddBinaryNumber(s) { + let count = 0; + for (const c of s) { + if (c === '1') count++; + } + + return '1'.repeat(count - 1) + '0'.repeat(s.length - count) + '1'; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Two Pointers + +::tabs-start + +```python +class Solution: + def maximumOddBinaryNumber(self, s: str) -> str: + s = [c for c in s] + left = 0 + + for i in range(len(s)): + if s[i] == "1": + s[i], s[left] = s[left], s[i] + left += 1 + s[left - 1], s[len(s) - 1] = s[len(s) - 1], s[left - 1] + return "".join(s) +``` + +```java +public class Solution { + public String maximumOddBinaryNumber(String s) { + char[] arr = s.toCharArray(); + int left = 0; + + for (int i = 0; i < arr.length; i++) { + if (arr[i] == '1') { + char temp = arr[left]; + arr[left] = arr[i]; + arr[i] = temp; + left++; + } + } + + char temp = arr[left - 1]; + arr[left - 1] = arr[arr.length - 1]; + arr[arr.length - 1] = temp; + + return new String(arr); + } +} +``` + +```cpp +class Solution { +public: + string maximumOddBinaryNumber(string s) { + vector arr(s.begin(), s.end()); + int left = 0; + + for (int i = 0; i < arr.size(); i++) { + if (arr[i] == '1') { + swap(arr[left], arr[i]); + left++; + } + } + + swap(arr[left - 1], arr[arr.size() - 1]); + return string(arr.begin(), arr.end()); + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {string} + */ + maximumOddBinaryNumber(s) { + let arr = s.split(''); + let left = 0; + + for (let i = 0; i < arr.length; i++) { + if (arr[i] === '1') { + [arr[left], arr[i]] = [arr[i], arr[left]]; + left++; + } + } + + [arr[left - 1], arr[arr.length - 1]] = [arr[arr.length - 1], arr[left - 1]]; + return arr.join(''); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/merge-in-between-linked-lists.md b/articles/merge-in-between-linked-lists.md new file mode 100644 index 000000000..2d8a1774f --- /dev/null +++ b/articles/merge-in-between-linked-lists.md @@ -0,0 +1,460 @@ +## 1. Convert To Array + +::tabs-start + +```python +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, val=0, next=None): +# self.val = val +# self.next = next +class Solution: + def mergeInBetween(self, list1: ListNode, a: int, b: int, list2: ListNode) -> ListNode: + cur = list1 + arr = [] + while cur: + arr.append(cur) + cur = cur.next + arr[a - 1].next = list2 + + cur = list2 + while cur.next: + cur = cur.next + + cur.next = arr[b + 1] + return list1 +``` + +```java +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode() {} + * ListNode(int val) { this.val = val; } + * ListNode(int val, ListNode next) { this.val = val; this.next = next; } + * } + */ +public class Solution { + public ListNode mergeInBetween(ListNode list1, int a, int b, ListNode list2) { + ListNode cur = list1; + List arr = new ArrayList<>(); + + while (cur != null) { + arr.add(cur); + cur = cur.next; + } + + arr.get(a - 1).next = list2; + cur = list2; + + while (cur.next != null) { + cur = cur.next; + } + + cur.next = arr.get(b + 1); + return list1; + } +} +``` + +```cpp +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode() : val(0), next(nullptr) {} + * ListNode(int x) : val(x), next(nullptr) {} + * ListNode(int x, ListNode *next) : val(x), next(next) {} + * }; + */ +class Solution { +public: + ListNode* mergeInBetween(ListNode* list1, int a, int b, ListNode* list2) { + ListNode* cur = list1; + vector arr; + + while (cur) { + arr.push_back(cur); + cur = cur->next; + } + + arr[a - 1]->next = list2; + cur = list2; + + while (cur->next) { + cur = cur->next; + } + + cur->next = arr[b + 1]; + return list1; + } +}; +``` + +```javascript +/** + * Definition for singly-linked list. + * class ListNode { + * constructor(val = 0, next = null) { + * this.val = val; + * this.next = next; + * } + * } + */ +class Solution { + /** + * @param {ListNode} list1 + * @param {number} a + * @param {number} b + * @param {ListNode} list2 + * @return {ListNode} + */ + mergeInBetween(list1, a, b, list2) { + let cur = list1; + let arr = []; + + while (cur) { + arr.push(cur); + cur = cur.next; + } + + arr[a - 1].next = list2; + cur = list2; + + while (cur.next) { + cur = cur.next; + } + + cur.next = arr[b + 1]; + return list1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n + m)$ +* Space complexity: $O(n)$ + +> Where $n$ is the length of the first list and $m$ is the length of the second list. + +--- + +## 2. Two Pointers + +::tabs-start + +```python +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, val=0, next=None): +# self.val = val +# self.next = next +class Solution: + def mergeInBetween(self, list1: ListNode, a: int, b: int, list2: ListNode) -> ListNode: + curr = list1 + i = 0 + + while i < a - 1: + curr = curr.next + i += 1 + head = curr + + while i <= b: + curr = curr.next + i += 1 + + head.next = list2 + + while list2.next: + list2 = list2.next + list2.next = curr + + return list1 +``` + +```java +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode() {} + * ListNode(int val) { this.val = val; } + * ListNode(int val, ListNode next) { this.val = val; this.next = next; } + * } + */ +public class Solution { + public ListNode mergeInBetween(ListNode list1, int a, int b, ListNode list2) { + ListNode curr = list1; + int i = 0; + + while (i < a - 1) { + curr = curr.next; + i++; + } + ListNode head = curr; + + while (i <= b) { + curr = curr.next; + i++; + } + + head.next = list2; + + while (list2.next != null) { + list2 = list2.next; + } + list2.next = curr; + + return list1; + } +} +``` + +```cpp +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode() : val(0), next(nullptr) {} + * ListNode(int x) : val(x), next(nullptr) {} + * ListNode(int x, ListNode *next) : val(x), next(next) {} + * }; + */ +class Solution { +public: + ListNode* mergeInBetween(ListNode* list1, int a, int b, ListNode* list2) { + ListNode* curr = list1; + int i = 0; + + while (i < a - 1) { + curr = curr->next; + i++; + } + ListNode* head = curr; + + while (i <= b) { + curr = curr->next; + i++; + } + head->next = list2; + + while (list2->next) { + list2 = list2->next; + } + list2->next = curr; + + return list1; + } +}; +``` + +```javascript +/** + * Definition for singly-linked list. + * class ListNode { + * constructor(val = 0, next = null) { + * this.val = val; + * this.next = next; + * } + * } + */ +class Solution { + /** + * @param {ListNode} list1 + * @param {number} a + * @param {number} b + * @param {ListNode} list2 + * @return {ListNode} + */ + mergeInBetween(list1, a, b, list2) { + let curr = list1, i = 0; + + while (i < a - 1) { + curr = curr.next; + i++; + } + let head = curr; + + while (i <= b) { + curr = curr.next; + i++; + } + head.next = list2; + + while (list2.next) { + list2 = list2.next; + } + list2.next = curr; + + return list1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n + m)$ +* Space complexity: $O(1)$ extra space. + +> Where $n$ is the length of the first list and $m$ is the length of the second list. + +--- + +## 3. Recursion + +::tabs-start + +```python +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, val=0, next=None): +# self.val = val +# self.next = next +class Solution: + def mergeInBetween(self, list1: ListNode, a: int, b: int, list2: ListNode) -> ListNode: + if a == 1 : + nxt = list1.next + list1.next = list2 + while list2.next: + list2 = list2.next + self.mergeInBetween(nxt, 0, b - 1, list2) + return list1 + + if b == 0: + list2.next = list1.next + return list1 + + self.mergeInBetween(list1.next, a - 1, b - 1, list2) + return list1 +``` + +```java +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode() {} + * ListNode(int val) { this.val = val; } + * ListNode(int val, ListNode next) { this.val = val; this.next = next; } + * } + */ +public class Solution { + public ListNode mergeInBetween(ListNode list1, int a, int b, ListNode list2) { + if (a == 1) { + ListNode nxt = list1.next; + list1.next = list2; + + while (list2.next != null) { + list2 = list2.next; + } + mergeInBetween(nxt, 0, b - 1, list2); + return list1; + } + + if (b == 0) { + list2.next = list1.next; + return list1; + } + + mergeInBetween(list1.next, a - 1, b - 1, list2); + return list1; + } +} +``` + +```cpp +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode() : val(0), next(nullptr) {} + * ListNode(int x) : val(x), next(nullptr) {} + * ListNode(int x, ListNode *next) : val(x), next(next) {} + * }; + */ +class Solution { +public: + ListNode* mergeInBetween(ListNode* list1, int a, int b, ListNode* list2) { + if (a == 1) { + ListNode* nxt = list1->next; + list1->next = list2; + + while (list2->next) { + list2 = list2->next; + } + mergeInBetween(nxt, 0, b - 1, list2); + return list1; + } + + if (b == 0) { + list2->next = list1->next; + return list1; + } + + mergeInBetween(list1->next, a - 1, b - 1, list2); + return list1; + } +}; +``` + +```javascript +/** + * Definition for singly-linked list. + * class ListNode { + * constructor(val = 0, next = null) { + * this.val = val; + * this.next = next; + * } + * } + */ +class Solution { + /** + * @param {ListNode} list1 + * @param {number} a + * @param {number} b + * @param {ListNode} list2 + * @return {ListNode} + */ + mergeInBetween(list1, a, b, list2) { + if (a === 1) { + let nxt = list1.next; + list1.next = list2; + + while (list2.next) { + list2 = list2.next; + } + this.mergeInBetween(nxt, 0, b - 1, list2); + return list1; + } + + if (b === 0) { + list2.next = list1.next; + return list1; + } + + this.mergeInBetween(list1.next, a - 1, b - 1, list2); + return list1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n + m)$ +* Space complexity: $O(n)$ for recursion stack. + +> Where $n$ is the length of the first list and $m$ is the length of the second list. \ No newline at end of file diff --git a/articles/middle-of-the-linked-list.md b/articles/middle-of-the-linked-list.md new file mode 100644 index 000000000..935efc3d4 --- /dev/null +++ b/articles/middle-of-the-linked-list.md @@ -0,0 +1,339 @@ +## 1. Convert To Array + +::tabs-start + +```python +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, val=0, next=None): +# self.val = val +# self.next = next +class Solution: + def middleNode(self, head: Optional[ListNode]) -> Optional[ListNode]: + cur = head + arr = [] + while cur: + arr.append(cur) + cur = cur.next + return arr[len(arr) // 2] +``` + +```java +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode() {} + * ListNode(int val) { this.val = val; } + * ListNode(int val, ListNode next) { this.val = val; this.next = next; } + * } + */ +public class Solution { + public ListNode middleNode(ListNode head) { + ArrayList arr = new ArrayList<>(); + ListNode cur = head; + while (cur != null) { + arr.add(cur); + cur = cur.next; + } + return arr.get(arr.size() / 2); + } +} +``` + +```cpp +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode() : val(0), next(nullptr) {} + * ListNode(int x) : val(x), next(nullptr) {} + * ListNode(int x, ListNode *next) : val(x), next(next) {} + * }; + */ +class Solution { +public: + ListNode* middleNode(ListNode* head) { + vector arr; + ListNode* cur = head; + while (cur) { + arr.push_back(cur); + cur = cur->next; + } + return arr[arr.size() / 2]; + } +}; +``` + +```javascript +/** + * Definition for singly-linked list. + * class ListNode { + * constructor(val = 0, next = null) { + * this.val = val; + * this.next = next; + * } + * } + */ +class Solution { + /** + * @param {ListNode} head + * @return {ListNode} + */ + middleNode(head) { + let arr = []; + let cur = head; + while (cur) { + arr.push(cur); + cur = cur.next; + } + return arr[Math.floor(arr.length / 2)]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Find Length of the List + +::tabs-start + +```python +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, val=0, next=None): +# self.val = val +# self.next = next +class Solution: + def middleNode(self, head: Optional[ListNode]) -> Optional[ListNode]: + n, cur = 0, head + while cur: + cur = cur.next + n += 1 + + n //= 2 + cur = head + while n: + n -= 1 + cur = cur.next + return cur +``` + +```java +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode() {} + * ListNode(int val) { this.val = val; } + * ListNode(int val, ListNode next) { this.val = val; this.next = next; } + * } + */ +public class Solution { + public ListNode middleNode(ListNode head) { + int n = 0; + ListNode cur = head; + while (cur != null) { + cur = cur.next; + n++; + } + + n /= 2; + cur = head; + while (n != 0) { + n--; + cur = cur.next; + } + return cur; + } +} +``` + +```cpp +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode() : val(0), next(nullptr) {} + * ListNode(int x) : val(x), next(nullptr) {} + * ListNode(int x, ListNode *next) : val(x), next(next) {} + * }; + */ +class Solution { +public: + ListNode* middleNode(ListNode* head) { + int n = 0; + ListNode* cur = head; + while (cur) { + cur = cur->next; + n++; + } + + n /= 2; + cur = head; + while (n) { + n--; + cur = cur->next; + } + return cur; + } +}; +``` + +```javascript +/** + * Definition for singly-linked list. + * class ListNode { + * constructor(val = 0, next = null) { + * this.val = val; + * this.next = next; + * } + * } + */ +class Solution { + /** + * @param {ListNode} head + * @return {ListNode} + */ + middleNode(head) { + let n = 0; + let cur = head; + while (cur) { + cur = cur.next; + n++; + } + + n = Math.floor(n / 2); + cur = head; + while (n) { + n--; + cur = cur.next; + } + return cur; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. + +--- + +## 3. Fast & Slow Pointers + +::tabs-start + +```python +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, val=0, next=None): +# self.val = val +# self.next = next +class Solution: + def middleNode(self, head: Optional[ListNode]) -> Optional[ListNode]: + slow, fast = head, head + + while fast and fast.next: + slow = slow.next + fast = fast.next.next + return slow +``` + +```java +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode() {} + * ListNode(int val) { this.val = val; } + * ListNode(int val, ListNode next) { this.val = val; this.next = next; } + * } + */ +public class Solution { + public ListNode middleNode(ListNode head) { + ListNode slow = head, fast = head; + + while (fast != null && fast.next != null) { + slow = slow.next; + fast = fast.next.next; + } + return slow; + } +} +``` + +```cpp +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode() : val(0), next(nullptr) {} + * ListNode(int x) : val(x), next(nullptr) {} + * ListNode(int x, ListNode *next) : val(x), next(next) {} + * }; + */ +class Solution { +public: + ListNode* middleNode(ListNode* head) { + ListNode* slow = head; + ListNode* fast = head; + + while (fast && fast->next) { + slow = slow->next; + fast = fast->next->next; + } + return slow; + } +}; +``` + +```javascript +/** + * Definition for singly-linked list. + * class ListNode { + * constructor(val = 0, next = null) { + * this.val = val; + * this.next = next; + * } + * } + */ +class Solution { + /** + * @param {ListNode} head + * @return {ListNode} + */ + middleNode(head) { + let slow = head, fast = head; + + while (fast && fast.next) { + slow = slow.next; + fast = fast.next.next; + } + return slow; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. \ No newline at end of file diff --git a/articles/open-the-lock.md b/articles/open-the-lock.md new file mode 100644 index 000000000..6ded4c7ee --- /dev/null +++ b/articles/open-the-lock.md @@ -0,0 +1,501 @@ +## 1. Breadth First Search - I + +::tabs-start + +```python +class Solution: + def openLock(self, deadends: List[str], target: str) -> int: + if "0000" in deadends: + return -1 + + def children(lock): + res = [] + for i in range(4): + digit = str((int(lock[i]) + 1) % 10) + res.append(lock[:i] + digit + lock[i+1:]) + digit = str((int(lock[i]) - 1 + 10) % 10) + res.append(lock[:i] + digit + lock[i+1:]) + return res + + q = deque([("0000", 0)]) + visit = set(deadends) + + while q: + lock, turns = q.popleft() + if lock == target: + return turns + for child in children(lock): + if child not in visit: + visit.add(child) + q.append((child, turns + 1)) + return -1 +``` + +```java +public class Solution { + public int openLock(String[] deadends, String target) { + Set visit = new HashSet<>(Arrays.asList(deadends)); + if (visit.contains("0000")) return -1; + + Queue queue = new LinkedList<>(); + queue.offer("0000"); + visit.add("0000"); + + int turns = 0; + while (!queue.isEmpty()) { + int size = queue.size(); + for (int i = 0; i < size; i++) { + String lock = queue.poll(); + if (lock.equals(target)) return turns; + + for (String next : children(lock)) { + if (!visit.contains(next)) { + queue.offer(next); + visit.add(next); + } + } + } + turns++; + } + return -1; + } + + private List children(String lock) { + List res = new ArrayList<>(); + for (int i = 0; i < 4; i++) { + char[] arr = lock.toCharArray(); + arr[i] = (char) (((arr[i] - '0' + 1) % 10) + '0'); + res.add(new String(arr)); + + arr = lock.toCharArray(); + arr[i] = (char) (((arr[i] - '0' - 1 + 10) % 10) + '0'); + res.add(new String(arr)); + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int openLock(vector& deadends, string target) { + unordered_set visit(deadends.begin(), deadends.end()); + if (visit.count("0000")) return -1; + + queue> q; + q.push({"0000", 0}); + visit.insert("0000"); + + while (!q.empty()) { + auto [lock, turns] = q.front(); + q.pop(); + + if (lock == target) return turns; + for (string child : children(lock)) { + if (!visit.count(child)) { + visit.insert(child); + q.push({child, turns + 1}); + } + } + } + return -1; + } + +private: + vector children(string lock) { + vector res; + for (int i = 0; i < 4; ++i) { + string next = lock; + next[i] = (next[i] - '0' + 1) % 10 + '0'; + res.push_back(next); + + next = lock; + next[i] = (next[i] - '0' - 1 + 10) % 10 + '0'; + res.push_back(next); + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} deadends + * @param {string} target + * @return {number} + */ + openLock(deadends, target) { + const visit = new Set(deadends); + if (visit.has("0000")) return -1; + + const children = (lock) => { + const res = []; + for (let i = 0; i < 4; i++) { + const up = lock.slice(0, i) + ((+lock[i] + 1) % 10) + lock.slice(i + 1); + const down = lock.slice(0, i) + ((+lock[i] - 1 + 10) % 10) + lock.slice(i + 1); + res.push(up, down); + } + return res; + }; + + const queue = new Queue([["0000", 0]]); + visit.add("0000"); + + while (!queue.isEmpty()) { + const [lock, turns] = queue.pop(); + if (lock === target) return turns; + + for (const child of children(lock)) { + if (!visit.has(child)) { + visit.add(child); + queue.push([child, turns + 1]); + } + } + } + return -1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(d ^ n + m)$ +* Space complexity: $O(d ^ n)$ + +> Where $d$ is the number of digits $(0 - 9)$, $n$ is the number of wheels $(4)$, and $m$ is the number of deadends. + +--- + +## 2. Breadth First Search - II + +::tabs-start + +```python +class Solution: + def openLock(self, deadends: List[str], target: str) -> int: + if target == "0000": + return 0 + + visit = set(deadends) + if "0000" in visit: + return -1 + + q = deque(["0000"]) + visit.add("0000") + steps = 0 + + while q: + steps += 1 + for _ in range(len(q)): + lock = q.popleft() + for i in range(4): + for j in [1, -1]: + digit = str((int(lock[i]) + j + 10) % 10) + nextLock = lock[:i] + digit + lock[i+1:] + if nextLock in visit: + continue + if nextLock == target: + return steps + q.append(nextLock) + visit.add(nextLock) + return -1 +``` + +```java +public class Solution { + public int openLock(String[] deadends, String target) { + if (target.equals("0000")) return 0; + + Set visit = new HashSet<>(Arrays.asList(deadends)); + if (visit.contains("0000")) return -1; + + Queue q = new LinkedList<>(); + q.offer("0000"); + visit.add("0000"); + int steps = 0; + + while (!q.isEmpty()) { + steps++; + for (int i = q.size(); i > 0; i--) { + String lock = q.poll(); + for (int j = 0; j < 4; j++) { + for (int move : new int[]{1, -1}) { + char[] arr = lock.toCharArray(); + arr[j] = (char)((arr[j] - '0' + move + 10) % 10 + '0'); + String nextLock = new String(arr); + if (visit.contains(nextLock)) continue; + if (nextLock.equals(target)) return steps; + q.offer(nextLock); + visit.add(nextLock); + } + } + } + } + return -1; + } +} +``` + +```cpp +class Solution { +public: + int openLock(vector& deadends, string target) { + if (target == "0000") return 0; + + unordered_set visit(deadends.begin(), deadends.end()); + if (visit.count("0000")) return -1; + + queue q; + q.push("0000"); + visit.insert("0000"); + int steps = 0; + + while (!q.empty()) { + steps++; + for (int i = q.size(); i > 0; i--) { + string lock = q.front(); q.pop(); + for (int j = 0; j < 4; j++) { + for (int move : {1, -1}) { + string nextLock = lock; + nextLock[j] = (nextLock[j] - '0' + move + 10) % 10 + '0'; + if (visit.count(nextLock)) continue; + if (nextLock == target) return steps; + q.push(nextLock); + visit.insert(nextLock); + } + } + } + } + return -1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} deadends + * @param {string} target + * @return {number} + */ + openLock(deadends, target) { + if (target === "0000") return 0; + + const visit = new Set(deadends); + if (visit.has("0000")) return -1; + + const q = new Queue(["0000"]); + visit.add("0000"); + let steps = 0; + + while (!q.isEmpty()) { + steps++; + for (let i = q.size(); i > 0; i--) { + const lock = q.pop(); + for (let j = 0; j < 4; j++) { + for (let move of [1, -1]) { + const digit = (parseInt(lock[j]) + move + 10) % 10; + const nextLock = lock.slice(0, j) + digit + lock.slice(j + 1); + if (visit.has(nextLock)) continue; + if (nextLock === target) return steps; + q.push(nextLock); + visit.add(nextLock); + } + } + } + } + return -1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(d ^ n + m)$ +* Space complexity: $O(d ^ n)$ + +> Where $d$ is the number of digits $(0 - 9)$, $n$ is the number of wheels $(4)$, and $m$ is the number of deadends. + +--- + +## 3. Bidirectional Breadth First Search + +::tabs-start + +```python +class Solution: + def openLock(self, deadends: List[str], target: str) -> int: + if target == "0000": + return 0 + + visit = set(deadends) + if "0000" in visit: + return -1 + + begin = {"0000"} + end = {target} + steps = 0 + + while begin and end: + if len(begin) > len(end): + begin, end = end, begin + + steps += 1 + temp = set() + for lock in begin: + for i in range(4): + for j in [-1, 1]: + digit = str((int(lock[i]) + j + 10) % 10) + nextLock = lock[:i] + digit + lock[i+1:] + + if nextLock in end: + return steps + if nextLock in visit: + continue + visit.add(nextLock) + temp.add(nextLock) + begin = temp + return -1 +``` + +```java +public class Solution { + public int openLock(String[] deadends, String target) { + if (target.equals("0000")) return 0; + + Set visit = new HashSet<>(Arrays.asList(deadends)); + if (visit.contains("0000")) return -1; + + Set begin = new HashSet<>(); + begin.add("0000"); + Set end = new HashSet<>(); + end.add(target); + int steps = 0; + + while (!begin.isEmpty() && !end.isEmpty()) { + if (begin.size() > end.size()) { + Set temp = begin; + begin = end; + end = temp; + } + + steps++; + Set temp = new HashSet<>(); + for (String lock : begin) { + for (int i = 0; i < 4; i++) { + for (int j : new int[]{-1, 1}) { + char[] chars = lock.toCharArray(); + chars[i] = (char) ((chars[i] - '0' + j + 10) % 10 + '0'); + String nextLock = new String(chars); + + if (end.contains(nextLock)) return steps; + if (visit.contains(nextLock)) continue; + + visit.add(nextLock); + temp.add(nextLock); + } + } + } + begin = temp; + } + return -1; + } +} +``` + +```cpp +class Solution { +public: + int openLock(vector& deadends, string target) { + if (target == "0000") return 0; + + unordered_set visit(deadends.begin(), deadends.end()); + if (visit.count("0000")) return -1; + + unordered_set begin = {"0000"}, end = {target}; + int steps = 0; + + while (!begin.empty() && !end.empty()) { + if (begin.size() > end.size()) swap(begin, end); + steps++; + unordered_set temp; + + for (const string& lock : begin) { + for (int i = 0; i < 4; ++i) { + for (int j : {-1, 1}) { + string nextLock = lock; + nextLock[i] = (nextLock[i] - '0' + j + 10) % 10 + '0'; + + if (end.count(nextLock)) return steps; + if (visit.count(nextLock)) continue; + + visit.insert(nextLock); + temp.insert(nextLock); + } + } + } + begin = temp; + } + return -1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} deadends + * @param {string} target + * @return {number} + */ + openLock(deadends, target) { + if (target === "0000") return 0; + + const visit = new Set(deadends); + if (visit.has("0000")) return -1; + + let begin = new Set(["0000"]); + let end = new Set([target]); + let steps = 0; + + while (begin.size > 0 && end.size > 0) { + if (begin.size > end.size) [begin, end] = [end, begin]; + + steps++; + const temp = new Set(); + + for (const lock of begin) { + for (let i = 0; i < 4; i++) { + for (const j of [-1, 1]) { + const digit = (parseInt(lock[i]) + j + 10) % 10; + const nextLock = lock.slice(0, i) + digit + lock.slice(i + 1); + + if (end.has(nextLock)) return steps; + if (visit.has(nextLock)) continue; + + visit.add(nextLock); + temp.add(nextLock); + } + } + } + begin = temp; + } + return -1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(d ^ n + m)$ +* Space complexity: $O(d ^ n)$ + +> Where $d$ is the number of digits $(0 - 9)$, $n$ is the number of wheels $(4)$, and $m$ is the number of deadends. \ No newline at end of file diff --git a/articles/palindrome-linked-list.md b/articles/palindrome-linked-list.md new file mode 100644 index 000000000..f4f73b7bc --- /dev/null +++ b/articles/palindrome-linked-list.md @@ -0,0 +1,600 @@ +## 1. Convert To Array + +::tabs-start + +```python +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, val=0, next=None): +# self.val = val +# self.next = next +class Solution: + def isPalindrome(self, head: Optional[ListNode]) -> bool: + arr = [] + cur = head + while cur: + arr.append(cur.val) + cur = cur.next + + l, r = 0, len(arr) - 1 + while l < r: + if arr[l] != arr[r]: + return False + l, r = l + 1, r - 1 + + return True +``` + +```java +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode() {} + * ListNode(int val) { this.val = val; } + * ListNode(int val, ListNode next) { this.val = val; this.next = next; } + * } + */ +public class Solution { + public boolean isPalindrome(ListNode head) { + List arr = new ArrayList<>(); + ListNode cur = head; + while (cur != null) { + arr.add(cur.val); + cur = cur.next; + } + + int l = 0, r = arr.size() - 1; + while (l < r) { + if (!arr.get(l).equals(arr.get(r))) { + return false; + } + l++; + r--; + } + + return true; + } +} +``` + +```cpp +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode() : val(0), next(nullptr) {} + * ListNode(int x) : val(x), next(nullptr) {} + * ListNode(int x, ListNode *next) : val(x), next(next) {} + * }; + */ +class Solution { +public: + bool isPalindrome(ListNode* head) { + std::vector arr; + ListNode* cur = head; + while (cur) { + arr.push_back(cur->val); + cur = cur->next; + } + + int l = 0, r = arr.size() - 1; + while (l < r) { + if (arr[l] != arr[r]) { + return false; + } + l++; + r--; + } + + return true; + } +}; +``` + +```javascript +/** + * Definition for singly-linked list. + * class ListNode { + * constructor(val = 0, next = null) { + * this.val = val; + * this.next = next; + * } + * } + */ +class Solution { + /** + * @param {ListNode} head + * @return {boolean} + */ + isPalindrome(head) { + const arr = []; + let cur = head; + + while (cur) { + arr.push(cur.val); + cur = cur.next; + } + + let l = 0, r = arr.length - 1; + while (l < r) { + if (arr[l] !== arr[r]) { + return false; + } + l++; + r--; + } + + return true; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Recursion + +::tabs-start + +```python +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, val=0, next=None): +# self.val = val +# self.next = next +class Solution: + def isPalindrome(self, head: Optional[ListNode]) -> bool: + self.cur = head + + def rec(node): + if node is not None: + if not rec(node.next): + return False + if self.cur.val != node.val: + return False + self.cur = self.cur.next + return True + + return rec(head) +``` + +```java +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode() {} + * ListNode(int val) { this.val = val; } + * ListNode(int val, ListNode next) { this.val = val; this.next = next; } + * } + */ +public class Solution { + private ListNode cur; + + public boolean isPalindrome(ListNode head) { + cur = head; + return rec(head); + } + + private boolean rec(ListNode node) { + if (node != null) { + if (!rec(node.next)) { + return false; + } + if (cur.val != node.val) { + return false; + } + cur = cur.next; + } + return true; + } +} +``` + +```cpp +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode() : val(0), next(nullptr) {} + * ListNode(int x) : val(x), next(nullptr) {} + * ListNode(int x, ListNode *next) : val(x), next(next) {} + * }; + */ +class Solution { + ListNode* cur; + + bool rec(ListNode* node) { + if (node != nullptr) { + if (!rec(node->next)) { + return false; + } + if (cur->val != node->val) { + return false; + } + cur = cur->next; + } + return true; + } + +public: + bool isPalindrome(ListNode* head) { + cur = head; + return rec(head); + } +}; +``` + +```javascript +/** + * Definition for singly-linked list. + * class ListNode { + * constructor(val = 0, next = null) { + * this.val = val; + * this.next = next; + * } + * } + */ +class Solution { + /** + * @param {ListNode} head + * @return {boolean} + */ + isPalindrome(head) { + let cur = head; + + const rec = (node) => { + if (node !== null) { + if (!rec(node.next)) { + return false; + } + if (cur.val !== node.val) { + return false; + } + cur = cur.next; + } + return true; + }; + + return rec(head); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ for recursion stack. + +--- + +## 3. Stack + +::tabs-start + +```python +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, val=0, next=None): +# self.val = val +# self.next = next +class Solution: + def isPalindrome(self, head: Optional[ListNode]) -> bool: + stack = [] + cur = head + + while cur: + stack.append(cur.val) + cur = cur.next + + cur = head + while cur and cur.val == stack.pop(): + cur = cur.next + + return not cur +``` + +```java +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode() {} + * ListNode(int val) { this.val = val; } + * ListNode(int val, ListNode next) { this.val = val; this.next = next; } + * } + */ +public class Solution { + public boolean isPalindrome(ListNode head) { + Stack stack = new Stack<>(); + ListNode cur = head; + + while (cur != null) { + stack.push(cur.val); + cur = cur.next; + } + + cur = head; + while (cur != null && cur.val == stack.pop()) { + cur = cur.next; + } + + return cur == null; + } +} +``` + +```cpp +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode() : val(0), next(nullptr) {} + * ListNode(int x) : val(x), next(nullptr) {} + * ListNode(int x, ListNode *next) : val(x), next(next) {} + * }; + */ +class Solution { +public: + bool isPalindrome(ListNode* head) { + stack stack; + ListNode* cur = head; + + while (cur != nullptr) { + stack.push(cur->val); + cur = cur->next; + } + + cur = head; + while (cur != nullptr && cur->val == stack.top()) { + stack.pop(); + cur = cur->next; + } + + return cur == nullptr; + } +}; +``` + +```javascript +/** + * Definition for singly-linked list. + * class ListNode { + * constructor(val = 0, next = null) { + * this.val = val; + * this.next = next; + * } + * } + */ +class Solution { + /** + * @param {ListNode} head + * @return {boolean} + */ + isPalindrome(head) { + const stack = []; + let cur = head; + + while (cur) { + stack.push(cur.val); + cur = cur.next; + } + + cur = head; + while (cur && cur.val === stack.pop()) { + cur = cur.next; + } + + return cur === null; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 4. Fast & Slow Pointers + +::tabs-start + +```python +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, val=0, next=None): +# self.val = val +# self.next = next +class Solution: + def isPalindrome(self, head: ListNode) -> bool: + fast = head + slow = head + + # find middle (slow) + while fast and fast.next: + fast = fast.next.next + slow = slow.next + + # reverse second half + prev = None + while slow: + tmp = slow.next + slow.next = prev + prev = slow + slow = tmp + + # check palindrome + left, right = head, prev + while right: + if left.val != right.val: + return False + left = left.next + right = right.next + + return True +``` + +```java +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode() {} + * ListNode(int val) { this.val = val; } + * ListNode(int val, ListNode next) { this.val = val; this.next = next; } + * } + */ +public class Solution { + public boolean isPalindrome(ListNode head) { + ListNode fast = head, slow = head; + + // find middle (slow) + while (fast != null && fast.next != null) { + fast = fast.next.next; + slow = slow.next; + } + + // reverse second half + ListNode prev = null; + while (slow != null) { + ListNode tmp = slow.next; + slow.next = prev; + prev = slow; + slow = tmp; + } + + // check palindrome + ListNode left = head, right = prev; + while (right != null) { + if (left.val != right.val) { + return false; + } + left = left.next; + right = right.next; + } + + return true; + } +} +``` + +```cpp +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode() : val(0), next(nullptr) {} + * ListNode(int x) : val(x), next(nullptr) {} + * ListNode(int x, ListNode *next) : val(x), next(next) {} + * }; + */ +class Solution { +public: + bool isPalindrome(ListNode* head) { + ListNode *fast = head, *slow = head; + + // find middle (slow) + while (fast && fast->next) { + fast = fast->next->next; + slow = slow->next; + } + + // reverse second half + ListNode *prev = nullptr; + while (slow) { + ListNode *tmp = slow->next; + slow->next = prev; + prev = slow; + slow = tmp; + } + + // check palindrome + ListNode *left = head, *right = prev; + while (right) { + if (left->val != right->val) { + return false; + } + left = left->next; + right = right->next; + } + + return true; + } +}; +``` + +```javascript +/** + * Definition for singly-linked list. + * class ListNode { + * constructor(val = 0, next = null) { + * this.val = val; + * this.next = next; + * } + * } + */ +class Solution { + /** + * @param {ListNode} head + * @return {boolean} + */ + isPalindrome(head) { + let fast = head, slow = head; + + // find middle (slow) + while (fast && fast.next) { + fast = fast.next.next; + slow = slow.next; + } + + // reverse second half + let prev = null; + while (slow) { + let tmp = slow.next; + slow.next = prev; + prev = slow; + slow = tmp; + } + + // check palindrome + let left = head, right = prev; + while (right) { + if (left.val !== right.val) { + return false; + } + left = left.next; + right = right.next; + } + + return true; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. \ No newline at end of file diff --git a/articles/permutations-ii.md b/articles/permutations-ii.md new file mode 100644 index 000000000..d4c0edd41 --- /dev/null +++ b/articles/permutations-ii.md @@ -0,0 +1,712 @@ +## 1. Backtracking (Hash Set) + +::tabs-start + +```python +class Solution: + def permuteUnique(self, nums: List[int]) -> List[List[int]]: + res = set() + + def backtrack(perm): + if len(perm) == len(nums): + res.add(tuple(perm)) + return + + for i in range(len(nums)): + if nums[i] != float("-inf"): + perm.append(nums[i]) + nums[i] = float("-inf") + backtrack(perm) + nums[i] = perm[-1] + perm.pop() + + backtrack([]) + return list(res) +``` + +```java +public class Solution { + private Set> res; + + public List> permuteUnique(int[] nums) { + res = new HashSet<>(); + List perm = new ArrayList<>(); + backtrack(nums, perm); + return new ArrayList<>(res); + } + + private void backtrack(int[] nums, List perm) { + if (perm.size() == nums.length) { + res.add(new ArrayList<>(perm)); + return; + } + + for (int i = 0; i < nums.length; i++) { + if (nums[i] != Integer.MIN_VALUE) { + int temp = nums[i]; + perm.add(nums[i]); + nums[i] = Integer.MIN_VALUE; + backtrack(nums, perm); + nums[i] = temp; + perm.remove(perm.size() - 1); + } + } + } +} +``` + +```cpp +class Solution { + set> res; +public: + vector> permuteUnique(vector& nums) { + vector perm; + backtrack(nums, perm); + return vector>(res.begin(), res.end()); + } + +private: + void backtrack(vector& nums, vector& perm) { + if (perm.size() == nums.size()) { + res.insert(perm); + return; + } + + for (int i = 0; i < nums.size(); ++i) { + if (nums[i] != INT_MIN) { + int temp = nums[i]; + perm.push_back(temp); + nums[i] = INT_MIN; + backtrack(nums, perm); + nums[i] = temp; + perm.pop_back(); + } + } + + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number[][]} + */ + permuteUnique(nums) { + const res = new Set(); + const perm = []; + + const backtrack = () => { + if (perm.length === nums.length) { + res.add(JSON.stringify(perm)); + return; + } + + for (let i = 0; i < nums.length; i++) { + if (nums[i] !== -Infinity) { + let temp = nums[i]; + perm.push(nums[i]); + nums[i] = -Infinity; + backtrack(); + nums[i] = temp; + perm.pop(); + } + } + }; + + backtrack(); + return Array.from(res).map(JSON.parse); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n! * n)$ +* Space complexity: $O(n! * n)$ for the hash set. + +--- + +## 2. Backtracking (Hash Map) + +::tabs-start + +```python +class Solution: + def permuteUnique(self, nums: List[int]) -> List[List[int]]: + res = [] + perm = [] + count = {n: 0 for n in nums} + for num in nums: + count[num] += 1 + + def dfs(): + if len(perm) == len(nums): + res.append(perm.copy()) + return + + for num in count: + if count[num] > 0: + perm.append(num) + count[num] -= 1 + dfs() + count[num] += 1 + perm.pop() + + dfs() + return res +``` + +```java +public class Solution { + private Map count; + private List> res; + + public List> permuteUnique(int[] nums) { + res = new ArrayList<>(); + count = new HashMap<>(); + List perm = new ArrayList<>(); + + for (int num : nums) { + count.put(num, count.getOrDefault(num, 0) + 1); + } + + dfs(nums, perm); + return res; + } + + private void dfs(int[] nums, List perm) { + if (perm.size() == nums.length) { + res.add(new ArrayList<>(perm)); + return; + } + + for (int num : count.keySet()) { + if (count.get(num) > 0) { + perm.add(num); + count.put(num, count.get(num) - 1); + dfs(nums, perm); + count.put(num, count.get(num) + 1); + perm.remove(perm.size() - 1); + } + } + } +} +``` + +```cpp +class Solution { + vector> res; + unordered_map count; + +public: + vector> permuteUnique(vector& nums) { + for (int& num : nums) { + count[num]++; + } + vector perm; + dfs(nums, perm); + return res; + } + + void dfs(vector& nums, vector& perm) { + if (perm.size() == nums.size()) { + res.push_back(perm); + return; + } + for (auto& [num, cnt] : count) { + if (cnt > 0) { + perm.push_back(num); + cnt--; + dfs(nums, perm); + cnt++; + perm.pop_back(); + } + } + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number[][]} + */ + permuteUnique(nums) { + const res = []; + const perm = []; + const count = {}; + + for (const num of nums) { + count[num] = (count[num] || 0) + 1; + } + + const dfs = () => { + if (perm.length === nums.length) { + res.push([...perm]); + return; + } + + for (const num in count) { + if (count[num] > 0) { + perm.push(+num); + count[num]--; + dfs(); + count[num]++; + perm.pop(); + } + } + }; + + dfs(); + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n! * n)$ +* Space complexity: $O(n! * n)$ for the output list. + +--- + +## 3. Backtracking (Boolean Array) + +::tabs-start + +```python +class Solution: + def permuteUnique(self, nums: List[int]) -> List[List[int]]: + res, n = [], len(nums) + visit = [False] * n + perm = [] + + def dfs(): + if len(perm) == n: + res.append(perm.copy()) + return + + for i in range(n): + if visit[i]: + continue + + if i and nums[i] == nums[i - 1] and not visit[i - 1]: + continue + visit[i] = True + perm.append(nums[i]) + dfs() + visit[i] = False + perm.pop() + + nums.sort() + dfs() + return res +``` + +```java +public class Solution { + private boolean[] visit; + private List> res; + + public List> permuteUnique(int[] nums) { + res = new ArrayList<>(); + visit = new boolean[nums.length]; + Arrays.sort(nums); + List perm = new ArrayList<>(); + dfs(nums, perm); + return res; + } + + private void dfs(int[] nums, List perm) { + if (perm.size() == nums.length) { + res.add(new ArrayList<>(perm)); + return; + } + + for (int i = 0; i < nums.length; i++) { + if (visit[i] || (i > 0 && nums[i] == nums[i - 1] && !visit[i - 1])) + continue; + + visit[i] = true; + perm.add(nums[i]); + dfs(nums, perm); + visit[i] = false; + perm.remove(perm.size() - 1); + } + } +} +``` + +```cpp +class Solution { + vector> res; + vector visit; + +public: + vector> permuteUnique(vector& nums) { + visit.assign(nums.size(), false); + vector perm; + sort(nums.begin(), nums.end()); + dfs(nums, perm); + return res; + } + + void dfs(vector& nums, vector& perm) { + if (perm.size() == nums.size()) { + res.push_back(perm); + return; + } + for (int i = 0; i < nums.size(); i++) { + if (visit[i] || (i > 0 && nums[i] == nums[i - 1] && !visit[i - 1])) + continue; + + visit[i] = true; + perm.push_back(nums[i]); + dfs(nums, perm); + visit[i] = false; + perm.pop_back(); + } + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number[][]} + */ + permuteUnique(nums) { + const res = []; + const visit = new Array(nums.length).fill(false); + const perm = []; + nums.sort((a, b) => a - b); + + const dfs = () => { + if (perm.length === nums.length) { + res.push([...perm]); + return; + } + + for (let i = 0; i < nums.length; i++) { + if (visit[i] || (i > 0 && nums[i] === nums[i - 1] && !visit[i - 1])) + continue; + + visit[i] = true; + perm.push(nums[i]); + dfs(); + visit[i] = false; + perm.pop(); + } + }; + + dfs(); + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n! * n)$ +* Space complexity: $O(n! * n)$ for the output list. + +--- + +## 4. Backtracking (Optimal) + +::tabs-start + +```python +class Solution: + def permuteUnique(self, nums: List[int]) -> List[List[int]]: + res = [] + + def dfs(i): + if i == len(nums): + res.append(nums.copy()) + return + + for j in range(i, len(nums)): + if j > i and nums[i] == nums[j]: + continue + + nums[i], nums[j] = nums[j], nums[i] + dfs(i + 1) + + for j in range(len(nums) - 1, i, -1): + nums[j], nums[i] = nums[i], nums[j] + + nums.sort() + dfs(0) + return res +``` + +```java +public class Solution { + private List> res; + + public List> permuteUnique(int[] nums) { + res = new ArrayList<>(); + Arrays.sort(nums); + dfs(0, nums); + return res; + } + + private void dfs(int i, int[] nums) { + if (i == nums.length) { + List temp = new ArrayList<>(); + for (int num : nums) temp.add(num); + res.add(temp); + return; + } + + for (int j = i; j < nums.length; j++) { + if (j > i && nums[j] == nums[i]) continue; + swap(nums, i, j); + dfs(i + 1, nums); + } + + for (int j = nums.length - 1; j > i; j--) { + swap(nums, i, j); + } + } + + private void swap(int[] nums, int i, int j) { + int temp = nums[i]; + nums[i] = nums[j]; + nums[j] = temp; + } +} +``` + +```cpp +class Solution { + vector> res; + +public: + vector> permuteUnique(vector& nums) { + sort(nums.begin(), nums.end()); + dfs(0, nums); + return res; + } + + void dfs(int i, vector& nums) { + if (i == nums.size()) { + res.push_back(nums); + return; + } + + for (int j = i; j < nums.size(); ++j) { + if (j > i && nums[j] == nums[i]) continue; + swap(nums[i], nums[j]); + dfs(i + 1, nums); + } + + for (int j = nums.size() - 1; j > i; --j) { + swap(nums[i], nums[j]); + } + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number[][]} + */ + permuteUnique(nums) { + const res = []; + nums.sort((a, b) => a - b); + + const dfs = (i) => { + if (i === nums.length) { + res.push([...nums]); + return; + } + + for (let j = i; j < nums.length; j++) { + if (j > i && nums[j] === nums[i]) continue; + [nums[i], nums[j]] = [nums[j], nums[i]]; + dfs(i + 1); + } + + for (let j = nums.length - 1; j > i; j--) { + [nums[i], nums[j]] = [nums[j], nums[i]]; + } + }; + + dfs(0); + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n! * n)$ +* Space complexity: $O(n! * n)$ for the output list. + +--- + +## 5. Iteration + +::tabs-start + +```python +class Solution: + def permuteUnique(self, nums: List[int]) -> List[List[int]]: + n = len(nums) + nums.sort() + res = [nums.copy()] + + while True: + i = n - 2 + while i >= 0 and nums[i] >= nums[i + 1]: + i -= 1 + + if i < 0: + break + + j = n - 1 + while nums[j] <= nums[i]: + j -= 1 + nums[i], nums[j] = nums[j], nums[i] + + l, r = i + 1, n - 1 + while l < r: + nums[l], nums[r] = nums[r], nums[l] + l, r = l + 1, r - 1 + + res.append(nums.copy()) + + return res +``` + +```java +public class Solution { + public List> permuteUnique(int[] nums) { + int n = nums.length; + Arrays.sort(nums); + List> res = new ArrayList<>(); + res.add(toList(nums)); + + while (true) { + int i = n - 2; + while (i >= 0 && nums[i] >= nums[i + 1]) i--; + + if (i < 0) break; + + int j = n - 1; + while (nums[j] <= nums[i]) j--; + + swap(nums, i, j); + reverse(nums, i + 1, n - 1); + res.add(toList(nums)); + } + + return res; + } + + private void reverse(int[] nums, int l, int r) { + while (l < r) { + swap(nums, l++, r--); + } + } + + private void swap(int[] nums, int i, int j) { + int temp = nums[i]; + nums[i] = nums[j]; + nums[j] = temp; + } + + private List toList(int[] nums) { + List list = new ArrayList<>(); + for (int num : nums) list.add(num); + return list; + } +} +``` + +```cpp +class Solution { +public: + vector> permuteUnique(vector& nums) { + int n = nums.size(); + sort(nums.begin(), nums.end()); + vector> res = {nums}; + + while (true) { + int i = n - 2; + while (i >= 0 && nums[i] >= nums[i + 1]) i--; + + if (i < 0) break; + + int j = n - 1; + while (nums[j] <= nums[i]) j--; + + swap(nums[i], nums[j]); + reverse(nums.begin() + i + 1, nums.end()); + res.push_back(nums); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number[][]} + */ + permuteUnique(nums) { + const n = nums.length; + nums.sort((a, b) => a - b); + const res = [nums.slice()]; + + while (true) { + let i = n - 2; + while (i >= 0 && nums[i] >= nums[i + 1]) i--; + + if (i < 0) break; + + let j = n - 1; + while (nums[j] <= nums[i]) j--; + + [nums[i], nums[j]] = [nums[j], nums[i]]; + + let l = i + 1, r = n - 1; + while (l < r) { + [nums[l], nums[r]] = [nums[r], nums[l]]; + l++; + r--; + } + + res.push(nums.slice()); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n! * n)$ +* Space complexity: $O(n! * n)$ for the output list. \ No newline at end of file diff --git a/articles/permutations.md b/articles/permutations.md index 3f9811dde..492290c73 100644 --- a/articles/permutations.md +++ b/articles/permutations.md @@ -151,7 +151,7 @@ class Solution { ### Time & Space Complexity * Time complexity: $O(n! * n ^ 2)$ -* Space complexity: $O(n! * n)$ +* Space complexity: $O(n! * n)$ for the output list. --- @@ -308,7 +308,7 @@ class Solution { ### Time & Space Complexity * Time complexity: $O(n! * n ^ 2)$ -* Space complexity: $O(n! * n)$ +* Space complexity: $O(n! * n)$ for the output list. --- @@ -506,7 +506,7 @@ class Solution { ### Time & Space Complexity * Time complexity: $O(n! * n)$ -* Space complexity: $O(n)$ +* Space complexity: $O(n! * n)$ for the output list. --- @@ -696,7 +696,7 @@ class Solution { ### Time & Space Complexity * Time complexity: $O(n! * n)$ -* Space complexity: $O(n)$ +* Space complexity: $O(n! * n)$ for the output list. --- @@ -894,4 +894,4 @@ class Solution { ### Time & Space Complexity * Time complexity: $O(n! * n)$ -* Space complexity: $O(n)$ \ No newline at end of file +* Space complexity: $O(n! * n)$ for the output list. \ No newline at end of file diff --git a/articles/remove-duplicates-from-sorted-list.md b/articles/remove-duplicates-from-sorted-list.md new file mode 100644 index 000000000..b50bbbdcb --- /dev/null +++ b/articles/remove-duplicates-from-sorted-list.md @@ -0,0 +1,312 @@ +## 1. Recursion + +::tabs-start + +```python +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, val=0, next=None): +# self.val = val +# self.next = next +class Solution: + def deleteDuplicates(self, head: Optional[ListNode]) -> Optional[ListNode]: + if not head or not head.next: + return head + + head.next = self.deleteDuplicates(head.next) + return head if head.val != head.next.val else head.next +``` + +```java +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode() {} + * ListNode(int val) { this.val = val; } + * ListNode(int val, ListNode next) { this.val = val; this.next = next; } + * } + */ +public class Solution { + public ListNode deleteDuplicates(ListNode head) { + if (head == null || head.next == null) return head; + + head.next = deleteDuplicates(head.next); + return head.val != head.next.val ? head : head.next; + } +} +``` + +```cpp +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode() : val(0), next(nullptr) {} + * ListNode(int x) : val(x), next(nullptr) {} + * ListNode(int x, ListNode *next) : val(x), next(next) {} + * }; + */ +class Solution { +public: + ListNode* deleteDuplicates(ListNode* head) { + if (!head || !head->next) return head; + + head->next = deleteDuplicates(head->next); + return head->val != head->next->val ? head : head->next; + } +}; +``` + +```javascript +/** + * Definition for singly-linked list. + * class ListNode { + * constructor(val = 0, next = null) { + * this.val = val; + * this.next = next; + * } + * } + */ +class Solution { + /** + * @param {ListNode} head + * @return {ListNode} + */ + deleteDuplicates(head) { + if (!head || !head.next) return head; + + head.next = this.deleteDuplicates(head.next); + return head.val !== head.next.val ? head : head.next; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(2 ^ n)$ +* Space complexity: $O(n)$ for recursion stack. + +--- + +## 2. Iteration - I + +::tabs-start + +```python +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, val=0, next=None): +# self.val = val +# self.next = next +class Solution: + def deleteDuplicates(self, head: Optional[ListNode]) -> Optional[ListNode]: + cur = head + while cur: + while cur.next and cur.next.val == cur.val: + cur.next = cur.next.next + cur = cur.next + return head +``` + +```java +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode() {} + * ListNode(int val) { this.val = val; } + * ListNode(int val, ListNode next) { this.val = val; this.next = next; } + * } + */ +public class Solution { + public ListNode deleteDuplicates(ListNode head) { + ListNode cur = head; + while (cur != null) { + while (cur.next != null && cur.next.val == cur.val) { + cur.next = cur.next.next; + } + cur = cur.next; + } + return head; + } +} +``` + +```cpp +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode() : val(0), next(nullptr) {} + * ListNode(int x) : val(x), next(nullptr) {} + * ListNode(int x, ListNode *next) : val(x), next(next) {} + * }; + */ +class Solution { +public: + ListNode* deleteDuplicates(ListNode* head) { + ListNode* cur = head; + while (cur) { + while (cur->next && cur->next->val == cur->val) { + cur->next = cur->next->next; + } + cur = cur->next; + } + return head; + } +}; +``` + +```javascript +/** + * Definition for singly-linked list. + * class ListNode { + * constructor(val = 0, next = null) { + * this.val = val; + * this.next = next; + * } + * } + */ +class Solution { + /** + * @param {ListNode} head + * @return {ListNode} + */ + deleteDuplicates(head) { + let cur = head; + while (cur) { + while (cur.next && cur.next.val === cur.val) { + cur.next = cur.next.next; + } + cur = cur.next; + } + return head; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. + +--- + +## 3. Iteration - II + +::tabs-start + +```python +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, val=0, next=None): +# self.val = val +# self.next = next +class Solution: + def deleteDuplicates(self, head: Optional[ListNode]) -> Optional[ListNode]: + cur = head + while cur and cur.next: + if cur.val == cur.next.val: + cur.next = cur.next.next + else: + cur = cur.next + return head +``` + +```java +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode() {} + * ListNode(int val) { this.val = val; } + * ListNode(int val, ListNode next) { this.val = val; this.next = next; } + * } + */ +public class Solution { + public ListNode deleteDuplicates(ListNode head) { + ListNode cur = head; + while (cur != null && cur.next != null) { + if (cur.next.val == cur.val) { + cur.next = cur.next.next; + } else { + cur = cur.next; + } + } + return head; + } +} +``` + +```cpp +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode() : val(0), next(nullptr) {} + * ListNode(int x) : val(x), next(nullptr) {} + * ListNode(int x, ListNode *next) : val(x), next(next) {} + * }; + */ +class Solution { +public: + ListNode* deleteDuplicates(ListNode* head) { + ListNode* cur = head; + while (cur && cur->next) { + if (cur->next->val == cur->val) { + cur->next = cur->next->next; + } else { + cur = cur->next; + } + } + return head; + } +}; +``` + +```javascript +/** + * Definition for singly-linked list. + * class ListNode { + * constructor(val = 0, next = null) { + * this.val = val; + * this.next = next; + * } + * } + */ +class Solution { + /** + * @param {ListNode} head + * @return {ListNode} + */ + deleteDuplicates(head) { + let cur = head; + while (cur && cur.next) { + if (cur.next.val === cur.val) { + cur.next = cur.next.next; + } else { + cur = cur.next; + } + } + return head; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. \ No newline at end of file diff --git a/articles/remove-linked-list-elements.md b/articles/remove-linked-list-elements.md new file mode 100644 index 000000000..30e4a5e94 --- /dev/null +++ b/articles/remove-linked-list-elements.md @@ -0,0 +1,514 @@ +## 1. Brute Force + +::tabs-start + +```python +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, val=0, next=None): +# self.val = val +# self.next = next +class Solution: + def removeElements(self, head: Optional[ListNode], val: int) -> Optional[ListNode]: + arr = [] + cur = head + + while cur: + if cur.val != val: + arr.append(cur.val) + cur = cur.next + + if not arr: + return None + + res = ListNode(arr[0]) + cur = res + for i in range(1, len(arr)): + node = ListNode(arr[i]) + cur.next = node + cur = cur.next + + return res +``` + +```java +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode() {} + * ListNode(int val) { this.val = val; } + * ListNode(int val, ListNode next) { this.val = val; this.next = next; } + * } + */ +public class Solution { + public ListNode removeElements(ListNode head, int val) { + List arr = new ArrayList<>(); + ListNode cur = head; + + while (cur != null) { + if (cur.val != val) { + arr.add(cur.val); + } + cur = cur.next; + } + + if (arr.isEmpty()) { + return null; + } + + ListNode res = new ListNode(arr.get(0)); + cur = res; + for (int i = 1; i < arr.size(); i++) { + ListNode node = new ListNode(arr.get(i)); + cur.next = node; + cur = cur.next; + } + + return res; + } +} +``` + +```cpp +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode() : val(0), next(nullptr) {} + * ListNode(int x) : val(x), next(nullptr) {} + * ListNode(int x, ListNode *next) : val(x), next(next) {} + * }; + */ +class Solution { +public: + ListNode* removeElements(ListNode* head, int val) { + vector arr; + ListNode* cur = head; + + while (cur) { + if (cur->val != val) { + arr.push_back(cur->val); + } + cur = cur->next; + } + + if (arr.empty()) { + return nullptr; + } + + ListNode* res = new ListNode(arr[0]); + cur = res; + for (int i = 1; i < arr.size(); i++) { + ListNode* node = new ListNode(arr[i]); + cur->next = node; + cur = cur->next; + } + + return res; + } +}; +``` + +```javascript +/** + * Definition for singly-linked list. + * class ListNode { + * constructor(val = 0, next = null) { + * this.val = val; + * this.next = next; + * } + * } + */ +class Solution { + /** + * @param {ListNode} head + * @param {number} val + * @return {ListNode} + */ + removeElements(head, val) { + const arr = []; + let cur = head; + + while (cur) { + if (cur.val !== val) { + arr.push(cur.val); + } + cur = cur.next; + } + + if (arr.length === 0) { + return null; + } + + const res = new ListNode(arr[0]); + cur = res; + for (let i = 1; i < arr.length; i++) { + const node = new ListNode(arr[i]); + cur.next = node; + cur = cur.next; + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Recursion + +::tabs-start + +```python +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, val=0, next=None): +# self.val = val +# self.next = next +class Solution: + def removeElements(self, head: Optional[ListNode], val: int) -> Optional[ListNode]: + if not head: + return None + head.next = self.removeElements(head.next, val) + return head if head.val != val else head.next +``` + +```java +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode() {} + * ListNode(int val) { this.val = val; } + * ListNode(int val, ListNode next) { this.val = val; this.next = next; } + * } + */ +public class Solution { + public ListNode removeElements(ListNode head, int val) { + if (head == null) return null; + head.next = removeElements(head.next, val); + return head.val != val ? head : head.next; + } +} +``` + +```cpp +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode() : val(0), next(nullptr) {} + * ListNode(int x) : val(x), next(nullptr) {} + * ListNode(int x, ListNode *next) : val(x), next(next) {} + * }; + */ +class Solution { +public: + ListNode* removeElements(ListNode* head, int val) { + if (head == nullptr) return nullptr; + head->next = removeElements(head->next, val); + return head->val != val ? head : head->next; + } +}; +``` + +```javascript +/** + * Definition for singly-linked list. + * class ListNode { + * constructor(val = 0, next = null) { + * this.val = val; + * this.next = next; + * } + * } + */ +class Solution { + /** + * @param {ListNode} head + * @param {number} val + * @return {ListNode} + */ + removeElements(head, val) { + if (head === null) return null; + head.next = this.removeElements(head.next, val); + return head.val !== val ? head : head.next; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ for recursion stack. + +--- + +## 3. Iteration + +::tabs-start + +```python +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, val=0, next=None): +# self.val = val +# self.next = next +class Solution: + def removeElements(self, head: ListNode, val: int) -> ListNode: + dummy = ListNode(0, head) + prev, curr = dummy, head + + while curr: + nxt = curr.next + if curr.val == val: + prev.next = nxt + else: + prev = curr + curr = nxt + + return dummy.next +``` + +```java +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode() {} + * ListNode(int val) { this.val = val; } + * ListNode(int val, ListNode next) { this.val = val; this.next = next; } + * } + */ +public class Solution { + public ListNode removeElements(ListNode head, int val) { + ListNode dummy = new ListNode(0, head); + ListNode prev = dummy, curr = head; + + while (curr != null) { + ListNode nxt = curr.next; + if (curr.val == val) { + prev.next = nxt; + } else { + prev = curr; + } + curr = nxt; + } + + return dummy.next; + } +} +``` + +```cpp +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode() : val(0), next(nullptr) {} + * ListNode(int x) : val(x), next(nullptr) {} + * ListNode(int x, ListNode *next) : val(x), next(next) {} + * }; + */ +class Solution { +public: + ListNode* removeElements(ListNode* head, int val) { + ListNode dummy(0, head); + ListNode *prev = &dummy, *curr = head; + + while (curr) { + ListNode* nxt = curr->next; + if (curr->val == val) { + prev->next = nxt; + } else { + prev = curr; + } + curr = nxt; + } + + return dummy.next; + } +}; +``` + +```javascript +/** + * Definition for singly-linked list. + * class ListNode { + * constructor(val = 0, next = null) { + * this.val = val; + * this.next = next; + * } + * } + */ +class Solution { + /** + * @param {ListNode} head + * @param {number} val + * @return {ListNode} + */ + removeElements(head, val) { + let dummy = new ListNode(0, head); + let prev = dummy, curr = head; + + while (curr) { + let nxt = curr.next; + if (curr.val === val) { + prev.next = nxt; + } else { + prev = curr; + } + curr = nxt; + } + + return dummy.next; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. + +--- + +## 4. iteration Without Prev Pointer + +::tabs-start + +```python +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, val=0, next=None): +# self.val = val +# self.next = next +class Solution: + def removeElements(self, head: ListNode, val: int) -> ListNode: + dummy = ListNode(-1, head) + curr = dummy + + while curr.next: + if curr.next.val == val: + curr.next = curr.next.next + else: + curr = curr.next + + return dummy.next +``` + +```java +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode() {} + * ListNode(int val) { this.val = val; } + * ListNode(int val, ListNode next) { this.val = val; this.next = next; } + * } + */ +public class Solution { + public ListNode removeElements(ListNode head, int val) { + ListNode dummy = new ListNode(-1, head); + ListNode curr = dummy; + + while (curr.next != null) { + if (curr.next.val == val) { + curr.next = curr.next.next; + } else { + curr = curr.next; + } + } + + return dummy.next; + } +} +``` + +```cpp +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode() : val(0), next(nullptr) {} + * ListNode(int x) : val(x), next(nullptr) {} + * ListNode(int x, ListNode *next) : val(x), next(next) {} + * }; + */ +class Solution { +public: + ListNode* removeElements(ListNode* head, int val) { + ListNode dummy(-1, head); + ListNode *curr = &dummy; + + while (curr->next) { + if (curr->next->val == val) { + curr->next = curr->next->next; + } else { + curr = curr->next; + } + } + + return dummy.next; + } +}; +``` + +```javascript +/** + * Definition for singly-linked list. + * class ListNode { + * constructor(val = 0, next = null) { + * this.val = val; + * this.next = next; + * } + * } + */ +class Solution { + /** + * @param {ListNode} head + * @param {number} val + * @return {ListNode} + */ + removeElements(head, val) { + let dummy = new ListNode(-1, head); + let curr = dummy; + + while (curr.next) { + if (curr.next.val === val) { + curr.next = curr.next.next; + } else { + curr = curr.next; + } + } + + return dummy.next; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. \ No newline at end of file diff --git a/articles/remove-nodes-from-linked-list.md b/articles/remove-nodes-from-linked-list.md new file mode 100644 index 000000000..b629d4760 --- /dev/null +++ b/articles/remove-nodes-from-linked-list.md @@ -0,0 +1,574 @@ +## 1. Convert To Array + +::tabs-start + +```python +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, val=0, next=None): +# self.val = val +# self.next = next +class Solution: + def removeNodes(self, head: Optional[ListNode]) -> Optional[ListNode]: + cur, arr = head, [ListNode(0, head)] + while cur: + arr.append(cur) + cur = cur.next + + rightMaxi = ListNode(0, None) + for i in range(len(arr) - 1, 0, -1): + if rightMaxi.val > arr[i].val: + arr[i - 1].next = rightMaxi + else: + rightMaxi = arr[i] + + return arr[0].next +``` + +```java +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode() {} + * ListNode(int val) { this.val = val; } + * ListNode(int val, ListNode next) { this.val = val; this.next = next; } + * } + */ +public class Solution { + public ListNode removeNodes(ListNode head) { + List arr = new ArrayList<>(); + arr.add(new ListNode(0, head)); + ListNode cur = head; + + while (cur != null) { + arr.add(cur); + cur = cur.next; + } + + ListNode rightMaxi = new ListNode(0, null); + for (int i = arr.size() - 1; i > 0; i--) { + if (rightMaxi.val > arr.get(i).val) { + arr.get(i - 1).next = rightMaxi; + } else { + rightMaxi = arr.get(i); + } + } + + return arr.get(0).next; + } +} +``` + +```cpp +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode() : val(0), next(nullptr) {} + * ListNode(int x) : val(x), next(nullptr) {} + * ListNode(int x, ListNode *next) : val(x), next(next) {} + * }; + */ +class Solution { +public: + ListNode* removeNodes(ListNode* head) { + vector arr; + arr.push_back(new ListNode(0, head)); + ListNode* cur = head; + + while (cur) { + arr.push_back(cur); + cur = cur->next; + } + + ListNode* rightMaxi = new ListNode(0, nullptr); + for (int i = arr.size() - 1; i > 0; i--) { + if (rightMaxi->val > arr[i]->val) { + arr[i - 1]->next = rightMaxi; + } else { + rightMaxi = arr[i]; + } + } + + return arr[0]->next; + } +}; +``` + +```javascript +/** + * Definition for singly-linked list. + * class ListNode { + * constructor(val = 0, next = null) { + * this.val = val; + * this.next = next; + * } + * } + */ +class Solution { + /** + * @param {ListNode} head + * @return {ListNode} + */ + removeNodes(head) { + let cur = head, arr = [{ val: 0, next: head }]; + while (cur) { + arr.push(cur); + cur = cur.next; + } + + let rightMaxi = { val: 0, next: null }; + for (let i = arr.length - 1; i > 0; i--) { + if (rightMaxi.val > arr[i].val) { + arr[i - 1].next = rightMaxi; + } else { + rightMaxi = arr[i]; + } + } + + return arr[0].next; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Monotonic Decreasing Stack + +::tabs-start + +```python +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, val=0, next=None): +# self.val = val +# self.next = next +class Solution: + def removeNodes(self, head: Optional[ListNode]) -> Optional[ListNode]: + stack = [] + cur = head + + while cur: + while stack and cur.val > stack[-1]: + stack.pop() + stack.append(cur.val) + cur = cur.next + + dummy = ListNode() + cur = dummy + + for num in stack: + cur.next = ListNode(num) + cur = cur.next + + return dummy.next +``` + +```java +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode() {} + * ListNode(int val) { this.val = val; } + * ListNode(int val, ListNode next) { this.val = val; this.next = next; } + * } + */ +public class Solution { + public ListNode removeNodes(ListNode head) { + Stack stack = new Stack<>(); + ListNode cur = head; + + while (cur != null) { + while (!stack.isEmpty() && cur.val > stack.peek()) { + stack.pop(); + } + stack.push(cur.val); + cur = cur.next; + } + + ListNode dummy = new ListNode(); + cur = dummy; + + for (int num : stack) { + cur.next = new ListNode(num); + cur = cur.next; + } + + return dummy.next; + } +} +``` + +```cpp +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode() : val(0), next(nullptr) {} + * ListNode(int x) : val(x), next(nullptr) {} + * ListNode(int x, ListNode *next) : val(x), next(next) {} + * }; + */ +class Solution { +public: + ListNode* removeNodes(ListNode* head) { + vector stack; + ListNode* cur = head; + + while (cur) { + while (!stack.empty() && cur->val > stack.back()) { + stack.pop_back(); + } + stack.push_back(cur->val); + cur = cur->next; + } + + ListNode* dummy = new ListNode(); + cur = dummy; + + for (int num : stack) { + cur->next = new ListNode(num); + cur = cur->next; + } + + return dummy->next; + } +}; +``` + +```javascript +/** + * Definition for singly-linked list. + * class ListNode { + * constructor(val = 0, next = null) { + * this.val = val; + * this.next = next; + * } + * } + */ +class Solution { + /** + * @param {ListNode} head + * @return {ListNode} + */ + removeNodes(head) { + let stack = []; + let cur = head; + + while (cur) { + while (stack.length && cur.val > stack[stack.length - 1]) { + stack.pop(); + } + stack.push(cur.val); + cur = cur.next; + } + + let dummy = new ListNode(); + cur = dummy; + + for (let num of stack) { + cur.next = new ListNode(num); + cur = cur.next; + } + + return dummy.next; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Recursion + +::tabs-start + +```python +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, val=0, next=None): +# self.val = val +# self.next = next +class Solution: + def removeNodes(self, head: Optional[ListNode]) -> Optional[ListNode]: + if not head: + return None + + head.next = self.removeNodes(head.next) + if head.next and head.val < head.next.val: + return head.next + return head +``` + +```java +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode() {} + * ListNode(int val) { this.val = val; } + * ListNode(int val, ListNode next) { this.val = val; this.next = next; } + * } + */ +public class Solution { + public ListNode removeNodes(ListNode head) { + if (head == null) return null; + + head.next = removeNodes(head.next); + if (head.next != null && head.val < head.next.val) { + return head.next; + } + return head; + } +} +``` + +```cpp +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode() : val(0), next(nullptr) {} + * ListNode(int x) : val(x), next(nullptr) {} + * ListNode(int x, ListNode *next) : val(x), next(next) {} + * }; + */ +class Solution { +public: + ListNode* removeNodes(ListNode* head) { + if (!head) return nullptr; + + head->next = removeNodes(head->next); + if (head->next && head->val < head->next->val) { + return head->next; + } + return head; + } +}; +``` + +```javascript +/** + * Definition for singly-linked list. + * class ListNode { + * constructor(val = 0, next = null) { + * this.val = val; + * this.next = next; + * } + * } + */ +class Solution { + /** + * @param {ListNode} head + * @return {ListNode} + */ + removeNodes(head) { + if (!head) return null; + + head.next = this.removeNodes(head.next); + if (head.next && head.val < head.next.val) { + return head.next; + } + return head; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ for recursion stack. + +--- + +## 4. Reverse Twice + +::tabs-start + +```python +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, val=0, next=None): +# self.val = val +# self.next = next +class Solution: + def removeNodes(self, head: Optional[ListNode]) -> Optional[ListNode]: + def reverse(head): + prev, cur = None, head + while cur: + tmp = cur.next + cur.next = prev + prev, cur = cur, tmp + return prev + + head = reverse(head) + cur = head + cur_max = head.val + + while cur and cur.next: + if cur.next.val < cur_max: + cur.next = cur.next.next + else: + cur_max = cur.next.val + cur = cur.next + + return reverse(head) +``` + +```java +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode() {} + * ListNode(int val) { this.val = val; } + * ListNode(int val, ListNode next) { this.val = val; this.next = next; } + * } + */ +public class Solution { + public ListNode removeNodes(ListNode head) { + head = reverse(head); + ListNode cur = head; + int curMax = head.val; + + while (cur != null && cur.next != null) { + if (cur.next.val < curMax) { + cur.next = cur.next.next; + } else { + curMax = cur.next.val; + cur = cur.next; + } + } + return reverse(head); + } + + private ListNode reverse(ListNode head) { + ListNode prev = null, cur = head; + while (cur != null) { + ListNode tmp = cur.next; + cur.next = prev; + prev = cur; + cur = tmp; + } + return prev; + } +} +``` + +```cpp +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode() : val(0), next(nullptr) {} + * ListNode(int x) : val(x), next(nullptr) {} + * ListNode(int x, ListNode *next) : val(x), next(next) {} + * }; + */ +class Solution { +public: + ListNode* reverse(ListNode* head) { + ListNode* prev = nullptr; + ListNode* cur = head; + while (cur) { + ListNode* tmp = cur->next; + cur->next = prev; + prev = cur; + cur = tmp; + } + return prev; + } + + ListNode* removeNodes(ListNode* head) { + head = reverse(head); + ListNode* cur = head; + int cur_max = head->val; + + while (cur && cur->next) { + if (cur->next->val < cur_max) { + cur->next = cur->next->next; + } else { + cur_max = cur->next->val; + cur = cur->next; + } + } + return reverse(head); + } +}; +``` + +```javascript +/** + * Definition for singly-linked list. + * class ListNode { + * constructor(val = 0, next = null) { + * this.val = val; + * this.next = next; + * } + * } + */ +class Solution { + /** + * @param {ListNode} head + * @return {ListNode} + */ + removeNodes(head) { + const reverse = (head) => { + let prev = null, cur = head; + while (cur) { + let tmp = cur.next; + cur.next = prev; + prev = cur; + cur = tmp; + } + return prev; + }; + + head = reverse(head); + let cur = head; + let cur_max = head.val; + + while (cur && cur.next) { + if (cur.next.val < cur_max) { + cur.next = cur.next.next; + } else { + cur_max = cur.next.val; + cur = cur.next; + } + } + return reverse(head); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. \ No newline at end of file diff --git a/articles/reorder-routes-to-make-all-paths-lead-to-the-city-zero.md b/articles/reorder-routes-to-make-all-paths-lead-to-the-city-zero.md new file mode 100644 index 000000000..ae2727f84 --- /dev/null +++ b/articles/reorder-routes-to-make-all-paths-lead-to-the-city-zero.md @@ -0,0 +1,385 @@ +## 1. Depth First Search - I + +::tabs-start + +```python +class Solution: + def minReorder(self, n: int, connections: List[List[int]]) -> int: + edges = {(a, b) for a, b in connections} + neighbors = {city: [] for city in range(n)} + visit = set() + changes = 0 + + for a, b in connections: + neighbors[a].append(b) + neighbors[b].append(a) + + def dfs(city): + nonlocal changes + visit.add(city) + for neighbor in neighbors[city]: + if neighbor in visit: + continue + if (neighbor, city) not in edges: + changes += 1 + dfs(neighbor) + + dfs(0) + return changes +``` + +```java +public class Solution { + private Map> neighbors; + private boolean[] visit; + private Set edges; + + public int minReorder(int n, int[][] connections) { + edges = new HashSet<>(); + neighbors = new HashMap<>(); + visit = new boolean[n]; + int[] changes = {0}; + + for (int[] conn : connections) { + int a = conn[0], b = conn[1]; + edges.add(a + "," + b); + neighbors.computeIfAbsent(a, k -> new ArrayList<>()).add(b); + neighbors.computeIfAbsent(b, k -> new ArrayList<>()).add(a); + } + + dfs(0, changes); + return changes[0]; + } + + private void dfs(int city, int[] changes) { + visit[city] = true; + for (int neighbor : neighbors.get(city)) { + if (visit[neighbor]) continue; + if (!edges.contains(neighbor + "," + city)) { + changes[0]++; + } + dfs(neighbor, changes); + } + } +} +``` + +```cpp +class Solution { +public: + int minReorder(int n, vector>& connections) { + unordered_set edges; + unordered_map> neighbors; + vector visit(n, false); + int changes = 0; + + for (auto& c : connections) { + edges.insert(to_string(c[0]) + "," + to_string(c[1])); + neighbors[c[0]].push_back(c[1]); + neighbors[c[1]].push_back(c[0]); + } + + function dfs = [&](int city) { + visit[city] = true; + for (int neighbor : neighbors[city]) { + if (visit[neighbor]) continue; + if (edges.find(to_string(neighbor) + "," + to_string(city)) == edges.end()) { + changes++; + } + dfs(neighbor); + } + }; + + dfs(0); + return changes; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number[][]} connections + * @return {number} + */ + minReorder(n, connections) { + const edges = new Set(); + const neighbors = Array.from({ length: n }, () => []); + const visit = new Array(n).fill(false); + let changes = 0; + + for (const [a, b] of connections) { + edges.add(`${a},${b}`); + neighbors[a].push(b); + neighbors[b].push(a); + } + + const dfs = (city) => { + visit[city] = true; + for (const neighbor of neighbors[city]) { + if (visit[neighbor]) continue; + if (!edges.has(`${neighbor},${city}`)) changes++; + dfs(neighbor); + } + }; + + dfs(0); + return changes; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Depth First Search - II + +::tabs-start + +```python +class Solution: + def minReorder(self, n: int, connections: List[List[int]]) -> int: + adj = [[] for _ in range(n)] + for u, v in connections: + adj[u].append(v) + adj[v].append(-u) + + def dfs(node, parent): + changes = 0 + for nei in adj[node]: + if abs(nei) == parent: + continue + changes += dfs(abs(nei), node) + (nei > 0) + return changes + + return dfs(0, -1) +``` + +```java +public class Solution { + public int minReorder(int n, int[][] connections) { + List> adj = new ArrayList<>(); + for (int i = 0; i < n; i++) adj.add(new ArrayList<>()); + + for (int[] conn : connections) { + adj.get(conn[0]).add(conn[1]); + adj.get(conn[1]).add(-conn[0]); + } + + return dfs(0, -1, adj); + } + + private int dfs(int node, int parent, List> adj) { + int changes = 0; + for (int nei : adj.get(node)) { + if (Math.abs(nei) == parent) continue; + changes += dfs(Math.abs(nei), node, adj) + (nei > 0 ? 1 : 0); + } + return changes; + } +} +``` + +```cpp +class Solution { +public: + int minReorder(int n, vector>& connections) { + vector> adj(n); + for (auto& conn : connections) { + int u = conn[0], v = conn[1]; + adj[u].push_back(v); + adj[v].push_back(-u); + } + + return dfs(0, -1, adj); + } + +private: + int dfs(int node, int parent, vector>& adj) { + int changes = 0; + for (int nei : adj[node]) { + if (abs(nei) == parent) continue; + changes += dfs(abs(nei), node, adj) + (nei > 0); + } + return changes; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number[][]} connections + * @return {number} + */ + minReorder(n, connections) { + const adj = Array.from({ length: n }, () => []); + for (const [u, v] of connections) { + adj[u].push(v); + adj[v].push(-u); + } + + const dfs = (node, parent) => { + let changes = 0; + for (const nei of adj[node]) { + if (Math.abs(nei) === parent) continue; + changes += dfs(Math.abs(nei), node) + (nei > 0 ? 1 : 0); + } + return changes; + }; + + return dfs(0, -1); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Breadth First Search + +::tabs-start + +```python +class Solution: + def minReorder(self, n: int, connections: List[List[int]]) -> int: + adj = [[] for _ in range(n)] + for u, v in connections: + adj[u].append((v, 1)) + adj[v].append((u, 0)) + + visit = [False] * n + queue = deque([0]) + visit[0] = True + changes = 0 + + while queue: + node = queue.popleft() + for neighbor, isForward in adj[node]: + if not visit[neighbor]: + visit[neighbor] = True + changes += isForward + queue.append(neighbor) + return changes +``` + +```java +public class Solution { + public int minReorder(int n, int[][] connections) { + List[] adj = new ArrayList[n]; + for (int i = 0; i < n; i++) adj[i] = new ArrayList<>(); + + for (int[] conn : connections) { + adj[conn[0]].add(new int[]{conn[1], 1}); + adj[conn[1]].add(new int[]{conn[0], 0}); + } + + boolean[] visited = new boolean[n]; + Queue queue = new LinkedList<>(); + queue.add(0); + visited[0] = true; + int changes = 0; + + while (!queue.isEmpty()) { + int node = queue.poll(); + for (int[] edge : adj[node]) { + int neighbor = edge[0], isForward = edge[1]; + if (!visited[neighbor]) { + visited[neighbor] = true; + changes += isForward; + queue.add(neighbor); + } + } + } + return changes; + } +} +``` + +```cpp +class Solution { +public: + int minReorder(int n, vector>& connections) { + vector>> adj(n); + for (auto& conn : connections) { + adj[conn[0]].push_back({conn[1], 1}); + adj[conn[1]].push_back({conn[0], 0}); + } + + vector visit(n, false); + queue q; + q.push(0); + visit[0] = true; + int changes = 0; + + while (!q.empty()) { + int node = q.front(); + q.pop(); + for (auto& [neighbor, isForward] : adj[node]) { + if (!visit[neighbor]) { + visit[neighbor] = true; + changes += isForward; + q.push(neighbor); + } + } + } + return changes; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number[][]} connections + * @return {number} + */ + minReorder(n, connections) { + const adj = Array.from({ length: n }, () => []); + for (const [u, v] of connections) { + adj[u].push([v, 1]); + adj[v].push([u, 0]); + } + + const visited = Array(n).fill(false); + const queue = new Queue(); + queue.push(0); + visited[0] = true; + let changes = 0; + + while (!queue.isEmpty()) { + const node = queue.pop(); + for (const [neighbor, isForward] of adj[node]) { + if (!visited[neighbor]) { + visited[neighbor] = true; + changes += isForward; + queue.push(neighbor); + } + } + } + return changes; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/restore-ip-addresses.md b/articles/restore-ip-addresses.md new file mode 100644 index 000000000..a0a6fd2b6 --- /dev/null +++ b/articles/restore-ip-addresses.md @@ -0,0 +1,278 @@ +## 1. Backtracking + +::tabs-start + +```python +class Solution: + def restoreIpAddresses(self, s: str) -> List[str]: + res = [] + if len(s) > 12: + return res + + def backtrack(i, dots, curIP): + if dots == 4 and i == len(s): + res.append(curIP[:-1]) + return + if dots > 4: + return + + for j in range(i, min(i + 3, len(s))): + if i != j and s[i] == "0": + continue + if int(s[i: j + 1]) < 256: + backtrack(j + 1, dots + 1, curIP + s[i: j + 1] + ".") + + backtrack(0, 0, "") + return res +``` + +```java +public class Solution { + public List restoreIpAddresses(String s) { + List res = new ArrayList<>(); + if (s.length() > 12) return res; + + backtrack(0, 0, "", s, res); + return res; + } + + private void backtrack(int i, int dots, String curIP, String s, List res) { + if (dots == 4 && i == s.length()) { + res.add(curIP.substring(0, curIP.length() - 1)); + return; + } + if (dots > 4) return; + + for (int j = i; j < Math.min(i + 3, s.length()); j++) { + if (i != j && s.charAt(i) == '0') continue; + if (Integer.parseInt(s.substring(i, j + 1)) < 256) { + backtrack(j + 1, dots + 1, curIP + s.substring(i, j + 1) + ".", s, res); + } + } + } +} +``` + +```cpp +class Solution { + vector res; + +public: + vector restoreIpAddresses(string s) { + if (s.length() > 12) return res; + backtrack(s, 0, 0, ""); + return res; + } + +private: + void backtrack(string& s, int i, int dots, string curIP) { + if (dots == 4 && i == s.size()) { + res.push_back(curIP.substr(0, curIP.size() - 1)); + return; + } + if (dots > 4) return; + + for (int j = i; j < min(i + 3, (int)s.size()); j++) { + if (i != j && s[i] == '0') continue; + if (stoi(s.substr(i, j - i + 1)) < 256) { + backtrack(s, j + 1, dots + 1, curIP + s.substr(i, j - i + 1) + "."); + } + } + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {string[]} + */ + restoreIpAddresses(s) { + const res = []; + if (s.length > 12) return res; + + const backtrack = (i, dots, curIP) => { + if (dots === 4 && i === s.length) { + res.push(curIP.slice(0, -1)); + return; + } + if (dots > 4) return; + + for (let j = i; j < Math.min(i + 3, s.length); j++) { + if (i !== j && s[i] === '0') continue; + if (parseInt(s.slice(i, j + 1)) < 256) { + backtrack(j + 1, dots + 1, curIP + s.slice(i, j + 1) + '.'); + } + } + }; + + backtrack(0, 0, ""); + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m ^ n * n)$ +* Space complexity: $O(m * n)$ + +> Where $m$ is equals to $3$ as there are at most three digits in a valid segment and $n$ is equals to $4$ as there are four segments in a valid IP. + +--- + +## 2. Iteration + +::tabs-start + +```python +class Solution: + def restoreIpAddresses(self, s: str) -> List[str]: + res = [] + if len(s) > 12: + return res + + def valid(num): + return len(num) == 1 or (int(num) < 256 and num[0] != "0") + + def add(s1, s2, s3, s4): + if s1 + s2 + s3 + s4 != len(s): + return + + num1 = s[:s1] + num2 = s[s1:s1+s2] + num3 = s[s1+s2:s1+s2+s3] + num4 = s[s1+s2+s3:] + if valid(num1) and valid(num2) and valid(num3) and valid(num4): + res.append(num1 + "." + num2 + "." + num3 + "." + num4) + + for seg1 in range(1, 4): + for seg2 in range(1, 4): + for seg3 in range(1, 4): + for seg4 in range(1, 4): + add(seg1, seg2, seg3, seg4) + + return res +``` + +```java +public class Solution { + public List restoreIpAddresses(String s) { + List res = new ArrayList<>(); + if (s.length() > 12) return res; + + for (int seg1 = 1; seg1 < 4; seg1++) { + for (int seg2 = 1; seg2 < 4; seg2++) { + for (int seg3 = 1; seg3 < 4; seg3++) { + for (int seg4 = 1; seg4 < 4; seg4++) { + if (seg1 + seg2 + seg3 + seg4 != s.length()) continue; + + String num1 = s.substring(0, seg1); + String num2 = s.substring(seg1, seg1 + seg2); + String num3 = s.substring(seg1 + seg2, seg1 + seg2 + seg3); + String num4 = s.substring(seg1 + seg2 + seg3); + + if (isValid(num1) && isValid(num2) && isValid(num3) && isValid(num4)) { + res.add(num1 + "." + num2 + "." + num3 + "." + num4); + } + } + } + } + } + return res; + } + + private boolean isValid(String num) { + if (num.length() > 1 && num.charAt(0) == '0') return false; + int value = Integer.parseInt(num); + return value <= 255; + } +} +``` + +```cpp +class Solution { +public: + vector restoreIpAddresses(string s) { + vector res; + if (s.size() > 12) return res; + + auto valid = [&](string& num) { + if (num.size() > 1 && num[0] == '0') return false; + int value = stoi(num); + return value <= 255; + }; + + for (int seg1 = 1; seg1 < 4; ++seg1) { + for (int seg2 = 1; seg2 < 4; ++seg2) { + for (int seg3 = 1; seg3 < 4; ++seg3) { + for (int seg4 = 1; seg4 < 4; ++seg4) { + if (seg1 + seg2 + seg3 + seg4 != s.size()) continue; + + string num1 = s.substr(0, seg1); + string num2 = s.substr(seg1, seg2); + string num3 = s.substr(seg1 + seg2, seg3); + string num4 = s.substr(seg1 + seg2 + seg3); + + if (valid(num1) && valid(num2) && valid(num3) && valid(num4)) { + res.push_back(num1 + "." + num2 + "." + num3 + "." + num4); + } + } + } + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {string[]} + */ + restoreIpAddresses(s) { + const res = []; + if (s.length > 12) return res; + + const isValid = (num) => { + if (num.length > 1 && num[0] === '0') return false; + const value = parseInt(num, 10); + return value <= 255; + }; + + for (let seg1 = 1; seg1 < 4; seg1++) { + for (let seg2 = 1; seg2 < 4; seg2++) { + for (let seg3 = 1; seg3 < 4; seg3++) { + for (let seg4 = 1; seg4 < 4; seg4++) { + if (seg1 + seg2 + seg3 + seg4 !== s.length) continue; + + const num1 = s.substring(0, seg1); + const num2 = s.substring(seg1, seg1 + seg2); + const num3 = s.substring(seg1 + seg2, seg1 + seg2 + seg3); + const num4 = s.substring(seg1 + seg2 + seg3); + + if (isValid(num1) && isValid(num2) && isValid(num3) && isValid(num4)) { + res.push(`${num1}.${num2}.${num3}.${num4}`); + } + } + } + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m ^ n * n)$ +* Space complexity: $O(m * n)$ + +> Where $m$ is equals to $3$ as there are at most three digits in a valid segment and $n$ is equals to $4$ as there are four segments in a valid IP. \ No newline at end of file diff --git a/articles/snakes-and-ladders.md b/articles/snakes-and-ladders.md new file mode 100644 index 000000000..afc71ef47 --- /dev/null +++ b/articles/snakes-and-ladders.md @@ -0,0 +1,572 @@ +## 1. Breadth First Search - I + +::tabs-start + +```python +class Solution: + def snakesAndLadders(self, board: List[List[int]]) -> int: + n = len(board) + + def intToPos(square): + r = (square - 1) // n + c = (square - 1) % n + if r % 2 == 1: + c = n - 1 - c + r = n - 1 - r + return r, c + + q = deque([(1, 0)]) + visit = set() + + while q: + square, moves = q.popleft() + + for i in range(1, 7): + nextSquare = square + i + r, c = intToPos(nextSquare) + if board[r][c] != -1: + nextSquare = board[r][c] + + if nextSquare == n * n: + return moves + 1 + + if nextSquare not in visit: + visit.add(nextSquare) + q.append((nextSquare, moves + 1)) + + return -1 +``` + +```java +public class Solution { + public int snakesAndLadders(int[][] board) { + int n = board.length; + Queue q = new LinkedList<>(); + q.offer(new int[]{1, 0}); + Set visit = new HashSet<>(); + + while (!q.isEmpty()) { + int[] cur = q.poll(); + int square = cur[0], moves = cur[1]; + + for (int i = 1; i <= 6; i++) { + int nextSquare = square + i; + int[] pos = intToPos(nextSquare, n); + int r = pos[0], c = pos[1]; + if (board[r][c] != -1) { + nextSquare = board[r][c]; + } + if (nextSquare == n * n) return moves + 1; + if (!visit.contains(nextSquare)) { + visit.add(nextSquare); + q.offer(new int[]{nextSquare, moves + 1}); + } + } + } + return -1; + } + + private int[] intToPos(int square, int n) { + int r = (square - 1) / n; + int c = (square - 1) % n; + if (r % 2 == 1) c = n - 1 - c; + r = n - 1 - r; + return new int[]{r, c}; + } +} +``` + +```cpp +class Solution { +public: + int snakesAndLadders(vector>& board) { + int n = board.size(); + queue> q; + q.push({1, 0}); + unordered_set visit; + + while (!q.empty()) { + auto [square, moves] = q.front(); q.pop(); + + for (int i = 1; i <= 6; i++) { + int nextSquare = square + i; + auto [r, c] = intToPos(nextSquare, n); + if (board[r][c] != -1) { + nextSquare = board[r][c]; + } + if (nextSquare == n * n) return moves + 1; + if (!visit.count(nextSquare)) { + visit.insert(nextSquare); + q.push({nextSquare, moves + 1}); + } + } + } + return -1; + } + +private: + pair intToPos(int square, int n) { + int r = (square - 1) / n; + int c = (square - 1) % n; + if (r % 2 == 1) c = n - 1 - c; + r = n - 1 - r; + return {r, c}; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} board + * @return {number} + */ + snakesAndLadders(board) { + const n = board.length; + const queue = new Queue([[1, 0]]); + const visit = new Set(); + + const intToPos = (square) => { + let r = Math.floor((square - 1) / n); + let c = (square - 1) % n; + if (r % 2 === 1) c = n - 1 - c; + r = n - 1 - r; + return [r, c]; + }; + + while (!queue.isEmpty()) { + const [square, moves] = queue.pop(); + + for (let i = 1; i <= 6; i++) { + let nextSquare = square + i; + const [r, c] = intToPos(nextSquare); + if (board[r][c] !== -1) { + nextSquare = board[r][c]; + } + if (nextSquare === n * n) return moves + 1; + if (!visit.has(nextSquare)) { + visit.add(nextSquare); + queue.push([nextSquare, moves + 1]); + } + } + } + return -1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n ^ 2)$ + +--- + +## 2. Breadth First Search - II + +::tabs-start + +```python +class Solution: + def snakesAndLadders(self, board: List[List[int]]) -> int: + n = len(board) + + def intToPos(square): + r = (square - 1) // n + c = (square - 1) % n + if r % 2 == 1: + c = n - 1 - c + r = n - 1 - r + return r, c + + dist = [-1] * (n * n + 1) + q = deque([1]) + dist[1] = 0 + + while q: + square = q.popleft() + + for i in range(1, 7): + nextSquare = square + i + if nextSquare > n * n: + break + + r, c = intToPos(nextSquare) + if board[r][c] != -1: + nextSquare = board[r][c] + + if dist[nextSquare] == -1: + dist[nextSquare] = dist[square] + 1 + if nextSquare == n * n: + return dist[nextSquare] + q.append(nextSquare) + + return -1 +``` + +```java +public class Solution { + public int snakesAndLadders(int[][] board) { + int n = board.length; + int[] dist = new int[n * n + 1]; + Arrays.fill(dist, -1); + Queue q = new LinkedList<>(); + q.add(1); + dist[1] = 0; + + while (!q.isEmpty()) { + int square = q.poll(); + + for (int i = 1; i <= 6; i++) { + int nextSquare = square + i; + if (nextSquare > n * n) { + break; + } + + int[] pos = intToPos(nextSquare, n); + int r = pos[0], c = pos[1]; + + if (board[r][c] != -1) { + nextSquare = board[r][c]; + } + + if (dist[nextSquare] == -1) { + dist[nextSquare] = dist[square] + 1; + if (nextSquare == n * n) { + return dist[nextSquare]; + } + q.add(nextSquare); + } + } + } + + return -1; + } + + private int[] intToPos(int square, int n) { + int r = (square - 1) / n; + int c = (square - 1) % n; + if (r % 2 == 1) { + c = n - 1 - c; + } + r = n - 1 - r; + return new int[]{r, c}; + } +} +``` + +```cpp +class Solution { +public: + int snakesAndLadders(vector>& board) { + int n = board.size(); + vector dist(n * n + 1, -1); + queue q; + q.push(1); + dist[1] = 0; + + while (!q.empty()) { + int square = q.front(); + q.pop(); + + for (int i = 1; i <= 6; i++) { + int nextSquare = square + i; + if (nextSquare > n * n) { + break; + } + + auto [r, c] = intToPos(nextSquare, n); + if (board[r][c] != -1) { + nextSquare = board[r][c]; + } + + if (dist[nextSquare] == -1) { + dist[nextSquare] = dist[square] + 1; + if (nextSquare == n * n) { + return dist[nextSquare]; + } + q.push(nextSquare); + } + } + } + + return -1; + } + +private: + pair intToPos(int square, int n) { + int r = (square - 1) / n; + int c = (square - 1) % n; + if (r % 2 == 1) { + c = n - 1 - c; + } + r = n - 1 - r; + return {r, c}; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} board + * @return {number} + */ + snakesAndLadders(board) { + const n = board.length; + const dist = new Array(n * n + 1).fill(-1); + const queue = new Queue([1]); + dist[1] = 0; + + const intToPos = (square) => { + let r = Math.floor((square - 1) / n); + let c = (square - 1) % n; + if (r % 2 === 1) c = n - 1 - c; + r = n - 1 - r; + return [r, c]; + }; + + while (!queue.isEmpty()) { + const square = queue.pop(); + + for (let i = 1; i <= 6; i++) { + let nextSquare = square + i; + if (nextSquare > n * n) break; + + const [r, c] = intToPos(nextSquare, n); + if (board[r][c] !== -1) { + nextSquare = board[r][c]; + } + + if (dist[nextSquare] === -1) { + dist[nextSquare] = dist[square] + 1; + if (nextSquare === n * n) { + return dist[nextSquare]; + } + queue.push(nextSquare); + } + } + } + + return -1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n ^ 2)$ + +--- + +## 3. Breadth First Search - III + +::tabs-start + +```python +class Solution: + def snakesAndLadders(self, board: List[List[int]]) -> int: + n = len(board) + + def intToPos(square): + r = (square - 1) // n + c = (square - 1) % n + if r % 2 == 1: + c = n - 1 - c + r = n - 1 - r + return r, c + + q = deque([1]) + board[n - 1][0] = 0 + moves = 0 + + while q: + for _ in range(len(q)): + square = q.popleft() + + for i in range(1, 7): + nextSquare = square + i + if nextSquare > n * n: + break + + r, c = intToPos(nextSquare) + if board[r][c] != -1: + nextSquare = board[r][c] + + if board[r][c] != 0: + if nextSquare == n * n: + return moves + 1 + q.append(nextSquare) + board[r][c] = 0 + moves += 1 + + return -1 +``` + +```java +public class Solution { + public int snakesAndLadders(int[][] board) { + int n = board.length; + Queue q = new LinkedList<>(); + q.add(1); + board[n - 1][0] = 0; + int moves = 0; + + while (!q.isEmpty()) { + for (int it = q.size(); it > 0; it--) { + int square = q.poll(); + for (int i = 1; i <= 6; i++) { + int nextSquare = square + i; + if (nextSquare > n * n) { + break; + } + + int[] pos = intToPos(nextSquare, n); + int r = pos[0], c = pos[1]; + + if (board[r][c] != -1) { + nextSquare = board[r][c]; + } + + if (board[r][c] != 0) { + if (nextSquare == n * n) { + return moves + 1; + } + + board[r][c] = 0; + q.add(nextSquare); + } + } + } + moves++; + } + + return -1; + } + + private int[] intToPos(int square, int n) { + int r = (square - 1) / n; + int c = (square - 1) % n; + if (r % 2 == 1) { + c = n - 1 - c; + } + r = n - 1 - r; + return new int[]{r, c}; + } +} +``` + +```cpp +class Solution { +public: + int snakesAndLadders(vector>& board) { + int n = board.size(); + queue q; + q.push(1); + board[n - 1][0] = 0; + int moves = 0; + + while (!q.empty()) { + for (int it = q.size(); it > 0; it--) { + int square = q.front(); q.pop(); + for (int i = 1; i <= 6; i++) { + int nextSquare = square + i; + if (nextSquare > n * n) { + break; + } + + auto [r, c] = intToPos(nextSquare, n); + if (board[r][c] != -1) { + nextSquare = board[r][c]; + } + + if (board[r][c] != 0) { + if (nextSquare == n * n) { + return moves + 1; + } + + board[r][c] = 0; + q.push(nextSquare); + } + } + } + moves++; + } + + return -1; + } + +private: + pair intToPos(int square, int n) { + int r = (square - 1) / n; + int c = (square - 1) % n; + if (r % 2 == 1) { + c = n - 1 - c; + } + r = n - 1 - r; + return {r, c}; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} board + * @return {number} + */ + snakesAndLadders(board) { + const n = board.length; + const queue = new Queue([1]); + board[n - 1][0] = 0; + + const intToPos = (square) => { + let r = Math.floor((square - 1) / n); + let c = (square - 1) % n; + if (r % 2 === 1) c = n - 1 - c; + r = n - 1 - r; + return [r, c]; + }; + + let moves = 0; + while (!queue.isEmpty()) { + for (let it = queue.size(); it > 0; it--) { + const square = queue.pop(); + for (let i = 1; i <= 6; i++) { + let nextSquare = square + i; + if (nextSquare > n * n) break; + + const [r, c] = intToPos(nextSquare, n); + if (board[r][c] !== -1) { + nextSquare = board[r][c]; + } + + if (board[r][c] !== 0) { + if (nextSquare === n * n) { + return moves + 1; + } + + board[r][c] = 0; + queue.push(nextSquare); + } + } + } + moves++; + } + + return -1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n ^ 2)$ \ No newline at end of file diff --git a/articles/sum-of-all-subset-xor-totals.md b/articles/sum-of-all-subset-xor-totals.md new file mode 100644 index 000000000..b1f8dc9ae --- /dev/null +++ b/articles/sum-of-all-subset-xor-totals.md @@ -0,0 +1,341 @@ +## 1. Backtracking + +::tabs-start + +```python +class Solution: + def subsetXORSum(self, nums: List[int]) -> int: + res = 0 + + def backtrack(i, subset): + nonlocal res + xorr = 0 + for num in subset: + xorr ^= num + res += xorr + + for j in range(i, len(nums)): + subset.append(nums[j]) + backtrack(j + 1, subset) + subset.pop() + + backtrack(0, []) + return res +``` + +```java +public class Solution { + int res = 0; + + public int subsetXORSum(int[] nums) { + backtrack(0, nums, new ArrayList<>()); + return res; + } + + private void backtrack(int i, int[] nums, List subset) { + int xorr = 0; + for (int num : subset) xorr ^= num; + res += xorr; + + for (int j = i; j < nums.length; j++) { + subset.add(nums[j]); + backtrack(j + 1, nums, subset); + subset.remove(subset.size() - 1); + } + } +} +``` + +```cpp +class Solution { +public: + int subsetXORSum(vector& nums) { + int res = 0; + vector subset; + + function backtrack = [&](int i) { + int xorr = 0; + for (int num : subset) xorr ^= num; + res += xorr; + + for (int j = i; j < nums.size(); ++j) { + subset.push_back(nums[j]); + backtrack(j + 1); + subset.pop_back(); + } + }; + + backtrack(0); + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + subsetXORSum(nums) { + let res = 0; + + const backtrack = (i, subset) => { + let xorr = 0; + for (let num of subset) xorr ^= num; + res += xorr; + + for (let j = i; j < nums.length; j++) { + subset.push(nums[j]); + backtrack(j + 1, subset); + subset.pop(); + } + }; + + backtrack(0, []); + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * 2 ^ n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Recursion + +::tabs-start + +```python +class Solution: + def subsetXORSum(self, nums: List[int]) -> int: + def dfs(i, total): + if i == len(nums): + return total + return dfs(i + 1, total ^ nums[i]) + dfs(i + 1, total) + + return dfs(0, 0) +``` + +```java +public class Solution { + public int subsetXORSum(int[] nums) { + return dfs(nums, 0, 0); + } + + private int dfs(int[] nums, int i, int total) { + if (i == nums.length) { + return total; + } + return dfs(nums, i + 1, total ^ nums[i]) + dfs(nums, i + 1, total); + } +} +``` + +```cpp +class Solution { +public: + int subsetXORSum(vector& nums) { + return dfs(nums, 0, 0); + } + +private: + int dfs(vector& nums, int i, int total) { + if (i == nums.size()) { + return total; + } + return dfs(nums, i + 1, total ^ nums[i]) + dfs(nums, i + 1, total); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + subsetXORSum(nums) { + const dfs = (i, total) => { + if (i === nums.length) { + return total; + } + return dfs(i + 1, total ^ nums[i]) + dfs(i + 1, total); + }; + + return dfs(0, 0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(2 ^ n)$ +* Space complexity: $O(n)$ for recursion stack. + +--- + +## 3. Bit Manipulation + +::tabs-start + +```python +class Solution: + def subsetXORSum(self, nums: List[int]) -> int: + n = len(nums) + res = 0 + + for mask in range(1 << n): + xorr = 0 + for i in range(n): + if mask & (1 << i): + xorr ^= nums[i] + res += xorr + + return res +``` + +```java +public class Solution { + public int subsetXORSum(int[] nums) { + int n = nums.length; + int res = 0; + + for (int mask = 0; mask < (1 << n); mask++) { + int xorr = 0; + for (int i = 0; i < n; i++) { + if ((mask & ( 1 << i)) != 0) { + xorr ^= nums[i]; + } + } + res += xorr; + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int subsetXORSum(vector& nums) { + int n = nums.size(); + int res = 0; + + for (int mask = 0; mask < (1 << n); mask++) { + int xorr = 0; + for (int i = 0; i < n; i++) { + if ((mask & ( 1 << i)) != 0) { + xorr ^= nums[i]; + } + } + res += xorr; + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + subsetXORSum(nums) { + const n = nums.length; + let res = 0; + + for (let mask = 0; mask < (1 << n); mask++) { + let xorr = 0; + for (let i = 0; i < n; i++) { + if ((mask & ( 1 << i)) !== 0) { + xorr ^= nums[i]; + } + } + res += xorr; + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * 2 ^ n)$ +* Space complexity: $O(1)$ extra space. + +--- + +## 4. Bit Manipulation (Optimal) + +::tabs-start + +```python +class Solution: + def subsetXORSum(self, nums: List[int]) -> int: + res = 0 + for num in nums: + res |= num + return res << (len(nums) - 1) +``` + +```java +public class Solution { + public int subsetXORSum(int[] nums) { + int res = 0; + for (int num : nums) { + res |= num; + } + return res << (nums.length - 1); + } +} +``` + +```cpp +class Solution { +public: + int subsetXORSum(vector& nums) { + int res = 0; + for (int& num : nums) { + res |= num; + } + return res << (nums.size() - 1); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + subsetXORSum(nums) { + let res = 0; + for (let num of nums) { + res |= num; + } + return res << (nums.length - 1); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. \ No newline at end of file diff --git a/articles/verifying-an-alien-dictionary.md b/articles/verifying-an-alien-dictionary.md new file mode 100644 index 000000000..7ab6ac58d --- /dev/null +++ b/articles/verifying-an-alien-dictionary.md @@ -0,0 +1,211 @@ +## 1. Sorting + +::tabs-start + +```python +class Solution: + def isAlienSorted(self, words: List[str], order: str) -> bool: + order_index = {c: i for i, c in enumerate(order)} + + def compare(word): + return [order_index[c] for c in word] + + return words == sorted(words, key=compare) +``` + +```java +public class Solution { + public boolean isAlienSorted(String[] words, String order) { + int[] orderIndex = new int[26]; + for (int i = 0; i < order.length(); i++) + orderIndex[order.charAt(i) - 'a'] = i; + + Comparator compare = (w1, w2) -> { + for (int i = 0; i < Math.min(w1.length(), w2.length()); i++) { + if (w1.charAt(i) != w2.charAt(i)) + return orderIndex[w1.charAt(i) - 'a'] - orderIndex[w2.charAt(i) - 'a']; + } + return w1.length() - w2.length(); + }; + + String[] sortedWords = words.clone(); + Arrays.sort(sortedWords, compare); + return Arrays.equals(words, sortedWords); + } +} +``` + +```cpp +class Solution { +public: + bool isAlienSorted(vector& words, string order) { + int orderIndex[26]; + for (int i = 0; i < order.size(); ++i) + orderIndex[order[i] - 'a'] = i; + + auto compare = [&](const string &a, const string &b) { + for (int i = 0; i < min(a.size(), b.size()); ++i) { + if (a[i] != b[i]) + return orderIndex[a[i] - 'a'] < orderIndex[b[i] - 'a']; + } + return a.size() < b.size(); + }; + + return is_sorted(words.begin(), words.end(), compare); + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} words + * @param {string} order + * @return {boolean} + */ + isAlienSorted(words, order) { + let orderIndex = new Array(26).fill(0); + for (let i = 0; i < order.length; i++) { + orderIndex[order.charCodeAt(i) - 97] = i; + } + + const compare = (w1, w2) => { + for (let i = 0; i < Math.min(w1.length, w2.length); i++) { + if (w1[i] !== w2[i]) { + return orderIndex[w1.charCodeAt(i) - 97] - orderIndex[w2.charCodeAt(i) - 97]; + } + } + return w1.length - w2.length; + }; + + let sortedWords = [...words].sort(compare); + return words.join() === sortedWords.join(); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * m\log n)$ +* Space complexity: $O(n * m)$ + +> Where $n$ is the number of words and $m$ is the average length of a word. + +--- + +## 2. Comparing adjacent words + +::tabs-start + +```python +class Solution: + def isAlienSorted(self, words: List[str], order: str) -> bool: + order_index = {c: i for i, c in enumerate(order)} + + for i in range(len(words) - 1): + w1, w2 = words[i], words[i + 1] + + for j in range(len(w1)): + if j == len(w2): + return False + + if w1[j] != w2[j]: + if order_index[w1[j]] > order_index[w2[j]]: + return False + break + return True +``` + +```java +public class Solution { + public boolean isAlienSorted(String[] words, String order) { + int[] orderIndex = new int[26]; + for (int i = 0; i < order.length(); i++) + orderIndex[order.charAt(i) - 'a'] = i; + + for (int i = 0; i < words.length - 1; i++) { + String w1 = words[i], w2 = words[i + 1]; + int j = 0; + + for (; j < w1.length(); j++) { + if (j == w2.length()) return false; + if (w1.charAt(j) != w2.charAt(j)) { + if (orderIndex[w1.charAt(j) - 'a'] > orderIndex[w2.charAt(j) - 'a']) { + return false; + } + break; + } + } + } + return true; + } +} +``` + +```cpp +class Solution { +public: + bool isAlienSorted(vector& words, string order) { + int orderIndex[26] = {0}; + for (int i = 0; i < order.size(); ++i) + orderIndex[order[i] - 'a'] = i; + + for (int i = 0; i < words.size() - 1; ++i) { + string w1 = words[i], w2 = words[i + 1]; + int j = 0; + + for (; j < w1.size(); ++j) { + if (j == w2.size()) return false; + if (w1[j] != w2[j]) { + if (orderIndex[w1[j] - 'a'] > orderIndex[w2[j] - 'a']) + return false; + break; + } + } + } + return true; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} words + * @param {string} order + * @return {boolean} + */ + isAlienSorted(words, order) { + let orderIndex = new Array(26).fill(0); + for (let i = 0; i < order.length; i++) { + orderIndex[order.charCodeAt(i) - 97] = i; + } + + for (let i = 0; i < words.length - 1; i++) { + let w1 = words[i], w2 = words[i + 1]; + + for (let j = 0; j < w1.length; j++) { + if (j === w2.length) return false; + + if (w1[j] !== w2[j]) { + if (orderIndex[w1.charCodeAt(j) - 97] > orderIndex[w2.charCodeAt(j) - 97]) + return false; + break; + } + } + } + return true; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * m)$ +* Space complexity: $O(1)$ since we have $26$ different characters. + +> Where $n$ is the number of words and $m$ is the average length of a word. \ No newline at end of file From d15fd78fb88e024d57790cc6ac143a45ae72a48f Mon Sep 17 00:00:00 2001 From: Sri Hari <94112314+Srihari2222@users.noreply.github.com> Date: Thu, 19 Dec 2024 04:12:31 +0530 Subject: [PATCH 18/45] Batch-4/Neetcode-150/Added-hints (#3768) --- hints/cheapest-flight-path.md | 31 ++++++++++++++++++++ hints/climbing-stairs.md | 39 ++++++++++++++++++++++++++ hints/coin-change.md | 31 ++++++++++++++++++++ hints/decode-ways.md | 39 ++++++++++++++++++++++++++ hints/foreign-dictionary.md | 39 ++++++++++++++++++++++++++ hints/house-robber-ii.md | 39 ++++++++++++++++++++++++++ hints/house-robber.md | 39 ++++++++++++++++++++++++++ hints/longest-palindromic-substring.md | 39 ++++++++++++++++++++++++++ hints/min-cost-climbing-stairs.md | 39 ++++++++++++++++++++++++++ hints/palindromic-substrings.md | 39 ++++++++++++++++++++++++++ 10 files changed, 374 insertions(+) create mode 100644 hints/cheapest-flight-path.md create mode 100644 hints/climbing-stairs.md create mode 100644 hints/coin-change.md create mode 100644 hints/decode-ways.md create mode 100644 hints/foreign-dictionary.md create mode 100644 hints/house-robber-ii.md create mode 100644 hints/house-robber.md create mode 100644 hints/longest-palindromic-substring.md create mode 100644 hints/min-cost-climbing-stairs.md create mode 100644 hints/palindromic-substrings.md diff --git a/hints/cheapest-flight-path.md b/hints/cheapest-flight-path.md new file mode 100644 index 000000000..85fcd7ec4 --- /dev/null +++ b/hints/cheapest-flight-path.md @@ -0,0 +1,31 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution as good or better than O(n + (m * k)) time and O(n) space, where n is the number of cities, m is the number of flights, and k is the number of stops. +

+
+ +
+
+ Hint 1 +

+ Consider this as a graph problem where the cities are nodes and the flights are edges connecting two cities, with the ticket cost as the edge weight. Can you think of a shortest path algorithm to solve the problem? Perhaps a better algorithm than Dijkstra's that can intuitively handle the k stops condition. +

+
+ +
+
+ Hint 2 +

+ We can use the Bellman-Ford algorithm. Initialize a prices array of size n with Infinity, setting prices[source] = 0. THese values describe the cost to reach a city from the source city. Iterate (k + 1) times (stops are 0-indexed), updating the cost to each city by extending paths from cities with valid costs. We only update the cost for a city if it is less than the previous cost. How would you implement this? +

+
+ +
+
+ Hint 3 +

+ At each level of iteration, we go through the given flights and use them to update the price array with the minimum costs compared to the previous level. We use a temporary prices array at each level to store the updated costs. After completing all levels, we return the result stored in prices[dst]. If that value is Infinity, we return -1 instead. +

+
\ No newline at end of file diff --git a/hints/climbing-stairs.md b/hints/climbing-stairs.md new file mode 100644 index 000000000..78e5ce36b --- /dev/null +++ b/hints/climbing-stairs.md @@ -0,0 +1,39 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution as good or better than O(n) time and O(n) space, where n is the number of steps. +

+
+ +
+
+ Hint 1 +

+ At each step, we have two choices: climb one step or climb two steps. We can solve this by considering both options and picking the minimum using recursion. However, this results in O(2^n) time complexity. Can you think of a better approach? Perhaps, try to avoid the repeated work of calling recursion more than once with same parameters. +

+
+ +
+
+ Hint 2 +

+ This is a Dynamic Programming problem. We can use Memoization to avoid repeated work. Create an n-sized array cache to store the results of recursive calls. When the recursion is called with specific parameters, return the stored value if it has already been computed. How would you implement this? +

+
+ +
+
+ Hint 3 +

+ We start the initial recursion with i = 0, indicating that we are at position i. We first check if the current recursion with the given i is already cached. If it is, we immediately return the stored value. Otherwise, we perform the recursion, store the result in the cache, and then return it. Can you think of the base condition to stop the recursion? +

+
+ +
+
+ Hint 4 +

+ At each recursion, we perform two recursive calls: one for climbing one step and another for climbing two steps. The minimum return value between the two is the result for the current recursion. The base condition is to return 0 if i == n. This is a one-dimensional dynamic programming problem, which can be further optimized using more advanced techniques. +

+
\ No newline at end of file diff --git a/hints/coin-change.md b/hints/coin-change.md new file mode 100644 index 000000000..c8522e7cb --- /dev/null +++ b/hints/coin-change.md @@ -0,0 +1,31 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution with O(n * t) time and O(t) space, where n is the number of coins and t is the given amount. +

+
+ +
+
+ Hint 1 +

+ Think of this problem in terms of recursion and try to visualize the decision tree, as there are multiple choices at each step. We start with the given amount. At each step of recursion, we have n coins and branch into paths using coins that are less than or equal to the current amount. Can you express this in terms of a recurrence relation? Also, try to determine the base condition to stop the recursion. +

+
+ +
+
+ Hint 2 +

+ If the amount is 0, we return 0 coins. The recurrence relation can be expressed as min(1 + dfs(amount - coins[i])), where we return the minimum coins among all paths. This results in an O(n ^ t) solution, where n is the number of coins and t is the total amount. Can you think of a better approach? Perhaps consider the repeated work and find a way to avoid it. +

+
+ +
+
+ Hint 3 +

+ We can use memoization to avoid the repeated work of calculating the result for each recursive call. A hash map or an array of size t can be used to cache the computed values for a specific amount. At each recursion step, we iterate over every coin and extend only the valid paths. If a result has already been computed, we return it from the cache instead of recalculating it. +

+
\ No newline at end of file diff --git a/hints/decode-ways.md b/hints/decode-ways.md new file mode 100644 index 000000000..56c40d46a --- /dev/null +++ b/hints/decode-ways.md @@ -0,0 +1,39 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution as good or better than O(n) time and O(n) space, where n is the length of the given string. +

+
+ +
+
+ Hint 1 +

+ The characters A through Z are mapped to the numbers from 1 to 26. A mapped number can have at most 2 digits. In the given string of digits, we can explore all possible decodings by combining one or two consecutive digits. Think of this in terms of a decision tree and explore all paths. Can you derive a recurrence relation for this? +

+
+ +
+
+ Hint 2 +

+ Iterate over the string with index i. At each index, we have two choices: decode the current digit as a character with its mapped value, or combine the current digit with the next digit to form a two-digit value. The recurrence relation can be expressed as dfs(i + 1) + dfs(i + 2) where dfs is the recursive function. Also, consider edge cases, as not every two-digit number or a number with a leading zero is valid. How would you implement this? +

+
+ +
+
+ Hint 3 +

+ A brute-force recursive solution would result in O(2^n) time complexity. Can you think of a better way? Perhaps you should consider the repeated work of calling the recursion multiple times with the same parameter values and find a way to avoid this. Also, can you think about the base condition of this recursive function? +

+
+ +
+
+ Hint 4 +

+ The base condition is to return 1 if i goes out of bounds. If the current digit is '0', return 0, as no character maps to '0', making the string invalid. Use memoization to avoid repeated work by caching recursion results in an array or hash map and returning the stored value when the result is already calculated. +

+
\ No newline at end of file diff --git a/hints/foreign-dictionary.md b/hints/foreign-dictionary.md new file mode 100644 index 000000000..9837818a0 --- /dev/null +++ b/hints/foreign-dictionary.md @@ -0,0 +1,39 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution with O(N + V + E) time and O(V + E) space, where N is the sum of the lengths of all the strings, V is the number of unique characters (vertices), and E is the number of edges. +

+
+ +
+
+ Hint 1 +

+ Can you think of this as a graph problem? Characters from a through z are nodes. What could the edges represent here? How can you create edges from the given words? Perhaps you should try comparing two adjacent words. +

+
+ +
+
+ Hint 2 +

+ The relative ordering of the characters can be treated as edges. For example, consider the words ordered as ["ape", "apple"]. "ape" comes before "apple", which indicates that 'e' is a predecessor of 'p'. Therefore, there is a directed edge e -> p, and this dependency should be valid across all the words. In this way, we can build an adjacency list by comparing adjacent words. Can you think of an algorithm that is suitable to find a valid ordering? +

+
+ +
+
+ Hint 3 +

+ We can use Topological Sort to ensure every node appears after its predecessor. Using DFS, we traverse the graph built from the adjacency list. A visited map tracks nodes in the current DFS path: False means not in the path, and True means in the path. If any DFS call returns True, it indicates a cycle and we return immediately. How do we extract the ordering from this DFS? +

+
+ +
+
+ Hint 4 +

+ When we visit a node and its children and don't find a cycle, we mark the node as False in the map and append it to the result, treating this as a post-order traversal. If we find a cycle, we return an empty string; otherwise, we return the result list. +

+
\ No newline at end of file diff --git a/hints/house-robber-ii.md b/hints/house-robber-ii.md new file mode 100644 index 000000000..1cc52b34d --- /dev/null +++ b/hints/house-robber-ii.md @@ -0,0 +1,39 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution as good or better than O(n) time and O(n) space, where n is the number of houses. +

+
+ +
+
+ Hint 1 +

+ First, consider solving the problem to get the maximum money after robbing without the condition that 'the first and last houses are adjacent'. Can you express this using a recurrence relation? Perhaps you could draw a decision tree, as at each step, you can either rob the current house and skip the next one or skip the current house and move to the next. +

+
+ +
+
+ Hint 2 +

+ The recurrence relation can be expressed as max(nums[i] + dfs(i + 2), dfs(i + 1)), where i is the current house and dfs is the recursive function. The base condition for this recursion would be to return 0 when i goes out of bounds. This solution results in O(2^n) time complexity because, at each recursive step, we branch into two paths. Can you think of a way to avoid recalculating the result for the same recursive call multiple times? +

+
+ +
+
+ Hint 3 +

+ We can use memoization to store the result of a recursive function in a hash map or an array and immediately return this value when the function is called again with the same parameter values. How would you implement this? How would you solve the problem if the first and last houses are adjacent to each other? Perhaps you should consider skipping any one house between the two. +

+
+ +
+
+ Hint 4 +

+ We can create two arrays from the given array. The first will include houses from the first house to the second-to-last house, and the second will include houses from the second house to the last house. We can run the recursive function on both arrays independently and return the maximum result between the two. Advanced techniques such as bottom-up dynamic programming can further optimize the solution. +

+
\ No newline at end of file diff --git a/hints/house-robber.md b/hints/house-robber.md new file mode 100644 index 000000000..2a1e5d7c8 --- /dev/null +++ b/hints/house-robber.md @@ -0,0 +1,39 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution as good or better than O(n) time and O(n) space, where n is the number of houses. +

+
+ +
+
+ Hint 1 +

+ Can you think of this problem in terms of recursion? Consider drawing a decision tree where, at each step, we can choose to rob the house or skip it. If we rob the current house, we cannot rob the next or the previous house. Can you derive a recurrence relation to solve the problem? +

+
+ +
+
+ Hint 2 +

+ We can recursively start from the first house and branch paths accordingly. If we rob the current house, we skip the next house; otherwise, we move to the next house. The recurrence relation can be expressed as max(nums[i] + dfs(i + 2), dfs(i + 1)), where i is the current house and dfs is the recursive function. Can you determine the base condition to stop the recursion? +

+
+ +
+
+ Hint 3 +

+ The base condition would be to return 0 when i goes out of bounds. This recursion can leads to O(2^n) time solution. Can you think of a better way? Maybe you should try to avoid recalculating the result for a recursive call. +

+
+ +
+
+ Hint 4 +

+ We can use Memoization to avoid recalculating the result multiple times for a recursive call. By storing the result of each recursive call in a hash map or an array using i as the parameter, we can immediately return the stored result if the recursion is called with the same i value again. Further optimization can be achieved using advanced techniques like Bottom-Up dynamic programming. +

+
\ No newline at end of file diff --git a/hints/longest-palindromic-substring.md b/hints/longest-palindromic-substring.md new file mode 100644 index 000000000..9d4dc9eb9 --- /dev/null +++ b/hints/longest-palindromic-substring.md @@ -0,0 +1,39 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution as good or better than O(n^2) time and O(1) space, where n is the length of the given string. +

+
+ +
+
+ Hint 1 +

+ A brute-force solution would be to check if every substring is a palindrome and return the maximum length among all the palindromic substring lengths. This would be an O(n^3) time solution. Can you think of a better way? Perhaps you should consider thinking in terms of the center of a palindrome. +

+
+ +
+
+ Hint 2 +

+ Iterate over the string with index i and treat the current character as the center. For this character, try to extend outward to the left and right simultaneously, but only if both characters are equal. Update the result variable accordingly. How would you implement this? Can you consider both cases: even-length and odd-length palindromes? +

+
+ +
+
+ Hint 3 +

+ Maintain two variables, resLen and res, which denote the length of the longest palindrome and the start index of that palindrome, respectively. At each index, you can create an odd-length palindrome starting at that index extending outward from both its left and right indices, i.e., i - 1 and i + 1. How can you find the even-length palindrome for this index? +

+
+ +
+
+ Hint 4 +

+ For an even-length palindrome, consider expanding from indices i and i + 1. This two-pointer approach, extending from the center of the palindrome, will help find all palindromic substrings in the given string. Update the two result variables and return the substring starting at res with a length of resLen. +

+
\ No newline at end of file diff --git a/hints/min-cost-climbing-stairs.md b/hints/min-cost-climbing-stairs.md new file mode 100644 index 000000000..258121826 --- /dev/null +++ b/hints/min-cost-climbing-stairs.md @@ -0,0 +1,39 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution as good or better than O(n) time and O(n) space, where n is the number of steps on the staircase. +

+
+ +
+
+ Hint 1 +

+ Can you find the recurrence relation to solve the problem, given that at each step we have two options: going one step or two steps? Consider drawing a decision tree where we branch into two paths at each step. By exploring every path, we can get the minimum cost. However, this results in an O(2^n) time solution. Can you think of a better approach? Is there any repeated work in the decision tree that we can optimize? +

+
+ +
+
+ Hint 2 +

+ The recurrence relation can be expressed as cost[i] + min(dfs(i + 1), dfs(i + 2)), where i is the current position and dfs is the recursive function. To avoid recalculating the result of a recursive call multiple times, we can use Memoization. Initialize a cache array of size n, where n is the number of steps on the staircase. How would you implement this? +

+
+ +
+
+ Hint 3 +

+ We start the recursion from positions 0 and 1. At each recursive step, before computing the result, we check if the result for the current position has already been calculated. If it has, we return the stored value. Otherwise, we calculate the result for the current position, store it in the cache, and then return the result. What can be the base condition for this recursion to stop? +

+
+ +
+
+ Hint 4 +

+ The base condition would be to return 0 if we are at the top of the staircase i >= n. This is a one-dimensional dynamic programming problem. We can further optimize the memoization solution by using advanced techniques such as Bottom-Up dynamic programming based on the recurrance relation. +

+
\ No newline at end of file diff --git a/hints/palindromic-substrings.md b/hints/palindromic-substrings.md new file mode 100644 index 000000000..5e994313c --- /dev/null +++ b/hints/palindromic-substrings.md @@ -0,0 +1,39 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution as good or better than O(n^2) time and O(1) space, where n is the length of the given string. +

+
+ +
+
+ Hint 1 +

+ A brute-force solution would be to check if every substring is a palindrome and return the total number of palindromic substrings. This would be an O(n^3) time solution. Can you think of a better way? Perhaps you should consider thinking in terms of the center of a palindrome. +

+
+ +
+
+ Hint 2 +

+ Iterate over the string with index i and treat the current character as the center. For this character, try to extend outward to the left and right simultaneously, but only if both characters are equal. At each iteration, we increment the count of palindromes. How would you implement this? Can you consider both cases: even-length and odd-length palindromes? +

+
+ +
+
+ Hint 3 +

+ Initialize a variable res to track the count of palindromes. At each index, create an odd-length palindrome starting at that index extending outward from both its left and right indices, i.e., i - 1 and i + 1. How can you find the even-length palindrome for this index? +

+
+ +
+
+ Hint 4 +

+ For an even-length palindrome, consider expanding from indices i and i + 1. This two-pointer approach, extending from the center of the palindrome, will help find all palindromic substrings in the given string and return its count. +

+
\ No newline at end of file From ae946f18530589437d1e5236d3ea6cca7498226b Mon Sep 17 00:00:00 2001 From: Sri Hari <94112314+Srihari2222@users.noreply.github.com> Date: Sun, 22 Dec 2024 23:08:02 +0530 Subject: [PATCH 19/45] Sri Hari: Batch-4/Neetcode-All/Added-articles (#3770) * Batch-4/Neetcode-All/Added-articles * Batch-4/Neetcode-150/Added-articles --- articles/cheapest-flight-path.md | 6 +- ...here-is-a-valid-partition-for-the-array.md | 451 +++++ articles/combination-sum-iv.md | 413 +++++ articles/delete-and-earn.md | 586 +++++++ articles/maximum-subarray-min-product.md | 967 +++++++++++ articles/minimum-cost-for-tickets.md | 956 +++++++++++ articles/perfect-squares.md | 548 ++++++ articles/queue-reconstruction-by-height.md | 1185 +++++++++++++ articles/range-sum-query-mutable.md | 1513 +++++++++++++++++ articles/sliding-window-maximum.md | 296 ++-- articles/triangle.md | 535 ++++++ 11 files changed, 7281 insertions(+), 175 deletions(-) create mode 100644 articles/check-if-there-is-a-valid-partition-for-the-array.md create mode 100644 articles/combination-sum-iv.md create mode 100644 articles/delete-and-earn.md create mode 100644 articles/maximum-subarray-min-product.md create mode 100644 articles/minimum-cost-for-tickets.md create mode 100644 articles/perfect-squares.md create mode 100644 articles/queue-reconstruction-by-height.md create mode 100644 articles/range-sum-query-mutable.md create mode 100644 articles/triangle.md diff --git a/articles/cheapest-flight-path.md b/articles/cheapest-flight-path.md index 95928ed07..6c23351be 100644 --- a/articles/cheapest-flight-path.md +++ b/articles/cheapest-flight-path.md @@ -265,7 +265,7 @@ class Solution { * Time complexity: $O((n + m) * k)$ * Space complexity: $O(n * k)$ -> Where $n$ is the number of flights, $m$ is the number of edges and $k$ is the number of stops. +> Where $n$ is the number of cities, $m$ is the number of flights and $k$ is the number of stops. --- @@ -478,7 +478,7 @@ class Solution { * Time complexity: $O(n + (m * k))$ * Space complexity: $O(n)$ -> Where $n$ is the number of flights, $m$ is the number of edges and $k$ is the number of stops. +> Where $n$ is the number of cities, $m$ is the number of flights and $k$ is the number of stops. --- @@ -734,4 +734,4 @@ class Solution { * Time complexity: $O(n * k)$ * Space complexity: $O(n + m)$ -> Where $n$ is the number of flights, $m$ is the number of edges and $k$ is the number of stops. \ No newline at end of file +> Where $n$ is the number of cities, $m$ is the number of flights and $k$ is the number of stops. \ No newline at end of file diff --git a/articles/check-if-there-is-a-valid-partition-for-the-array.md b/articles/check-if-there-is-a-valid-partition-for-the-array.md new file mode 100644 index 000000000..e855fd84d --- /dev/null +++ b/articles/check-if-there-is-a-valid-partition-for-the-array.md @@ -0,0 +1,451 @@ +## 1. Recursion + +::tabs-start + +```python +class Solution: + def validPartition(self, nums: List[int]) -> bool: + def dfs(i): + if i == len(nums): + return True + + res = False + if i < len(nums) - 1 and nums[i] == nums[i + 1]: + res = dfs(i + 2) + if i < len(nums) - 2: + if ((nums[i] == nums[i + 1] == nums[i + 2]) or + (nums[i] + 1 == nums[i + 1] and nums[i + 1] + 1 == nums[i + 2]) + ): + res = res or dfs(i + 3) + return res + + return dfs(0) +``` + +```java +public class Solution { + public boolean validPartition(int[] nums) { + return dfs(nums, 0); + } + + private boolean dfs(int[] nums, int i) { + if (i == nums.length) return true; + + boolean res = false; + if (i < nums.length - 1 && nums[i] == nums[i + 1]) { + res = dfs(nums, i + 2); + } + if (i < nums.length - 2) { + if ((nums[i] == nums[i + 1] && nums[i + 1] == nums[i + 2]) || + (nums[i] + 1 == nums[i + 1] && nums[i + 1] + 1 == nums[i + 2])) { + res = res || dfs(nums, i + 3); + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + bool validPartition(vector& nums) { + return dfs(nums, 0); + } + +private: + bool dfs(vector& nums, int i) { + if (i == nums.size()) return true; + + bool res = false; + if (i < nums.size() - 1 && nums[i] == nums[i + 1]) { + res = dfs(nums, i + 2); + } + if (i < nums.size() - 2) { + if ((nums[i] == nums[i + 1] && nums[i + 1] == nums[i + 2]) || + (nums[i] + 1 == nums[i + 1] && nums[i + 1] + 1 == nums[i + 2])) { + res = res || dfs(nums, i + 3); + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {boolean} + */ + validPartition(nums) { + const dfs = (i) => { + if (i === nums.length) return true; + + let res = false; + if (i < nums.length - 1 && nums[i] === nums[i + 1]) { + res = dfs(i + 2); + } + if (i < nums.length - 2) { + if ((nums[i] === nums[i + 1] && nums[i + 1] === nums[i + 2]) || + (nums[i] + 1 === nums[i + 1] && nums[i + 1] + 1 === nums[i + 2])) { + res = res || dfs(i + 3); + } + } + return res; + }; + + return dfs(0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(2 ^ n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def validPartition(self, nums: List[int]) -> bool: + dp = { len(nums) : True } + def dfs(i): + if i in dp: + return dp[i] + + res = False + if i < len(nums) - 1 and nums[i] == nums[i + 1]: + res = dfs(i + 2) + if i < len(nums) - 2: + if ((nums[i] == nums[i + 1] == nums[i + 2]) or + (nums[i] + 1 == nums[i + 1] and nums[i + 1] + 1 == nums[i + 2]) + ): + res = res or dfs(i + 3) + + dp[i] = res + return res + + return dfs(0) +``` + +```java +public class Solution { + private Map memo = new HashMap<>(); + + public boolean validPartition(int[] nums) { + return dfs(nums, 0); + } + + private boolean dfs(int[] nums, int i) { + if (i == nums.length) return true; + if (memo.containsKey(i)) return memo.get(i); + + boolean res = false; + if (i < nums.length - 1 && nums[i] == nums[i + 1]) { + res = dfs(nums, i + 2); + } + if (i < nums.length - 2) { + if ((nums[i] == nums[i + 1] && nums[i + 1] == nums[i + 2]) || + (nums[i] + 1 == nums[i + 1] && nums[i + 1] + 1 == nums[i + 2])) { + res = res || dfs(nums, i + 3); + } + } + + memo.put(i, res); + return res; + } +} +``` + +```cpp +class Solution { +public: + unordered_map memo; + + bool validPartition(vector& nums) { + return dfs(nums, 0); + } + +private: + bool dfs(vector& nums, int i) { + if (i == nums.size()) return true; + if (memo.count(i)) return memo[i]; + + bool res = false; + if (i < nums.size() - 1 && nums[i] == nums[i + 1]) { + res = dfs(nums, i + 2); + } + if (i < nums.size() - 2) { + if ((nums[i] == nums[i + 1] && nums[i + 1] == nums[i + 2]) || + (nums[i] + 1 == nums[i + 1] && nums[i + 1] + 1 == nums[i + 2])) { + res = res || dfs(nums, i + 3); + } + } + + return memo[i] = res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {boolean} + */ + validPartition(nums) { + const memo = new Map(); + + const dfs = (i) => { + if (i === nums.length) return true; + if (memo.has(i)) return memo.get(i); + + let res = false; + if (i < nums.length - 1 && nums[i] === nums[i + 1]) { + res = dfs(i + 2); + } + if (i < nums.length - 2) { + if ((nums[i] === nums[i + 1] && nums[i + 1] === nums[i + 2]) || + (nums[i] + 1 === nums[i + 1] && nums[i + 1] + 1 === nums[i + 2])) { + res = res || dfs(i + 3); + } + } + + memo.set(i, res); + return res; + }; + + return dfs(0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def validPartition(self, nums: List[int]) -> bool: + dp = [False] * (len(nums) + 1) + dp[0] = True + + for i in range(2, len(nums) + 1): + if nums[i - 1] == nums[i - 2]: + dp[i] = dp[i] or dp[i - 2] + if i > 2 and ((nums[i - 1] == nums[i - 2] == nums[i - 3]) or + (nums[i - 3] + 1 == nums[i - 2] and nums[i - 2] + 1 == nums[i - 1])): + dp[i] = dp[i] or dp[i - 3] + + return dp[len(nums)] +``` + +```java +public class Solution { + public boolean validPartition(int[] nums) { + boolean[] dp = new boolean[nums.length + 1]; + dp[0] = true; + + for (int i = 2; i <= nums.length; i++) { + if (nums[i - 1] == nums[i - 2]) { + dp[i] = dp[i] || dp[i - 2]; + } + if (i > 2 && ((nums[i - 1] == nums[i - 2] && nums[i - 2] == nums[i - 3]) || + (nums[i - 3] + 1 == nums[i - 2] && nums[i - 2] + 1 == nums[i - 1]))) { + dp[i] = dp[i] || dp[i - 3]; + } + } + + return dp[nums.length]; + } +} +``` + +```cpp +class Solution { +public: + bool validPartition(vector& nums) { + vector dp(nums.size() + 1, false); + dp[0] = true; + + for (int i = 2; i <= nums.size(); i++) { + if (nums[i - 1] == nums[i - 2]) { + dp[i] = dp[i] || dp[i - 2]; + } + if (i > 2 && ((nums[i - 1] == nums[i - 2] && nums[i - 2] == nums[i - 3]) || + (nums[i - 3] + 1 == nums[i - 2] && nums[i - 2] + 1 == nums[i - 1]))) { + dp[i] = dp[i] || dp[i - 3]; + } + } + + return dp[nums.size()]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {boolean} + */ + validPartition(nums) { + const dp = Array(nums.length + 1).fill(false); + dp[0] = true; + + for (let i = 2; i <= nums.length; i++) { + if (nums[i - 1] === nums[i - 2]) { + dp[i] = dp[i] || dp[i - 2]; + } + if (i > 2 && ((nums[i - 1] === nums[i - 2] && nums[i - 2] === nums[i - 3]) || + (nums[i - 3] + 1 === nums[i - 2] && nums[i - 2] + 1 === nums[i - 1]))) { + dp[i] = dp[i] || dp[i - 3]; + } + } + + return dp[nums.length]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 4. Dynamic Programming (Space Optimized) + +::tabs-start + +```python +class Solution: + def validPartition(self, nums: List[int]) -> bool: + dp = [False, True, True] + + for i in range(len(nums) - 2, -1, -1): + dp1 = dp[0] + if nums[i] == nums[i + 1] and dp[1]: + dp[0] = True + elif i < len(nums) - 2 and dp[2] and ( + (nums[i] == nums[i + 1] == nums[i + 2]) or + (nums[i] + 1 == nums[i + 1] and nums[i + 1] == nums[i + 2] - 1) + ): + dp[0] = True + else: + dp[0] = False + dp[2] = dp[1] + dp[1] = dp1 + + return dp[0] +``` + +```java +public class Solution { + public boolean validPartition(int[] nums) { + boolean[] dp = new boolean[3]; + dp[2] = true; + dp[1] = true; + dp[0] = false; + + for (int i = nums.length - 2; i >= 0; i--) { + boolean dp1 = dp[0]; + if (nums[i] == nums[i + 1] && dp[1]) { + dp[0] = true; + } else if (i < nums.length - 2 && dp[2] && + ((nums[i] == nums[i + 1] && nums[i] == nums[i + 2]) || + (nums[i] + 1 == nums[i + 1] && nums[i + 1] == nums[i + 2] - 1))) { + dp[0] = true; + } else { + dp[0] = false; + } + dp[2] = dp[1]; + dp[1] = dp1; + } + + return dp[0]; + } +} +``` + +```cpp +class Solution { +public: + bool validPartition(vector& nums) { + bool dp[3] = {false, true, true}; + + for (int i = nums.size() - 2; i >= 0; --i) { + bool dp1 = dp[0]; + if (nums[i] == nums[i + 1] && dp[1]) { + dp[0] = true; + } else if (i < nums.size() - 2 && dp[2] && + ((nums[i] == nums[i + 1] && nums[i] == nums[i + 2]) || + (nums[i] + 1 == nums[i + 1] && nums[i + 1] == nums[i + 2] - 1))) { + dp[0] = true; + } else { + dp[0] = false; + } + dp[2] = dp[1]; + dp[1] = dp1; + } + + return dp[0]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {boolean} + */ + validPartition(nums) { + let dp = [false, true, true]; + + for (let i = nums.length - 2; i >= 0; i--) { + let dp1 = dp[0]; + if (nums[i] === nums[i + 1] && dp[1]) { + dp[0] = true; + } else if (i < nums.length - 2 && dp[2] && + ((nums[i] === nums[i + 1] && nums[i] === nums[i + 2]) || + (nums[i] + 1 === nums[i + 1] && nums[i + 1] === nums[i + 2] - 1))) { + dp[0] = true; + } else { + dp[0] = false; + } + dp[2] = dp[1]; + dp[1] = dp1; + } + + return dp[0]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. \ No newline at end of file diff --git a/articles/combination-sum-iv.md b/articles/combination-sum-iv.md new file mode 100644 index 000000000..3a607251c --- /dev/null +++ b/articles/combination-sum-iv.md @@ -0,0 +1,413 @@ +## 1. Recursion + +::tabs-start + +```python +class Solution: + def combinationSum4(self, nums: List[int], target: int) -> int: + nums.sort() + + def dfs(total): + if total == 0: + return 1 + + res = 0 + for i in range(len(nums)): + if total < nums[i]: + break + res += dfs(total - nums[i]) + return res + + return dfs(target) +``` + +```java +public class Solution { + public int combinationSum4(int[] nums, int target) { + Arrays.sort(nums); + + return dfs(nums, target); + } + + private int dfs(int[] nums, int total) { + if (total == 0) { + return 1; + } + + int res = 0; + for (int num : nums) { + if (total < num) { + break; + } + res += dfs(nums, total - num); + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int combinationSum4(vector& nums, int target) { + sort(nums.begin(), nums.end()); + return dfs(nums, target); + } + + int dfs(vector& nums, int total) { + if (total == 0) { + return 1; + } + + int res = 0; + for (int num : nums) { + if (total < num) { + break; + } + res += dfs(nums, total - num); + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} target + * @return {number} + */ + combinationSum4(nums, target) { + nums.sort((a, b) => a - b); + + const dfs = (total) => { + if (total === 0) return 1; + + let res = 0; + for (let num of nums) { + if (total < num) break; + res += dfs(total - num); + } + return res; + }; + + return dfs(target); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ t)$ +* Space complexity: $O(t)$ + +> Where $n$ is the size of the array $nums$ and $t$ is the given target. + +--- + +## 2. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def combinationSum4(self, nums: List[int], target: int) -> int: + nums.sort() + memo = { 0 : 1 } + + def dfs(total): + if total in memo: + return memo[total] + + res = 0 + for num in nums: + if total < num: + break + res += dfs(total - num) + memo[total] = res + return res + + return dfs(target) +``` + +```java +public class Solution { + private Map memo; + + public int combinationSum4(int[] nums, int target) { + Arrays.sort(nums); + memo = new HashMap<>(); + memo.put(0, 1); + return dfs(nums, target); + } + + private int dfs(int[] nums, int total) { + if (memo.containsKey(total)) { + return memo.get(total); + } + + int res = 0; + for (int num : nums) { + if (total < num) { + break; + } + res += dfs(nums, total - num); + } + memo.put(total, res); + return res; + } +} +``` + +```cpp +class Solution { +private: + unordered_map memo; + +public: + int combinationSum4(vector& nums, int target) { + sort(nums.begin(), nums.end()); + memo[0] = 1; + return dfs(nums, target); + } + + int dfs(vector& nums, int total) { + if (memo.count(total)) { + return memo[total]; + } + + int res = 0; + for (int num : nums) { + if (total < num) { + break; + } + res += dfs(nums, total - num); + } + memo[total] = res; + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} target + * @return {number} + */ + combinationSum4(nums, target) { + nums.sort((a, b) => a - b); + const memo = { 0 : 1 }; + + const dfs = (total) => { + if (memo[total] !== undefined) { + return memo[total]; + } + + let res = 0; + for (let num of nums) { + if (total < num) break; + res += dfs(total - num); + } + memo[total] = res; + return res; + }; + + return dfs(target); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * t)$ +* Space complexity: $O(t)$ + +> Where $n$ is the size of the array $nums$ and $t$ is the given target. + +--- + +## 3. Dynamic Programming (Bottom-Up) - I + +::tabs-start + +```python +class Solution: + def combinationSum4(self, nums: List[int], target: int) -> int: + dp = { 0 : 1 } + + for total in range(1, target + 1): + dp[total] = 0 + for num in nums: + dp[total] += dp.get(total - num, 0) + + return dp[target] +``` + +```java +public class Solution { + public int combinationSum4(int[] nums, int target) { + Map dp = new HashMap<>(); + dp.put(0, 1); + + for (int total = 1; total <= target; total++) { + dp.put(total, 0); + for (int num : nums) { + dp.put(total, dp.get(total) + dp.getOrDefault(total - num, 0)); + } + } + return dp.get(target); + } +} +``` + +```cpp +class Solution { +public: + int combinationSum4(vector& nums, int target) { + unordered_map dp; + dp[0] = 1; + + for (int total = 1; total <= target; total++) { + dp[total] = 0; + for (int num : nums) { + if (total >= num) { + dp[total] += dp[total - num]; + } + } + if (dp[total] > INT_MAX) { + dp[total] = 0; + } + } + return dp[target]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} target + * @return {number} + */ + combinationSum4(nums, target) { + let dp = {0: 1}; + for (let total = 1; total <= target; total++) { + dp[total] = 0; + for (let num of nums) { + dp[total] += dp[total - num] || 0; + } + } + return dp[target]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * t)$ +* Space complexity: $O(t)$ + +> Where $n$ is the size of the array $nums$ and $t$ is the given target. + +--- + +## 4. Dynamic Programming (Bottom-Up) - II + +::tabs-start + +```python +class Solution: + def combinationSum4(self, nums: List[int], target: int) -> int: + nums.sort() + dp = defaultdict(int) + dp[target] = 1 + for total in range(target, 0, -1): + for num in nums: + if total < num: + break + dp[total - num] += dp[total] + return dp[0] +``` + +```java +public class Solution { + public int combinationSum4(int[] nums, int target) { + Arrays.sort(nums); + Map dp = new HashMap<>(); + dp.put(target, 1); + for (int total = target; total > 0; total--) { + for (int num : nums) { + if (total < num) break; + dp.put(total - num, dp.getOrDefault(total, 0) + dp.getOrDefault(total - num, 0)); + } + } + return dp.getOrDefault(0, 0); + } +} +``` + +```cpp +class Solution { +public: + int combinationSum4(vector& nums, int target) { + sort(nums.begin(), nums.end()); + unordered_map dp; + dp[target] = 1; + + for (int total = target; total > 0; total--) { + if (dp[total] == -1) continue; + for (auto& num : nums) { + if (total < num) break; + if (dp[total - num] + 0LL + dp[total] > INT_MAX) { + dp[total - num] = -1; + break; + } + dp[total - num] += dp[total]; + } + } + return dp[0]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} target + * @return {number} + */ + combinationSum4(nums, target) { + nums.sort((a, b) => a - b); + const dp = new Map(); + dp.set(target, 1); + + for (let total = target; total > 0; total--) { + for (const num of nums) { + if (total < num) break; + dp.set(total - num, (dp.get(total - num) || 0) + (dp.get(total) || 0)); + } + } + return dp.get(0) || 0; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * t)$ +* Space complexity: $O(t)$ + +> Where $n$ is the size of the array $nums$ and $t$ is the given target. \ No newline at end of file diff --git a/articles/delete-and-earn.md b/articles/delete-and-earn.md new file mode 100644 index 000000000..ae00ea5db --- /dev/null +++ b/articles/delete-and-earn.md @@ -0,0 +1,586 @@ +## 1. Recursion + +::tabs-start + +```python +class Solution: + def deleteAndEarn(self, nums: List[int]) -> int: + nums.sort() + + def dfs(i): + if i >= len(nums): + return 0 + + cur = nums[i] + pick = 0 + while i < len(nums) and nums[i] == cur: + pick += nums[i] + i += 1 + + res = dfs(i) + while i < len(nums) and nums[i] == 1 + cur: + i += 1 + + res = max(res, pick + dfs(i)) + return res + + return dfs(0) +``` + +```java +public class Solution { + public int deleteAndEarn(int[] nums) { + Arrays.sort(nums); + return dfs(nums, 0); + } + + private int dfs(int[] nums, int i) { + if (i >= nums.length) return 0; + + int cur = nums[i], pick = 0; + while (i < nums.length && nums[i] == cur) { + pick += nums[i]; + i++; + } + + int res = dfs(nums, i); + while (i < nums.length && nums[i] == cur + 1) { + i++; + } + + res = Math.max(res, pick + dfs(nums, i)); + return res; + } +} +``` + +```cpp +class Solution { +public: + int deleteAndEarn(vector& nums) { + sort(nums.begin(), nums.end()); + return dfs(nums, 0); + } + +private: + int dfs(const vector& nums, int i) { + if (i >= nums.size()) return 0; + + int cur = nums[i], pick = 0; + while (i < nums.size() && nums[i] == cur) { + pick += nums[i]; + i++; + } + + int res = dfs(nums, i); + while (i < nums.size() && nums[i] == cur + 1) { + i++; + } + + res = max(res, pick + dfs(nums, i)); + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + deleteAndEarn(nums) { + nums.sort((a, b) => a - b); + + const dfs = (i) => { + if (i >= nums.length) return 0; + + let cur = nums[i], pick = 0; + while (i < nums.length && nums[i] === cur) { + pick += nums[i]; + i++; + } + + let res = dfs(i); + while (i < nums.length && nums[i] === cur + 1) { + i++; + } + + res = Math.max(res, pick + dfs(i)); + return res; + }; + + return dfs(0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(2 ^ n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def deleteAndEarn(self, nums: List[int]) -> int: + val = defaultdict(int) + for num in nums: + val[num] += num + nums = sorted(list(set(nums))) + memo = [-1] * len(nums) + + def dfs(i): + if i >= len(nums): + return 0 + if memo[i] != -1: + return memo[i] + + res = val[nums[i]] + if i + 1 < len(nums) and nums[i] + 1 == nums[i + 1]: + res += dfs(i + 2) + else: + res += dfs(i + 1) + + res = max(res, dfs(i + 1)) + memo[i] = res + return res + + return dfs(0) +``` + +```java +public class Solution { + private Map val; + private int[] memo; + + public int deleteAndEarn(int[] nums) { + val = new HashMap<>(); + for (int num : nums) { + val.put(num, val.getOrDefault(num, 0) + num); + } + + List uniqueNums = new ArrayList<>(val.keySet()); + Collections.sort(uniqueNums); + memo = new int[uniqueNums.size()]; + Arrays.fill(memo, -1); + + return dfs(uniqueNums, 0); + } + + private int dfs(List nums, int i) { + if (i >= nums.size()) return 0; + if (memo[i] != -1) return memo[i]; + + int res = val.get(nums.get(i)); + if (i + 1 < nums.size() && nums.get(i) + 1 == nums.get(i + 1)) { + res += dfs(nums, i + 2); + } else { + res += dfs(nums, i + 1); + } + + res = Math.max(res, dfs(nums, i + 1)); + memo[i] = res; + return res; + } +} +``` + +```cpp +class Solution { + unordered_map val; + vector memo; + +public: + int deleteAndEarn(vector& nums) { + for (int num : nums) { + val[num] += num; + } + + vector uniqueNums; + for (auto& pair : val) { + uniqueNums.push_back(pair.first); + } + sort(uniqueNums.begin(), uniqueNums.end()); + memo.resize(uniqueNums.size(), -1); + + return dfs(uniqueNums, 0); + } + +private: + int dfs(vector& nums, int i) { + if (i >= nums.size()) return 0; + if (memo[i] != -1) return memo[i]; + + int res = val[nums[i]]; + if (i + 1 < nums.size() && nums[i] + 1 == nums[i + 1]) { + res += dfs(nums, i + 2); + } else { + res += dfs(nums, i + 1); + } + + res = max(res, dfs(nums, i + 1)); + memo[i] = res; + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + deleteAndEarn(nums) { + const val = new Map(); + nums.forEach(num => { + val.set(num, (val.get(num) || 0) + num); + }); + const uniqueNums = Array.from(new Set(nums)).sort((a, b) => a - b); + const memo = Array(uniqueNums.length).fill(-1); + + const dfs = (nums, i) => { + if (i >= nums.length) return 0; + if (memo[i] !== -1) return memo[i]; + + let res = val.get(nums[i]); + if (i + 1 < nums.length && nums[i] + 1 === nums[i + 1]) { + res += dfs(nums, i + 2); + } else { + res += dfs(nums, i + 1); + } + + res = Math.max(res, dfs(nums, i + 1)); + memo[i] = res; + return res; + }; + + return dfs(uniqueNums, 0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Dynamic Programming (Bottom-Up) - I + +::tabs-start + +```python +class Solution: + def deleteAndEarn(self, nums: List[int]) -> int: + val = defaultdict(int) + for num in nums: + val[num] += num + nums = sorted(list(set(nums))) + + dp = [0] * (len(nums) + 1) + for i in range(len(nums) - 1, -1, -1): + take = val[nums[i]] + if i + 1 < len(nums) and nums[i + 1] == nums[i] + 1: + take += dp[i + 2] + else: + take += dp[i + 1] + dp[i] = max(dp[i + 1], take) + + return dp[0] +``` + +```java +public class Solution { + public int deleteAndEarn(int[] nums) { + Map val = new HashMap<>(); + for (int num : nums) val.put(num, val.getOrDefault(num, 0) + num); + List sortedNums = new ArrayList<>(val.keySet()); + Collections.sort(sortedNums); + + int[] dp = new int[sortedNums.size() + 1]; + for (int i = sortedNums.size() - 1; i >= 0; i--) { + int take = val.get(sortedNums.get(i)); + if (i + 1 < sortedNums.size() && sortedNums.get(i + 1) == sortedNums.get(i) + 1) { + take += dp[i + 2]; + } else { + take += dp[i + 1]; + } + dp[i] = Math.max(dp[i + 1], take); + } + + return dp[0]; + } +} +``` + +```cpp +class Solution { +public: + int deleteAndEarn(vector& nums) { + unordered_map val; + for (int num : nums) val[num] += num; + vector sortedNums; + for (auto& [key, _] : val) sortedNums.push_back(key); + sort(sortedNums.begin(), sortedNums.end()); + + vector dp(sortedNums.size() + 1); + for (int i = sortedNums.size() - 1; i >= 0; i--) { + int take = val[sortedNums[i]]; + if (i + 1 < sortedNums.size() && sortedNums[i + 1] == sortedNums[i] + 1) { + take += dp[i + 2]; + } else { + take += dp[i + 1]; + } + dp[i] = max(dp[i + 1], take); + } + + return dp[0]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + deleteAndEarn(nums) { + const val = new Map(); + nums.forEach(num => val.set(num, (val.get(num) || 0) + num)); + const sortedNums = Array.from(val.keys()).sort((a, b) => a - b); + + const dp = Array(sortedNums.length + 1).fill(0); + for (let i = sortedNums.length - 1; i >= 0; i--) { + let take = val.get(sortedNums[i]); + if (i + 1 < sortedNums.length && sortedNums[i + 1] === sortedNums[i] + 1) { + take += dp[i + 2]; + } else { + take += dp[i + 1]; + } + dp[i] = Math.max(dp[i + 1], take); + } + + return dp[0]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ + +--- + +## 4. Dynamic Programming (Bottom-Up) - II + +::tabs-start + +```python +class Solution: + def deleteAndEarn(self, nums: List[int]) -> int: + m = max(nums) + dp = [0] * (m + 2) + + for num in nums: + dp[num] += num + for i in range(m - 1, 0, -1): + dp[i] = max(dp[i + 1], dp[i + 2] + dp[i]) + + return dp[1] +``` + +```java +public class Solution { + public int deleteAndEarn(int[] nums) { + int m = 0; + for (int num : nums) m = Math.max(m, num); + + int[] dp = new int[m + 2]; + for (int num : nums) dp[num] += num; + for (int i = m - 1; i > 0; i--) { + dp[i] = Math.max(dp[i + 1], dp[i + 2] + dp[i]); + } + return dp[1]; + } +} +``` + +```cpp +class Solution { +public: + int deleteAndEarn(vector& nums) { + int m = *max_element(nums.begin(), nums.end()); + vector dp(m + 2); + for (auto& num : nums) { + dp[num] += num; + } + + for (int i = m - 1; i > 0; i--) { + dp[i] = max(dp[i + 1], dp[i + 2] + dp[i]); + } + return dp[1]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + deleteAndEarn(nums) { + const m = Math.max(...nums); + const dp = new Int32Array(m + 2); + for (let num of nums) { + dp[num] += num; + } + + for (let i = m - 1; i > 0; i--) { + dp[i] = Math.max(dp[i + 1], dp[i + 2] + dp[i]); + } + return dp[1]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m + n)$ +* Space complexity: $O(m)$ + +> Where $m$ is the maximum element in the array and $n$ is the size of the array. + +--- + +## 5. Dynamic Programming (Space Optimized) + +::tabs-start + +```python +class Solution: + def deleteAndEarn(self, nums: List[int]) -> int: + count = Counter(nums) + nums = sorted(list(set(nums))) + + earn1, earn2 = 0, 0 + for i in range(len(nums)): + curEarn = nums[i] * count[nums[i]] + if i > 0 and nums[i] == nums[i - 1] + 1: + temp = earn2 + earn2 = max(curEarn + earn1, earn2) + earn1 = temp + else: + temp = earn2 + earn2 = curEarn + earn2 + earn1 = temp + return earn2 +``` + +```java +public class Solution { + public int deleteAndEarn(int[] nums) { + Map count = new HashMap<>(); + for (int num : nums) count.put(num, count.getOrDefault(num, 0) + num); + List uniqueNums = new ArrayList<>(count.keySet()); + Collections.sort(uniqueNums); + + int earn1 = 0, earn2 = 0; + for (int i = 0; i < uniqueNums.size(); i++) { + int curEarn = count.get(uniqueNums.get(i)); + if (i > 0 && uniqueNums.get(i) == uniqueNums.get(i - 1) + 1) { + int temp = earn2; + earn2 = Math.max(curEarn + earn1, earn2); + earn1 = temp; + } else { + int temp = earn2; + earn2 = curEarn + earn2; + earn1 = temp; + } + } + return earn2; + } +} +``` + +```cpp +class Solution { +public: + int deleteAndEarn(vector& nums) { + unordered_map count; + for (int num : nums) count[num] += num; + vector uniqueNums; + for (auto& pair : count) uniqueNums.push_back(pair.first); + sort(uniqueNums.begin(), uniqueNums.end()); + + int earn1 = 0, earn2 = 0; + for (int i = 0; i < uniqueNums.size(); i++) { + int curEarn = count[uniqueNums[i]]; + if (i > 0 && uniqueNums[i] == uniqueNums[i - 1] + 1) { + int temp = earn2; + earn2 = max(curEarn + earn1, earn2); + earn1 = temp; + } else { + int temp = earn2; + earn2 = curEarn + earn2; + earn1 = temp; + } + } + return earn2; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + deleteAndEarn(nums) { + const count = new Map(); + nums.forEach(num => count.set(num, (count.get(num) || 0) + num)); + const uniqueNums = [...count.keys()].sort((a, b) => a - b); + + let earn1 = 0, earn2 = 0; + for (let i = 0; i < uniqueNums.length; i++) { + const curEarn = count.get(uniqueNums[i]); + if (i > 0 && uniqueNums[i] === uniqueNums[i - 1] + 1) { + const temp = earn2; + earn2 = Math.max(curEarn + earn1, earn2); + earn1 = temp; + } else { + const temp = earn2; + earn2 = curEarn + earn2; + earn1 = temp; + } + } + return earn2; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/maximum-subarray-min-product.md b/articles/maximum-subarray-min-product.md new file mode 100644 index 000000000..1ac42156b --- /dev/null +++ b/articles/maximum-subarray-min-product.md @@ -0,0 +1,967 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def maxSumMinProduct(self, nums: List[int]) -> int: + res, MOD = 0, 1000000007 + for i in range(len(nums)): + total_sum = 0 + mini = float("inf") + for j in range(i, len(nums)): + mini = min(mini, nums[j]) + total_sum += nums[j] + cur = (mini * total_sum) % MOD + res = max(res, cur) + return res +``` + +```java +public class Solution { + public int maxSumMinProduct(int[] nums) { + long res = 0, MOD = 1000000007; + for (int i = 0; i < nums.length; i++) { + long totalSum = 0, mini = Long.MAX_VALUE; + for (int j = i; j < nums.length; j++) { + mini = Math.min(mini, nums[j]); + totalSum += nums[j]; + long cur = (mini * totalSum) % MOD; + res = Math.max(res, cur); + } + } + return (int) res; + } +} +``` + +```cpp +class Solution { +public: + int maxSumMinProduct(vector& nums) { + long long res = 0, MOD = 1000000007; + for (int i = 0; i < nums.size(); i++) { + long long total_sum = 0, mini = INT_MAX; + for (int j = i; j < nums.size(); j++) { + mini = min(mini, (long long)nums[j]); + total_sum += nums[j]; + long long cur = (mini * total_sum) % MOD; + res = max(res, cur); + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + maxSumMinProduct(nums) { + let res = 0, MOD = 1000000007; + for (let i = 0; i < nums.length; i++) { + let totalSum = 0, mini = Infinity; + for (let j = i; j < nums.length; j++) { + mini = Math.min(mini, nums[j]); + totalSum += nums[j]; + let cur = (mini * totalSum) % MOD; + res = Math.max(res, cur); + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(1)$ extra space. + +--- + +## 2. Divide And Conquer (Brute Force) + +::tabs-start + +```python +class Solution: + def maxSumMinProduct(self, nums: List[int]) -> int: + MOD = 10**9 + 7 + + def rec(l, r): + if l > r: + return 0 + + min_idx = l + total_sum = 0 + for i in range(l, r + 1): + total_sum += nums[i] + if nums[i] < nums[min_idx]: + min_idx = i + + cur = total_sum * nums[min_idx] + left = rec(l, min_idx - 1) + right = rec(min_idx + 1, r) + + return max(cur, left, right) + + return rec(0, len(nums) - 1) % MOD +``` + +```java +public class Solution { + public int maxSumMinProduct(int[] nums) { + int MOD = 1_000_000_007; + return (int) (rec(nums, 0, nums.length - 1) % MOD); + } + + private long rec(int[] nums, int l, int r) { + if (l > r) return 0; + + int minIdx = l; + long totalSum = 0; + for (int i = l; i <= r; i++) { + totalSum += nums[i]; + if (nums[i] < nums[minIdx]) { + minIdx = i; + } + } + + long cur = totalSum * nums[minIdx]; + long left = rec(nums, l, minIdx - 1); + long right = rec(nums, minIdx + 1, r); + + return Math.max(cur, Math.max(left, right)); + } +} +``` + +```cpp +class Solution { +public: + int maxSumMinProduct(vector& nums) { + const int MOD = 1e9 + 7; + return rec(nums, 0, nums.size() - 1) % MOD; + } + +private: + long long rec(vector& nums, int l, int r) { + if (l > r) return 0; + + int minIdx = l; + long long totalSum = 0; + for (int i = l; i <= r; i++) { + totalSum += nums[i]; + if (nums[i] < nums[minIdx]) { + minIdx = i; + } + } + + long long cur = totalSum * nums[minIdx]; + long long left = rec(nums, l, minIdx - 1); + long long right = rec(nums, minIdx + 1, r); + + return max(cur, max(left, right)); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + maxSumMinProduct(nums) { + const MOD = 1_000_000_007; + + const rec = (l, r) => { + if (l > r) return 0n; + + let minIdx = l; + let totalSum = 0n; + for (let i = l; i <= r; i++) { + totalSum += BigInt(nums[i]); + if (nums[i] < nums[minIdx]) { + minIdx = i; + } + } + + let cur = totalSum * BigInt(nums[minIdx]); + let left = rec(l, minIdx - 1); + let right = rec(minIdx + 1, r); + + if (cur < left) cur = left; + if (cur < right) cur = right; + return cur; + }; + + return Number(rec(0, nums.length - 1) % BigInt(MOD)); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n)$ for recursion stack. + +--- + +## 3. Divide And Conquer (Segment Tree) + +::tabs-start + +```python +class SegmentTree: + def __init__(self, N, A): + self.n = N + while (self.n & (self.n - 1)) != 0: + self.n += 1 + self.build(N, A) + + def build(self, N, A): + self.tree = [(-1, float('inf'))] * (2 * self.n) + for i in range(N): + self.tree[self.n + i] = (i, A[i]) + for i in range(self.n - 1, 0, -1): + self.tree[i] = min(self.tree[i << 1], self.tree[i << 1 | 1], key=lambda x: x[1]) + + def query(self, l, r): + res = (-1, float('inf')) + l += self.n + r += self.n + 1 + while l < r: + if l & 1: + res = min(res, self.tree[l], key=lambda x: x[1]) + l += 1 + if r & 1: + r -= 1 + res = min(res, self.tree[r], key=lambda x: x[1]) + l >>= 1 + r >>= 1 + return res[0] + +class Solution: + def maxSumMinProduct(self, nums: List[int]) -> int: + MOD = 10**9 + 7 + segTree = SegmentTree(len(nums), nums) + prefix_sum = [0] * (len(nums) + 1) + for i in range(0, len(nums)): + prefix_sum[i + 1] += prefix_sum[i] + nums[i] + + def rec(l, r): + if l > r: + return 0 + + min_idx = segTree.query(l, r) + total_sum = prefix_sum[r + 1] - prefix_sum[l] + cur = total_sum * nums[min_idx] + left = rec(l, min_idx - 1) + right = rec(min_idx + 1, r) + + return max(cur, left, right) + + return rec(0, len(nums) - 1) % MOD +``` + +```java +class SegmentTree { + private int n; + private long[][] tree; + + public SegmentTree(int n, int[] nums) { + this.n = n; + while ((this.n & (this.n - 1)) != 0) { + this.n++; + } + build(n, nums); + } + + private void build(int n, int[] nums) { + tree = new long[2 * this.n][2]; + Arrays.fill(tree, new long[]{-1, Long.MAX_VALUE}); + for (int i = 0; i < n; i++) { + tree[this.n + i] = new long[]{i, nums[i]}; + } + for (int i = this.n - 1; i > 0; i--) { + tree[i] = merge(tree[i << 1], tree[i << 1 | 1]); + } + } + + private long[] merge(long[] a, long[] b) { + return a[1] < b[1] ? a : b; + } + + public int query(int l, int r) { + long[] res = new long[]{-1, Long.MAX_VALUE}; + l += this.n; + r += this.n + 1; + while (l < r) { + if ((l & 1) == 1) res = merge(res, tree[l++]); + if ((r & 1) == 1) res = merge(res, tree[--r]); + l >>= 1; + r >>= 1; + } + return (int) res[0]; + } +} + +public class Solution { + public int maxSumMinProduct(int[] nums) { + int MOD = 1_000_000_007; + SegmentTree segTree = new SegmentTree(nums.length, nums); + long[] prefixSum = new long[nums.length + 1]; + for (int i = 0; i < nums.length; i++) { + prefixSum[i + 1] = prefixSum[i] + nums[i]; + } + + return (int) (rec(0, nums.length - 1, nums, prefixSum, segTree) % MOD); + } + + private long rec(int l, int r, int[] nums, long[] prefixSum, SegmentTree segTree) { + if (l > r) return 0; + int minIdx = segTree.query(l, r); + long totalSum = prefixSum[r + 1] - prefixSum[l]; + long cur = totalSum * nums[minIdx]; + long left = rec(l, minIdx - 1, nums, prefixSum, segTree); + long right = rec(minIdx + 1, r, nums, prefixSum, segTree); + return Math.max(cur, Math.max(left, right)); + } +} +``` + +```cpp +class SegmentTree { + int n; + vector> tree; + +public: + SegmentTree(int n, vector& nums) : n(n) { + while ((this->n & (this->n - 1)) != 0) this->n++; + build(n, nums); + } + + void build(int n, vector& nums) { + tree.resize(2 * this->n, {-1, LLONG_MAX}); + for (int i = 0; i < n; i++) { + tree[this->n + i] = {i, nums[i]}; + } + for (int i = this->n - 1; i > 0; i--) { + tree[i] = min(tree[i << 1], tree[i << 1 | 1], [](auto& a, auto& b) { return a.second < b.second; }); + } + } + + int query(int l, int r) { + pair res = {-1, LLONG_MAX}; + l += this->n; + r += this->n + 1; + while (l < r) { + if (l & 1) res = min(res, tree[l++], [](auto& a, auto& b) { return a.second < b.second; }); + if (r & 1) res = min(res, tree[--r], [](auto& a, auto& b) { return a.second < b.second; }); + l >>= 1; + r >>= 1; + } + return res.first; + } +}; + +class Solution { +public: + int maxSumMinProduct(vector& nums) { + const int MOD = 1e9 + 7; + SegmentTree segTree(nums.size(), nums); + vector prefixSum(nums.size() + 1, 0); + for (int i = 0; i < nums.size(); i++) { + prefixSum[i + 1] = prefixSum[i] + nums[i]; + } + + return rec(0, nums.size() - 1, nums, prefixSum, segTree) % MOD; + } + +private: + long long rec(int l, int r, vector& nums, vector& prefixSum, SegmentTree& segTree) { + if (l > r) return 0; + int minIdx = segTree.query(l, r); + long long totalSum = prefixSum[r + 1] - prefixSum[l]; + long long cur = totalSum * nums[minIdx]; + long long left = rec(l, minIdx - 1, nums, prefixSum, segTree); + long long right = rec(minIdx + 1, r, nums, prefixSum, segTree); + return max(cur, max(left, right)); + } +}; +``` + +```javascript +class SegmentTree { + /** + * @constructor + * @param {number} n + * @param {number[]} nums + */ + constructor(n, nums) { + this.n = n; + while ((this.n & (this.n - 1)) !== 0) { + this.n++; + } + this.build(n, nums); + } + + /** + * @param {number} n + * @param {number[]} nums + * @return {void} + */ + build(n, nums) { + this.tree = Array(2 * this.n).fill([BigInt(-1), BigInt(Number.MAX_SAFE_INTEGER)]); + for (let i = 0; i < n; i++) { + this.tree[this.n + i] = [BigInt(i), BigInt(nums[i])]; + } + for (let i = this.n - 1; i > 0; i--) { + this.tree[i] = this._merge(this.tree[i << 1], this.tree[i << 1 | 1]); + } + } + + /** + * @param {number} a + * @param {number} b + * @return {number} + */ + _merge(a, b) { + return a[1] < b[1] ? a : b; + } + + /** + * @param {number} l + * @param {number} r + * @return {number} + */ + query(l, r) { + let res = [BigInt(-1), BigInt(Number.MAX_SAFE_INTEGER)]; + l += this.n; + r += this.n + 1; + while (l < r) { + if (l & 1) res = this._merge(res, this.tree[l++]); + if (r & 1) res = this._merge(res, this.tree[--r]); + l >>= 1; + r >>= 1; + } + return Number(res[0]); + } +} + +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + maxSumMinProduct(nums) { + const MOD = BigInt(1_000_000_007); + const segTree = new SegmentTree(nums.length, nums); + const prefixSum = Array(nums.length + 1).fill(BigInt(0)); + for (let i = 0; i < nums.length; i++) { + prefixSum[i + 1] = prefixSum[i] + BigInt(nums[i]); + } + + const rec = (l, r) => { + if (l > r) return BigInt(0); + const minIdx = segTree.query(l, r); + const totalSum = prefixSum[r + 1] - prefixSum[l]; + let cur = totalSum * BigInt(nums[minIdx]); + let left = rec(l, minIdx - 1); + let right = rec(minIdx + 1, r); + + if (cur < left) cur = left; + if (cur < right) cur = right; + return cur; + }; + + return Number(rec(0, nums.length - 1) % MOD); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ + +--- + +## 4. Monotonic Stack + +::tabs-start + +```python +class Solution: + def maxSumMinProduct(self, nums: List[int]) -> int: + n = len(nums) + prefix_sum = [0] * (n + 1) + for i in range(n): + prefix_sum[i + 1] = prefix_sum[i] + nums[i] + + prev_min, nxt_min = [-1] * n, [n] * n + stack = [] + + for i in range(n): + while stack and nums[stack[-1]] >= nums[i]: + stack.pop() + if stack: + prev_min[i] = stack[-1] + stack.append(i) + + stack.clear() + + for i in range(n - 1, -1, -1): + while stack and nums[stack[-1]] >= nums[i]: + stack.pop() + if stack: + nxt_min[i] = stack[-1] + stack.append(i) + + res = 0 + + for i in range(n): + l, r = prev_min[i] + 1, nxt_min[i] - 1 + total_sum = prefix_sum[r + 1] - prefix_sum[l] + res = max(res, nums[i] * total_sum) + + return res % (10 ** 9 + 7) +``` + +```java +public class Solution { + public int maxSumMinProduct(int[] nums) { + int n = nums.length; + long[] prefixSum = new long[n + 1]; + for (int i = 0; i < n; i++) { + prefixSum[i + 1] = prefixSum[i] + nums[i]; + } + + int[] prevMin = new int[n]; + int[] nxtMin = new int[n]; + Arrays.fill(prevMin, -1); + Arrays.fill(nxtMin, n); + + Stack stack = new Stack<>(); + for (int i = 0; i < n; i++) { + while (!stack.isEmpty() && nums[stack.peek()] >= nums[i]) { + stack.pop(); + } + if (!stack.isEmpty()) { + prevMin[i] = stack.peek(); + } + stack.push(i); + } + + stack.clear(); + for (int i = n - 1; i >= 0; i--) { + while (!stack.isEmpty() && nums[stack.peek()] >= nums[i]) { + stack.pop(); + } + if (!stack.isEmpty()) { + nxtMin[i] = stack.peek(); + } + stack.push(i); + } + + long res = 0; + int MOD = 1_000_000_007; + for (int i = 0; i < n; i++) { + int l = prevMin[i] + 1, r = nxtMin[i] - 1; + long totalSum = prefixSum[r + 1] - prefixSum[l]; + res = Math.max(res, nums[i] * totalSum); + } + + return (int)(res % MOD); + } +} +``` + +```cpp +class Solution { +public: + int maxSumMinProduct(vector& nums) { + int n = nums.size(); + vector prefixSum(n + 1, 0); + for (int i = 0; i < n; i++) { + prefixSum[i + 1] = prefixSum[i] + nums[i]; + } + + vector prevMin(n, -1), nxtMin(n, n); + stack stack; + + for (int i = 0; i < n; i++) { + while (!stack.empty() && nums[stack.top()] >= nums[i]) { + stack.pop(); + } + if (!stack.empty()) { + prevMin[i] = stack.top(); + } + stack.push(i); + } + + while (!stack.empty()) stack.pop(); + for (int i = n - 1; i >= 0; i--) { + while (!stack.empty() && nums[stack.top()] >= nums[i]) { + stack.pop(); + } + if (!stack.empty()) { + nxtMin[i] = stack.top(); + } + stack.push(i); + } + + long long res = 0; + int MOD = 1e9 + 7; + for (int i = 0; i < n; i++) { + int l = prevMin[i] + 1, r = nxtMin[i] - 1; + long long totalSum = prefixSum[r + 1] - prefixSum[l]; + res = max(res, nums[i] * totalSum); + } + + return res % MOD; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + maxSumMinProduct(nums) { + const n = nums.length; + const prefixSum = new Array(n + 1).fill(0); + for (let i = 0; i < n; i++) { + prefixSum[i + 1] = prefixSum[i] + nums[i]; + } + + const prevMin = new Array(n).fill(-1); + const nxtMin = new Array(n).fill(n); + const stack = []; + + for (let i = 0; i < n; i++) { + while (stack.length && nums[stack[stack.length - 1]] >= nums[i]) { + stack.pop(); + } + if (stack.length) { + prevMin[i] = stack[stack.length - 1]; + } + stack.push(i); + } + + stack.length = 0; + for (let i = n - 1; i >= 0; i--) { + while (stack.length && nums[stack[stack.length - 1]] >= nums[i]) { + stack.pop(); + } + if (stack.length) { + nxtMin[i] = stack[stack.length - 1]; + } + stack.push(i); + } + + let res = 0n; + const MOD = 10n ** 9n + 7n; + for (let i = 0; i < n; i++) { + const l = prevMin[i] + 1; + const r = nxtMin[i] - 1; + const totalSum = BigInt(prefixSum[r + 1] - prefixSum[l]); + const tmp = BigInt(nums[i]) * totalSum; + if (tmp > res) res = tmp; + } + + return Number(res % MOD); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 5. Monotonic Stack (Space Optimized) - I + +::tabs-start + +```python +class Solution: + def maxSumMinProduct(self, nums: List[int]) -> int: + n = len(nums) + prefix = [0] * (n + 1) + for i in range(n): + prefix[i + 1] = prefix[i] + nums[i] + + res = 0 + stack = [] + + for i, num in enumerate(nums): + new_start = i + while stack and stack[-1][1] > num: + start, val = stack.pop() + total = prefix[i] - prefix[start] + res = max(res, val * total) + new_start = start + stack.append((new_start, num)) + + while stack: + start, val = stack.pop() + total = prefix[n] - prefix[start] + res = max(res, val * total) + + return res % (10**9 + 7) +``` + +```java +public class Solution { + public int maxSumMinProduct(int[] nums) { + int n = nums.length; + long[] prefix = new long[n + 1]; + for (int i = 0; i < n; i++) { + prefix[i + 1] = prefix[i] + nums[i]; + } + + long res = 0; + Stack stack = new Stack<>(); + for (int i = 0; i < n; i++) { + int newStart = i; + while (!stack.isEmpty() && stack.peek()[1] > nums[i]) { + int[] top = stack.pop(); + int start = top[0], val = top[1]; + long total = prefix[i] - prefix[start]; + res = Math.max(res, val * total); + newStart = start; + } + stack.push(new int[]{newStart, nums[i]}); + } + + while (!stack.isEmpty()) { + int[] top = stack.pop(); + int start = top[0], val = top[1]; + long total = prefix[n] - prefix[start]; + res = Math.max(res, val * total); + } + + return (int) (res % (1_000_000_007)); + } +} +``` + +```cpp +class Solution { +public: + int maxSumMinProduct(vector& nums) { + int n = nums.size(); + vector prefix(n + 1, 0); + for (int i = 0; i < n; i++) { + prefix[i + 1] = prefix[i] + nums[i]; + } + + long long res = 0; + stack> stack; + for (int i = 0; i < n; i++) { + int newStart = i; + while (!stack.empty() && stack.top().second > nums[i]) { + auto [start, val] = stack.top(); + stack.pop(); + long long total = prefix[i] - prefix[start]; + res = max(res, val * total); + newStart = start; + } + stack.push({newStart, nums[i]}); + } + + while (!stack.empty()) { + auto [start, val] = stack.top(); + stack.pop(); + long long total = prefix[n] - prefix[start]; + res = max(res, val * total); + } + + return res % 1000000007; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + maxSumMinProduct(nums) { + const n = nums.length; + const prefix = new Array(n + 1).fill(0); + for (let i = 0; i < n; i++) { + prefix[i + 1] = prefix[i] + nums[i]; + } + + let res = 0n; + const MOD = 10n ** 9n + 7n; + const stack = []; + + for (let i = 0; i < n; i++) { + let newStart = i; + while (stack.length && stack[stack.length - 1][1] > nums[i]) { + const [start, val] = stack.pop(); + const total = BigInt(prefix[i] - prefix[start]); + const tmp = BigInt(val) * total; + if (tmp > res) res = tmp; + newStart = start; + } + stack.push([newStart, nums[i]]); + } + + while (stack.length) { + const [start, val] = stack.pop(); + const total = BigInt(prefix[n] - prefix[start]); + const tmp = BigInt(val) * total; + if (tmp > res) res = tmp; + } + + return Number(res % MOD); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 6. Monotonic Stack (Space Optimized) - II + +::tabs-start + +```python +class Solution: + def maxSumMinProduct(self, nums: List[int]) -> int: + n = len(nums) + prefix_sum = [0] * (n + 1) + for i in range(n): + prefix_sum[i + 1] = prefix_sum[i] + nums[i] + + res, stack = 0, [] + for i in range(n + 1): + while stack and (i == n or nums[i] < nums[stack[-1]]): + j = stack.pop() + start = 0 if not stack else stack[-1] + 1 + res = max(res, nums[j] * (prefix_sum[i] - prefix_sum[start])) + stack.append(i) + + return res % (10 ** 9 + 7) +``` + +```java +public class Solution { + public int maxSumMinProduct(int[] nums) { + int n = nums.length; + long[] prefixSum = new long[n + 1]; + for (int i = 0; i < n; i++) { + prefixSum[i + 1] = prefixSum[i] + nums[i]; + } + + long res = 0; + int mod = 1_000_000_007; + Stack stack = new Stack<>(); + for (int i = 0; i <= n; i++) { + while (!stack.isEmpty() && (i == n || nums[i] < nums[stack.peek()])) { + int j = stack.pop(); + int start = stack.isEmpty() ? 0 : stack.peek() + 1; + res = Math.max(res, (long) nums[j] * (prefixSum[i] - prefixSum[start])); + } + stack.push(i); + } + + return (int) (res % mod); + } +} +``` + +```cpp +class Solution { +public: + int maxSumMinProduct(vector& nums) { + int n = nums.size(); + vector prefixSum(n + 1, 0); + for (int i = 0; i < n; i++) { + prefixSum[i + 1] = prefixSum[i] + nums[i]; + } + + long long res = 0; + const int mod = 1e9 + 7; + stack st; + for (int i = 0; i <= n; i++) { + while (!st.empty() && (i == n || nums[i] < nums[st.top()])) { + int j = st.top(); + st.pop(); + int start = st.empty() ? 0 : st.top() + 1; + res = max(res, (long long) nums[j] * (prefixSum[i] - prefixSum[start])); + } + st.push(i); + } + + return res % mod; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + maxSumMinProduct(nums) { + const n = nums.length; + const prefixSum = new Array(n + 1).fill(0); + for (let i = 0; i < n; i++) { + prefixSum[i + 1] = prefixSum[i] + nums[i]; + } + + let res = 0n; + const MOD = 10n ** 9n + 7n; + const stack = []; + for (let i = 0; i <= n; i++) { + while (stack.length && (i === n || nums[i] < nums[stack[stack.length - 1]])) { + const j = stack.pop(); + const start = stack.length === 0 ? 0 : stack[stack.length - 1] + 1; + const total = BigInt(prefixSum[i] - prefixSum[start]); + const tmp = BigInt(nums[j]) * total; + if (tmp > res) res = tmp; + } + stack.push(i); + } + + return Number(res % MOD); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/minimum-cost-for-tickets.md b/articles/minimum-cost-for-tickets.md new file mode 100644 index 000000000..158c186db --- /dev/null +++ b/articles/minimum-cost-for-tickets.md @@ -0,0 +1,956 @@ +## 1. Recursion + +::tabs-start + +```python +class Solution: + def mincostTickets(self, days: List[int], costs: List[int]) -> int: + n = len(days) + + def dfs(i): + if i == n: + return 0 + + res = costs[0] + dfs(i + 1) + j = i + while j < n and days[j] < days[i] + 7: + j += 1 + res = min(res, costs[1] + dfs(j)) + + j = i + while j < n and days[j] < days[i] + 30: + j += 1 + res = min(res, costs[2] + dfs(j)) + + return res + + return dfs(0) +``` + +```java +public class Solution { + public int mincostTickets(int[] days, int[] costs) { + return dfs(0, days.length, days, costs); + } + + private int dfs(int i, int n, int[] days, int[] costs) { + if (i == n) return 0; + + int res = costs[0] + dfs(i + 1, n, days, costs); + int j = i; + while (j < n && days[j] < days[i] + 7) { + j++; + } + res = Math.min(res, costs[1] + dfs(j, n, days, costs)); + + j = i; + while (j < n && days[j] < days[i] + 30) { + j++; + } + res = Math.min(res, costs[2] + dfs(j, n, days, costs)); + + return res; + } +} +``` + +```cpp +class Solution { +public: + int mincostTickets(vector& days, vector& costs) { + int n = days.size(); + + function dfs = [&](int i) -> int { + if (i == n) return 0; + + int res = costs[0] + dfs(i + 1); + + int j = i; + while (j < n && days[j] < days[i] + 7) { + j++; + } + res = min(res, costs[1] + dfs(j)); + + j = i; + while (j < n && days[j] < days[i] + 30) { + j++; + } + res = min(res, costs[2] + dfs(j)); + + return res; + }; + + return dfs(0); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} days + * @param {number[]} costs + * @return {number} + */ + mincostTickets(days, costs) { + const n = days.length; + + const dfs = (i) => { + if (i === n) return 0; + + let res = costs[0] + dfs(i + 1); + + let j = i; + while (j < n && days[j] < days[i] + 7) { + j++; + } + res = Math.min(res, costs[1] + dfs(j)); + + j = i; + while (j < n && days[j] < days[i] + 30) { + j++; + } + res = Math.min(res, costs[2] + dfs(j)); + + return res; + }; + + return dfs(0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(3 ^ n)$ +* Space complexity: $O(n)$ for recursion stack. + +--- + +## 2. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def mincostTickets(self, days: List[int], costs: List[int]) -> int: + dp = {} + + def dfs(i): + if i == len(days): + return 0 + if i in dp: + return dp[i] + + dp[i] = float("inf") + for d, c in zip([1, 7, 30], costs): + j = i + while j < len(days) and days[j] < days[i] + d: + j += 1 + dp[i] = min(dp[i], c + dfs(j)) + + return dp[i] + + return dfs(0) +``` + +```java +public class Solution { + private int[] dp; + + public int mincostTickets(int[] days, int[] costs) { + dp = new int[days.length]; + Arrays.fill(dp, -1); + return dfs(0, days, costs); + } + + private int dfs(int i, int[] days, int[] costs) { + if (i == days.length) { + return 0; + } + if (dp[i] != -1) { + return dp[i]; + } + + dp[i] = Integer.MAX_VALUE; + int idx = 0; + for (int d : new int[]{1, 7, 30}) { + int j = i + 1; + while (j < days.length && days[j] < days[i] + d) { + j++; + } + dp[i] = Math.min(dp[i], costs[idx] + dfs(j, days, costs)); + idx++; + } + return dp[i]; + } +} +``` + +```cpp +class Solution { +private: + vector dp; + + int dfs(int i, const vector& days, const vector& costs) { + if (i == days.size()) return 0; + if (dp[i] != -1) return dp[i]; + + dp[i] = INT_MAX; + int idx = 0; + for (int d : {1, 7, 30}) { + int j = i + 1; + while (j < days.size() && days[j] < days[i] + d) { + j++; + } + dp[i] = min(dp[i], costs[idx] + dfs(j, days, costs)); + idx++; + } + return dp[i]; + } + +public: + int mincostTickets(vector& days, vector& costs) { + dp = vector(days.size(), -1); + return dfs(0, days, costs); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} days + * @param {number[]} costs + * @return {number} + */ + mincostTickets(days, costs) { + const dp = new Array(days.length).fill(-1); + + const dfs = (i) => { + if (i === days.length) return 0; + if (dp[i] !== -1) return dp[i]; + + dp[i] = Infinity; + [1, 7, 30].forEach((d, idx) => { + let j = i + 1; + while (j < days.length && days[j] < days[i] + d) { + j++; + } + dp[i] = Math.min(dp[i], costs[idx] + dfs(j)); + }); + + return dp[i]; + }; + + return dfs(0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Dynamic Programming (Bottom-Up) - I + +::tabs-start + +```python +class Solution: + def mincostTickets(self, days: List[int], costs: List[int]) -> int: + n = len(days) + dp = [0] * (n + 1) + + for i in range(n - 1, -1, -1): + dp[i] = float('inf') + for d, c in zip([1, 7, 30], costs): + j = i + 1 + while j < n and days[j] < days[i] + d: + j += 1 + dp[i] = min(dp[i], c + dp[j]) + + return dp[0] +``` + +```java +public class Solution { + public int mincostTickets(int[] days, int[] costs) { + int n = days.length; + int[] dp = new int[n + 1]; + + for (int i = n - 1; i >= 0; i--) { + dp[i] = Integer.MAX_VALUE; + int idx = 0; + for (int d : new int[]{1, 7, 30}) { + int j = i + 1; + while (j < n && days[j] < days[i] + d) { + j++; + } + dp[i] = Math.min(dp[i], costs[idx] + dp[j]); + idx++; + } + } + + return dp[0]; + } +} +``` + +```cpp +class Solution { +public: + int mincostTickets(vector& days, vector& costs) { + int n = days.size(); + vector dp(n + 1, 0); + + for (int i = n - 1; i >= 0; i--) { + dp[i] = INT_MAX; + for (int k = 0; k < 3; ++k) { + int j = i + 1; + while (j < n && days[j] < days[i] + (k == 0 ? 1 : k == 1 ? 7 : 30)) { + j++; + } + dp[i] = min(dp[i], costs[k] + dp[j]); + } + } + + return dp[0]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} days + * @param {number[]} costs + * @return {number} + */ + mincostTickets(days, costs) { + const n = days.length; + const dp = new Array(n + 1).fill(0); + + for (let i = n - 1; i >= 0; i--) { + dp[i] = Infinity; + [1, 7, 30].forEach((d, idx) => { + let j = i + 1; + while (j < n && days[j] < days[i] + d) { + j++; + } + dp[i] = Math.min(dp[i], costs[idx] + dp[j]); + }); + } + + return dp[0]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 4. Dynamic Programming (Bottom-Up) - II + +::tabs-start + +```python +class Solution: + def mincostTickets(self, days: List[int], costs: List[int]) -> int: + days.append(days[-1] + 30) + n = len(days) + dp = [0] * n + last7 = last30 = n + + for i in range(n - 2, -1, -1): + dp[i] = dp[i + 1] + costs[0] + + while last7 > i + 1 and days[last7 - 1] >= days[i] + 7: + last7 -= 1 + dp[i] = min(dp[i], costs[1] + dp[last7]) + + while last30 > i + 1 and days[last30 - 1] >= days[i] + 30: + last30 -= 1 + dp[i] = min(dp[i], costs[2] + dp[last30]) + + return dp[0] +``` + +```java +public class Solution { + public int mincostTickets(int[] days, int[] costs) { + int n = days.length; + int[] dp = new int[n + 1]; + int last7 = n, last30 = n; + int[] extendedDays = Arrays.copyOf(days, n + 1); + extendedDays[n] = days[n - 1] + 30; + + for (int i = n - 1; i >= 0; i--) { + dp[i] = dp[i + 1] + costs[0]; + + while (last7 > i + 1 && extendedDays[last7 - 1] >= days[i] + 7) { + last7--; + } + dp[i] = Math.min(dp[i], costs[1] + dp[last7]); + + while (last30 > i + 1 && extendedDays[last30 - 1] >= days[i] + 30) { + last30--; + } + dp[i] = Math.min(dp[i], costs[2] + dp[last30]); + } + + return dp[0]; + } +} +``` + +```cpp +class Solution { +public: + int mincostTickets(vector& days, vector& costs) { + days.push_back(days.back() + 30); + int n = days.size(); + vector dp(n, 0); + int last7 = n, last30 = n; + + for (int i = n - 2; i >= 0; --i) { + dp[i] = dp[i + 1] + costs[0]; + + while (last7 > i + 1 && days[last7 - 1] >= days[i] + 7) { + --last7; + } + dp[i] = min(dp[i], costs[1] + dp[last7]); + + while (last30 > i + 1 && days[last30 - 1] >= days[i] + 30) { + --last30; + } + dp[i] = min(dp[i], costs[2] + dp[last30]); + } + + return dp[0]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} days + * @param {number[]} costs + * @return {number} + */ + mincostTickets(days, costs) { + days.push(days[days.length - 1] + 30); + const n = days.length; + const dp = new Array(n).fill(0); + let last7 = n, last30 = n; + + for (let i = n - 2; i >= 0; i--) { + dp[i] = dp[i + 1] + costs[0]; + + while (last7 > i + 1 && days[last7 - 1] >= days[i] + 7) { + last7--; + } + dp[i] = Math.min(dp[i], costs[1] + dp[last7]); + + while (last30 > i + 1 && days[last30 - 1] >= days[i] + 30) { + last30--; + } + dp[i] = Math.min(dp[i], costs[2] + dp[last30]); + } + + return dp[0]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 5. Dynamic Programming (Space Optimized) - I + +::tabs-start + +```python +class Solution: + def mincostTickets(self, days: List[int], costs: List[int]) -> int: + dp7, dp30 = deque(), deque() + dp = 0 + + for d in days: + while dp7 and dp7[0][0] + 7 <= d: + dp7.popleft() + + while dp30 and dp30[0][0] + 30 <= d: + dp30.popleft() + + dp7.append([d, dp + costs[1]]) + dp30.append([d, dp + costs[2]]) + dp = min(dp + costs[0], dp7[0][1], dp30[0][1]) + + return dp +``` + +```java +public class Solution { + public int mincostTickets(int[] days, int[] costs) { + Queue dp7 = new LinkedList<>(); + Queue dp30 = new LinkedList<>(); + int dp = 0; + + for (int d : days) { + while (!dp7.isEmpty() && dp7.peek()[0] + 7 <= d) { + dp7.poll(); + } + + while (!dp30.isEmpty() && dp30.peek()[0] + 30 <= d) { + dp30.poll(); + } + + dp7.offer(new int[]{d, dp + costs[1]}); + dp30.offer(new int[]{d, dp + costs[2]}); + dp = Math.min(dp + costs[0], Math.min(dp7.peek()[1], dp30.peek()[1])); + } + + return dp; + } +} +``` + +```cpp +class Solution { +public: + int mincostTickets(vector& days, vector& costs) { + queue> dp7, dp30; + int dp = 0; + + for (int& d : days) { + while (!dp7.empty() && dp7.front().first + 7 <= d) { + dp7.pop(); + } + + while (!dp30.empty() && dp30.front().first + 30 <= d) { + dp30.pop(); + } + + dp7.emplace(d, dp + costs[1]); + dp30.emplace(d, dp + costs[2]); + dp = min({dp + costs[0], dp7.front().second, dp30.front().second}); + } + + return dp; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} days + * @param {number[]} costs + * @return {number} + */ + mincostTickets(days, costs) { + const dp7 = new Queue; + const dp30 = new Queue; + let dp = 0; + + for (const d of days) { + while (!dp7.isEmpty() && dp7.front()[0] + 7 <= d) { + dp7.pop(); + } + + while (!dp30.isEmpty() && dp30.front()[0] + 30 <= d) { + dp30.pop(); + } + + dp7.push([d, dp + costs[1]]); + dp30.push([d, dp + costs[2]]); + + dp = Math.min(dp + costs[0], dp7.front()[1], dp30.front()[1]); + } + + return dp; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ since we keep at most $30$ values in the queue. + +--- + +## 6. Dynamic Programming (Space Optimized) - II + +::tabs-start + +```python +class Solution: + def mincostTickets(self, days: List[int], costs: List[int]) -> int: + dp7, dp30 = deque(), deque() + dp = 0 + + last7 = last30 = 0 + for i in range(len(days) - 1, -1, -1): + dp += costs[0] + while dp7 and dp7[-1][0] >= days[i] + 7: + last7 = dp7.pop()[1] + dp = min(dp, costs[1] + last7) + + while dp30 and dp30[-1][0] >= days[i] + 30: + last30 = dp30.pop()[1] + dp = min(dp, costs[2] + last30) + + dp7.appendleft([days[i], dp]) + dp30.appendleft([days[i], dp]) + + return dp +``` + +```java +public class Solution { + public int mincostTickets(int[] days, int[] costs) { + Deque dp7 = new ArrayDeque<>(); + Deque dp30 = new ArrayDeque<>(); + int dp = 0, last7 = 0, last30 = 0; + + for (int i = days.length - 1; i >= 0; i--) { + dp += costs[0]; + + while (!dp7.isEmpty() && dp7.peekLast()[0] >= days[i] + 7) { + last7 = dp7.pollLast()[1]; + } + dp = Math.min(dp, costs[1] + last7); + + while (!dp30.isEmpty() && dp30.peekLast()[0] >= days[i] + 30) { + last30 = dp30.pollLast()[1]; + } + dp = Math.min(dp, costs[2] + last30); + + dp7.offerFirst(new int[]{days[i], dp}); + dp30.offerFirst(new int[]{days[i], dp}); + } + return dp; + } +} +``` + +```cpp +class Solution { +public: + int mincostTickets(vector& days, vector& costs) { + deque> dp7, dp30; + int dp = 0, last7 = 0, last30 = 0; + + for (int i = days.size() - 1; i >= 0; --i) { + dp += costs[0]; + + while (!dp7.empty() && dp7.back().first >= days[i] + 7) { + last7 = dp7.back().second; + dp7.pop_back(); + } + dp = min(dp, costs[1] + last7); + + while (!dp30.empty() && dp30.back().first >= days[i] + 30) { + last30 = dp30.back().second; + dp30.pop_back(); + } + dp = min(dp, costs[2] + last30); + + dp7.push_front({days[i], dp}); + dp30.push_front({days[i], dp}); + } + return dp; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} days + * @param {number[]} costs + * @return {number} + */ + mincostTickets(days, costs) { + const dp7 = new Deque(); + const dp30 = new Deque(); + let dp = 0, last7 = 0, last30 = 0; + + for (let i = days.length - 1; i >= 0; i--) { + dp += costs[0]; + + while (!dp7.isEmpty() && dp7.back()[0] >= days[i] + 7) { + last7 = dp7.popBack()[1]; + } + dp = Math.min(dp, costs[1] + last7); + + while (!dp30.isEmpty() && dp30.back()[0] >= days[i] + 30) { + last30 = dp30.popBack()[1]; + } + dp = Math.min(dp, costs[2] + last30); + + dp7.pushFront([days[i], dp]); + dp30.pushFront([days[i], dp]); + } + return dp; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ since we keep at most $30$ values in the deque. + +--- + +## 7. Dynamic Programming (Space Optimized) - III + +::tabs-start + +```python +class Solution: + def mincostTickets(self, days: List[int], costs: List[int]) -> int: + dp = [0] * 366 + i = 0 + + for d in range(1, 366): + dp[d] = dp[d - 1] + if i == len(days): + return dp[d] + + if d == days[i]: + dp[d] += costs[0] + dp[d] = min(dp[d], costs[1] + dp[max(0, d - 7)]) + dp[d] = min(dp[d], costs[2] + dp[max(0, d - 30)]) + i += 1 + + return dp[365] +``` + +```java +public class Solution { + public int mincostTickets(int[] days, int[] costs) { + int[] dp = new int[366]; + int i = 0; + + for (int d = 1; d < 366; d++) { + dp[d] = dp[d - 1]; + + if (i == days.length) { + return dp[d]; + } + + if (d == days[i]) { + dp[d] += costs[0]; + dp[d] = Math.min(dp[d], costs[1] + dp[Math.max(0, d - 7)]); + dp[d] = Math.min(dp[d], costs[2] + dp[Math.max(0, d - 30)]); + i++; + } + } + return dp[365]; + } +} +``` + +```cpp +class Solution { +public: + int mincostTickets(vector& days, vector& costs) { + vector dp(366, 0); + int i = 0; + + for (int d = 1; d < 366; d++) { + dp[d] = dp[d - 1]; + + if (i == days.size()) { + return dp[d]; + } + + if (d == days[i]) { + dp[d] += costs[0]; + dp[d] = min(dp[d], costs[1] + dp[max(0, d - 7)]); + dp[d] = min(dp[d], costs[2] + dp[max(0, d - 30)]); + i++; + } + } + return dp[365]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} days + * @param {number[]} costs + * @return {number} + */ + mincostTickets(days, costs) { + const dp = new Array(366).fill(0); + let i = 0; + + for (let d = 1; d < 366; d++) { + dp[d] = dp[d - 1]; + + if (i === days.length) { + return dp[d]; + } + + if (d === days[i]) { + dp[d] += costs[0]; + dp[d] = Math.min(dp[d], costs[1] + dp[Math.max(0, d - 7)]); + dp[d] = Math.min(dp[d], costs[2] + dp[Math.max(0, d - 30)]); + i++; + } + } + return dp[365]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ since the size of the $dp$ array is $366$. + + +--- + +## 8. Dynamic Programming (Space Optimized) - IV + +::tabs-start + +```python +class Solution: + def mincostTickets(self, days: List[int], costs: List[int]) -> int: + dp = [0] * 31 + i = 0 + + for d in range(1, 366): + if i >= len(days): + break + + dp[d % 31] = dp[(d - 1) % 31] + + if d == days[i]: + dp[d % 31] += costs[0] + dp[d % 31] = min(dp[d % 31], costs[1] + dp[max(0, d - 7) % 31]) + dp[d % 31] = min(dp[d % 31], costs[2] + dp[max(0, d - 30) % 31]) + i += 1 + + return dp[days[-1] % 31] +``` + +```java +public class Solution { + public int mincostTickets(int[] days, int[] costs) { + int[] dp = new int[31]; + int i = 0; + + for (int d = 1; d <= 365; d++) { + if (i >= days.length) break; + + dp[d % 31] = dp[(d - 1) % 31]; + + if (d == days[i]) { + dp[d % 31] += costs[0]; + dp[d % 31] = Math.min(dp[d % 31], costs[1] + dp[Math.max(0, d - 7) % 31]); + dp[d % 31] = Math.min(dp[d % 31], costs[2] + dp[Math.max(0, d - 30) % 31]); + i++; + } + } + + return dp[days[days.length - 1] % 31]; + } +} +``` + +```cpp +class Solution { +public: + int mincostTickets(vector& days, vector& costs) { + vector dp(31, 0); + int i = 0; + + for (int d = 1; d <= 365; d++) { + if (i >= days.size()) break; + + dp[d % 31] = dp[(d - 1) % 31]; + + if (d == days[i]) { + dp[d % 31] += costs[0]; + dp[d % 31] = min(dp[d % 31], costs[1] + dp[max(0, d - 7) % 31]); + dp[d % 31] = min(dp[d % 31], costs[2] + dp[max(0, d - 30) % 31]); + i++; + } + } + + return dp[days.back() % 31]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} days + * @param {number[]} costs + * @return {number} + */ + mincostTickets(days, costs) { + const dp = new Array(31).fill(0); + let i = 0; + + for (let d = 1; d <= 365; d++) { + if (i >= days.length) break; + + dp[d % 31] = dp[(d - 1) % 31]; + + if (d === days[i]) { + dp[d % 31] += costs[0]; + dp[d % 31] = Math.min(dp[d % 31], costs[1] + dp[Math.max(0, d - 7) % 31]); + dp[d % 31] = Math.min(dp[d % 31], costs[2] + dp[Math.max(0, d - 30) % 31]); + i++; + } + } + + return dp[days[days.length - 1] % 31]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ since the size of the $dp$ array is $31$. \ No newline at end of file diff --git a/articles/perfect-squares.md b/articles/perfect-squares.md new file mode 100644 index 000000000..ce067516a --- /dev/null +++ b/articles/perfect-squares.md @@ -0,0 +1,548 @@ +## 1. Recursion + +::tabs-start + +```python +class Solution: + def numSquares(self, n: int) -> int: + def dfs(target): + if target == 0: + return 0 + + res = target + for i in range(1, target): + if i * i > target: + break + res = min(res, 1 + dfs(target - i * i)) + return res + + return dfs(n) +``` + +```java +public class Solution { + public int numSquares(int n) { + return dfs(n); + } + + private int dfs(int target) { + if (target == 0) { + return 0; + } + + int res = target; + for (int i = 1; i * i <= target; i++) { + res = Math.min(res, 1 + dfs(target - i * i)); + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int numSquares(int n) { + return dfs(n); + } + +private: + int dfs(int target) { + if (target == 0) { + return 0; + } + + int res = target; + for (int i = 1; i * i <= target; i++) { + res = min(res, 1 + dfs(target - i * i)); + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {number} + */ + numSquares(n) { + const dfs = (target) => { + if (target === 0) return 0; + + let res = target; + for (let i = 1; i * i <= target; i++) { + res = Math.min(res, 1 + dfs(target - i * i)); + } + return res; + }; + + return dfs(n); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ {\sqrt {n}})$ +* Space complexity: $O(n)$ for recursion stack. + +--- + +## 2. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def numSquares(self, n: int) -> int: + memo = {} + + def dfs(target): + if target == 0: + return 0 + if target in memo: + return memo[target] + + res = target + for i in range(1, target + 1): + if i * i > target: + break + res = min(res, 1 + dfs(target - i * i)) + + memo[target] = res + return res + + return dfs(n) +``` + +```java +public class Solution { + Map memo = new HashMap<>(); + + private int dfs(int target) { + if (target == 0) return 0; + if (memo.containsKey(target)) return memo.get(target); + + int res = target; + for (int i = 1; i * i <= target; i++) { + res = Math.min(res, 1 + dfs(target - i * i)); + } + + memo.put(target, res); + return res; + } + + public int numSquares(int n) { + return dfs(n); + } +} +``` + +```cpp +class Solution { +public: + unordered_map memo; + + int dfs(int target) { + if (target == 0) return 0; + if (memo.count(target)) return memo[target]; + + int res = target; + for (int i = 1; i * i <= target; i++) { + res = min(res, 1 + dfs(target - i * i)); + } + + return memo[target] = res; + } + + int numSquares(int n) { + return dfs(n); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {number} + */ + numSquares(n) { + const memo = new Map(); + + const dfs = (target) => { + if (target === 0) return 0; + if (memo.has(target)) { + return memo.get(target); + } + + let res = target; + for (let i = 1; i * i <= target; i++) { + res = Math.min(res, 1 + dfs(target - i * i)); + } + memo.set(target, res); + return res; + }; + + return dfs(n); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * \sqrt {n})$ +* Space complexity: $O(n)$ + +--- + +## 3. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def numSquares(self, n: int) -> int: + dp = [n] * (n + 1) + dp[0] = 0 + + for target in range(1, n + 1): + for s in range(1, target + 1): + square = s * s + if target - square < 0: + break + dp[target] = min(dp[target], 1 + dp[target - square]) + + return dp[n] +``` + +```java +public class Solution { + public int numSquares(int n) { + int[] dp = new int[n + 1]; + Arrays.fill(dp, n); + dp[0] = 0; + + for (int target = 1; target <= n; target++) { + for (int s = 1; s * s <= target; s++) { + dp[target] = Math.min(dp[target], 1 + dp[target - s * s]); + } + } + + return dp[n]; + } +} +``` + +```cpp +class Solution { +public: + int numSquares(int n) { + vector dp(n + 1, n); + dp[0] = 0; + + for (int target = 1; target <= n; target++) { + for (int s = 1; s * s <= target; s++) { + dp[target] = min(dp[target], 1 + dp[target - s * s]); + } + } + + return dp[n]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {number} + */ + numSquares(n) { + const dp = Array(n + 1).fill(n); + dp[0] = 0; + + for (let target = 1; target <= n; target++) { + for (let s = 1; s * s <= target; s++) { + dp[target] = Math.min(dp[target], 1 + dp[target - s * s]); + } + } + + return dp[n]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * \sqrt {n})$ +* Space complexity: $O(n)$ + +--- + +## 4. Breadth First Search + +::tabs-start + +```python +class Solution: + def numSquares(self, n: int) -> int: + q = deque() + seen = set() + + res = 0 + q.append(0) + while q: + res += 1 + for _ in range(len(q)): + cur = q.popleft() + s = 1 + while s * s + cur <= n: + nxt = cur + s * s + if nxt == n: + return res + if nxt not in seen: + seen.add(nxt) + q.append(nxt) + s += 1 + + return res +``` + +```java +public class Solution { + public int numSquares(int n) { + Queue q = new LinkedList<>(); + Set seen = new HashSet<>(); + + int res = 0; + q.offer(0); + while (!q.isEmpty()) { + res++; + for (int i = q.size(); i > 0; i--) { + int cur = q.poll(); + for (int s = 1; s * s + cur <= n; s++) { + int next = cur + s * s; + if (next == n) return res; + if (!seen.contains(next)) { + q.offer(next); + seen.add(next); + } + } + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int numSquares(int n) { + queue q; + unordered_set seen; + + int res = 0; + q.push(0); + while (!q.empty()) { + res++; + for (int i = q.size(); i > 0; i--) { + int cur = q.front(); q.pop(); + for (int s = 1; s * s + cur <= n; s++) { + int next = cur + s * s; + if (next == n) return res; + if (seen.find(next) == seen.end()) { + q.push(next); + seen.insert(next); + } + } + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {number} + */ + numSquares(n) { + const q = new Queue; + const seen = new Set(); + + let res = 0; + q.push(0); + while (!q.isEmpty()) { + res++; + for (let i = q.size(); i > 0; i--) { + const cur = q.pop(); + for (let s = 1; s * s + cur <= n; s++) { + const next = cur + s * s; + if (next === n) return res; + if (!seen.has(next)) { + q.push(next); + seen.add(next); + } + } + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * \sqrt {n})$ +* Space complexity: $O(n)$ + +--- + +## 5. Math + +::tabs-start + +```python +class Solution: + def numSquares(self, n: int) -> int: + while n % 4 == 0: + n //= 4 + + if n % 8 == 7: + return 4 + + def isSquareNum(num): + s = int(math.sqrt(num)) + return s * s == num + + if isSquareNum(n): + return 1 + + i = 1 + while i * i <= n: + if isSquareNum(n - i * i): + return 2 + i += 1 + + return 3 +``` + +```java +public class Solution { + public int numSquares(int n) { + while (n % 4 == 0) { + n /= 4; + } + + if (n % 8 == 7) { + return 4; + } + + if (isSquareNum(n)) { + return 1; + } + + for (int i = 1; i * i <= n; i++) { + if (isSquareNum(n - i * i)) { + return 2; + } + } + + return 3; + } + + private boolean isSquareNum(int num) { + int s = (int) Math.sqrt(num); + return s * s == num; + } +} +``` + +```cpp +class Solution { +public: + int numSquares(int n) { + while (n % 4 == 0) { + n /= 4; + } + + if (n % 8 == 7) { + return 4; + } + + if (isSquareNum(n)) { + return 1; + } + + for (int i = 1; i * i <= n; i++) { + if (isSquareNum(n - i * i)) { + return 2; + } + } + + return 3; + } + +private: + bool isSquareNum(int num) { + int s = (int) sqrt(num); + return s * s == num; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {number} + */ + numSquares(n) { + while (n % 4 === 0) { + n = Math.floor(n / 4); + } + + if (n % 8 === 7) { + return 4; + } + + const isSquareNum = (num) => { + const s = Math.floor(Math.sqrt(num)); + return s * s === num; + }; + + if (isSquareNum(n)) { + return 1; + } + + for (let i = 1; i * i <= n; i++) { + if (isSquareNum(n - i * i)) { + return 2; + } + } + + return 3; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(\sqrt {n})$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/queue-reconstruction-by-height.md b/articles/queue-reconstruction-by-height.md new file mode 100644 index 000000000..72dbfcf5c --- /dev/null +++ b/articles/queue-reconstruction-by-height.md @@ -0,0 +1,1185 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def reconstructQueue(self, people: List[List[int]]) -> List[List[int]]: + n = len(people) + mp = defaultdict(list) + + for p in people: + mp[p[1]].append(p[0]) + for key in mp: + mp[key].sort(reverse=True) + + res = [] + for i in range(n): + mini = -1 + for k in mp: + if k > i: + continue + cnt = 0 + j = len(res) - 1 + while j >= 0: + if res[j][0] >= mp[k][-1]: + cnt += 1 + j -= 1 + if cnt == k and (mini == -1 or mp[k][-1] < mp[mini][-1]): + mini = k + + res.append([mp[mini].pop(), mini]) + if not mp[mini]: + mp.pop(mini) + return res +``` + +```java +public class Solution { + public int[][] reconstructQueue(int[][] people) { + int n = people.length; + Map> mp = new HashMap<>(); + + for (int[] p : people) { + mp.computeIfAbsent(p[1], k -> new ArrayList<>()).add(p[0]); + } + for (int key : mp.keySet()) { + Collections.sort(mp.get(key), Collections.reverseOrder()); + } + + List res = new ArrayList<>(); + for (int i = 0; i < n; i++) { + int mini = -1; + for (int k : mp.keySet()) { + if (k > i) continue; + + int cnt = 0; + for (int j = res.size() - 1; j >= 0; j--) { + if (res.get(j)[0] >= mp.get(k).get(mp.get(k).size() - 1)) { + cnt++; + } + } + + if (cnt == k && (mini == -1 || mp.get(k).get(mp.get(k).size() - 1) < + mp.get(mini).get(mp.get(mini).size() - 1))) { + mini = k; + } + } + + List list = mp.get(mini); + res.add(new int[]{list.get(list.size() - 1), mini}); + list.remove(list.size() - 1); + if (list.isEmpty()) { + mp.remove(mini); + } + } + + return res.toArray(new int[n][2]); + } +} +``` + +```cpp +class Solution { +public: + vector> reconstructQueue(vector>& people) { + int n = people.size(); + unordered_map> mp; + + for (const auto& p : people) { + mp[p[1]].push_back(p[0]); + } + for (auto& pair : mp) { + sort(pair.second.rbegin(), pair.second.rend()); + } + + vector> res; + for (int i = 0; i < n; i++) { + int mini = -1; + for (const auto& pair : mp) { + int k = pair.first; + if (k > i) continue; + + int cnt = 0; + for (int j = res.size() - 1; j >= 0; j--) { + if (res[j][0] >= mp[k].back()) { + cnt++; + } + } + + if (cnt == k && (mini == -1 || mp[k].back() < mp[mini].back())) { + mini = k; + } + } + + res.push_back({mp[mini].back(), mini}); + mp[mini].pop_back(); + if (mp[mini].empty()) { + mp.erase(mini); + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} people + * @return {number[][]} + */ + reconstructQueue(people) { + const n = people.length; + const mp = new Map(); + + for (const [h, k] of people) { + if (!mp.has(k)) mp.set(k, []); + mp.get(k).push(h); + } + for (const [k, heights] of mp) { + heights.sort((a, b) => b - a); + } + + const res = []; + for (let i = 0; i < n; i++) { + let mini = -1; + for (const [k, heights] of mp) { + if (k > i) continue; + + let cnt = 0; + for (let j = res.length - 1; j >= 0; j--) { + if (res[j][0] >= heights[heights.length - 1]) cnt++; + } + + if (cnt === k && (mini === -1 || heights[heights.length - 1] < mp.get(mini)[mp.get(mini).length - 1])) { + mini = k; + } + } + + res.push([mp.get(mini).pop(), mini]); + if (mp.get(mini).length === 0) { + mp.delete(mini); + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n + n ^ 3)$ +* Space complexity: $O(n)$ + +--- + +## 2. Sorting by Height in Descending Order + +::tabs-start + +```python +class Solution: + def reconstructQueue(self, people: List[List[int]]) -> List[List[int]]: + res = [] + people.sort(key=lambda x: (-x[0], x[1])) + for p in people: + res.insert(p[1], p) + return res +``` + +```java +public class Solution { + public int[][] reconstructQueue(int[][] people) { + Arrays.sort(people, (a, b) -> a[0] == b[0] ? a[1] - b[1] : b[0] - a[0]); + List res = new ArrayList<>(); + for (int[] person : people) { + res.add(person[1], person); + } + return res.toArray(new int[res.size()][]); + } +} +``` + +```cpp +class Solution { +public: + vector> reconstructQueue(vector>& people) { + sort(people.begin(), people.end(), [](auto& a, auto& b) { + return a[0] == b[0] ? a[1] < b[1] : a[0] > b[0]; + }); + + list> res; + for (const auto& p : people) { + auto it = res.begin(); + advance(it, p[1]); + res.insert(it, p); + } + + return vector>(res.begin(), res.end()); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} people + * @return {number[][]} + */ + reconstructQueue(people) { + people.sort((a, b) => (a[0] === b[0] ? a[1] - b[1] : b[0] - a[0])); + const res = []; + for (const [h, k] of people) { + res.splice(k, 0, [h, k]); + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n\log n + n ^ 2)$ +* Space complexity: $O(n)$ + +--- + +## 3. Sorting by Height in Ascending Order + +::tabs-start + +```python +class Solution: + def reconstructQueue(self, people: List[List[int]]) -> List[List[int]]: + n = len(people) + people.sort(key=lambda x: (x[0], -x[1])) + res = [[] for _ in range(n)] + + for p in people: + cnt = i = 0 + while i < n: + if not res[i]: + if cnt == p[1]: + break + cnt += 1 + i += 1 + res[i] = p + + return res +``` + +```java +public class Solution { + public int[][] reconstructQueue(int[][] people) { + int n = people.length; + Arrays.sort(people, (a, b) -> a[0] == b[0] ? b[1] - a[1] : a[0] - b[0]); + int[][] res = new int[n][2]; + boolean[] used = new boolean[n]; + + for (int[] p : people) { + int cnt = 0, i = 0; + while (i < n) { + if (!used[i]) { + if (cnt == p[1]) break; + cnt++; + } + i++; + } + used[i] = true; + res[i] = p; + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector> reconstructQueue(vector>& people) { + sort(people.begin(), people.end(), [](auto& a, auto& b) { + return a[0] == b[0] ? a[1] > b[1] : a[0] < b[0]; + }); + + vector> res(people.size(), vector()); + for (const auto& p : people) { + int cnt = 0, i = 0; + while (i < people.size()) { + if (res[i].empty()) { + if (cnt == p[1]) break; + cnt++; + } + i++; + } + res[i] = p; + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} people + * @return {number[][]} + */ + reconstructQueue(people) { + people.sort((a, b) => a[0] === b[0] ? b[1] - a[1] : a[0] - b[0]); + const res = Array(people.length).fill(null); + + for (const p of people) { + let cnt = 0, i = 0; + + while (i < people.length) { + if (res[i] === null) { + if (cnt === p[1]) break; + cnt++; + } + i++; + } + res[i] = p; + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n\log n + n ^ 2)$ +* Space complexity: $O(n)$ + +--- + +## 4. Binary Search + Segment Tree + +::tabs-start + +```python +class SegmentTree: + def __init__(self, N): + self.n = N + while (self.n & (self.n - 1)) != 0: + self.n += 1 + self.build(N) + + def build(self, N): + self.tree = [0] * (2 * self.n) + for i in range(N): + self.tree[self.n + i] = 1 + for i in range(self.n - 1, 0, -1): + self.tree[i] = self.tree[i << 1] + self.tree[i << 1 | 1] + + def update(self, i, val): + self.tree[self.n + i] = val + j = (self.n + i) >> 1 + while j >= 1: + self.tree[j] = self.tree[j << 1] + self.tree[j << 1 | 1] + j >>= 1 + + def query(self, l, r): + res = 0 + l += self.n + r += self.n + 1 + while l < r: + if l & 1: + res += self.tree[l] + l += 1 + if r & 1: + r -= 1 + res += self.tree[r] + l >>= 1 + r >>= 1 + return res + +class Solution: + def reconstructQueue(self, people: List[List[int]]) -> List[List[int]]: + n = len(people) + people.sort(key=lambda x: (x[0], -x[1])) + res = [[] for _ in range(n)] + + segTree = SegmentTree(n) + for p in people: + l, r = 0, n - 1 + idx = 0 + while l <= r: + mid = (l + r) >> 1 + cnt = segTree.query(0, mid) + if cnt > p[1]: + idx = mid + r = mid - 1 + else: + l = mid + 1 + + res[idx] = p + segTree.update(idx, 0) + + return res +``` + +```java +class SegmentTree { + int n; + int[] tree; + + SegmentTree(int N) { + this.n = N; + while (Integer.bitCount(n) != 1) { + n++; + } + build(N); + } + + void build(int N) { + tree = new int[2 * n]; + for (int i = 0; i < N; i++) { + tree[n + i] = 1; + } + for (int i = n - 1; i > 0; --i) { + tree[i] = tree[i << 1] + tree[i << 1 | 1]; + } + } + + void update(int i, int val) { + tree[n + i] = val; + for (int j = (n + i) >> 1; j >= 1; j >>= 1) { + tree[j] = tree[j << 1] + tree[j << 1 | 1]; + } + } + + int query(int l, int r) { + int res = 0; + for (l += n, r += n + 1; l < r; l >>= 1, r >>= 1) { + if ((l & 1) == 1) res += tree[l++]; + if ((r & 1) == 1) res += tree[--r]; + } + return res; + } +} + +public class Solution { + public int[][] reconstructQueue(int[][] people) { + int n = people.length; + Arrays.sort(people, (a, b) -> a[0] == b[0] ? b[1] - a[1] : a[0] - b[0]); + int[][] res = new int[n][2]; + + SegmentTree segTree = new SegmentTree(n); + for (int[] p : people) { + int l = 0, r = n - 1, idx = 0; + while (l <= r) { + int mid = (l + r) >> 1; + int cnt = segTree.query(0, mid); + if (cnt > p[1]) { + idx = mid; + r = mid - 1; + } else { + l = mid + 1; + } + } + + res[idx] = p; + segTree.update(idx, 0); + } + + return res; + } +} +``` + +```cpp +class SegmentTree { +public: + int n; + vector tree; + + SegmentTree(int N) { + this->n = N; + while (__builtin_popcount(n) != 1) { + n++; + } + build(N); + } + + void build(int N) { + tree.resize(2 * n); + for (int i = 0; i < N; i++) { + tree[n + i] = 1; + } + for (int i = n - 1; i > 0; --i) { + tree[i] = tree[i << 1] + tree[i << 1 | 1]; + } + } + + void update(int i, int val) { + tree[n + i] = val; + for (int j = (n + i) >> 1; j >= 1; j >>= 1) { + tree[j] = tree[j << 1] + tree[j << 1 | 1]; + } + } + + int query(int l, int r) { + int res = 0; + for (l += n, r += n + 1; l < r; l >>= 1, r >>= 1) { + if (l & 1) res += tree[l++]; + if (r & 1) res += tree[--r]; + } + return res; + } +}; + +class Solution { +public: + vector> reconstructQueue(vector>& people) { + int n = people.size(); + sort(people.begin(), people.end(), [](auto& a, auto& b) { + return a[0] == b[0] ? a[1] > b[1] : a[0] < b[0]; + }); + vector> res(n, vector()); + + SegmentTree segTree(n); + for (const auto& p : people) { + int l = 0, r = n - 1, idx = 0; + while (l <= r) { + int mid = (l + r) >> 1; + int cnt = segTree.query(0, mid); + if (cnt > p[1]) { + idx = mid; + r = mid - 1; + } else { + l = mid + 1; + } + } + + res[idx] = p; + segTree.update(idx, 0); + } + + return res; + } +}; +``` + +```javascript +class SegmentTree { + /** + * @constructor + * @param {number} N + */ + constructor(N) { + this.n = N; + while ((this.n & (this.n - 1)) !== 0) { + this.n++; + } + this.build(N); + } + + /** + * @param {number} N + * @return {void} + */ + build(N) { + this.tree = new Int32Array(2 * this.n); + for (let i = 0; i < N; i++) { + this.tree[this.n + i] = 1; + } + for (let i = this.n - 1; i > 0; i--) { + this.tree[i] = this.tree[i << 1] + this.tree[i << 1 | 1]; + } + } + + /** + * @param {number} i + * @param {number} val + * @return {void} + */ + update(i, val) { + this.tree[this.n + i] = val; + for (let j = (this.n + i) >> 1; j >= 1; j >>= 1) { + this.tree[j] = this.tree[j << 1] + this.tree[j << 1 | 1]; + } + } + + /** + * @param {number} l + * @param {number} r + * @return {number} + */ + query(l, r) { + let res = 0; + l += this.n; + r += this.n + 1; + + while (l < r) { + if (l & 1) res += this.tree[l++]; + if (r & 1) res += this.tree[--r]; + l >>= 1; + r >>= 1; + } + + return res; + } +} + +class Solution { + /** + * @param {number[][]} people + * @return {number[][]} + */ + reconstructQueue(people) { + const n = people.length; + people.sort((a, b) => a[0] === b[0] ? b[1] - a[1] : a[0] - b[0]); + const res = Array(n).fill(null); + + const segTree = new SegmentTree(n); + for (const p of people) { + let l = 0, r = n - 1, idx = 0; + while (l <= r) { + let mid = (l + r) >> 1; + let cnt = segTree.query(0, mid); + if (cnt > p[1]) { + idx = mid; + r = mid - 1; + } else { + l = mid + 1; + } + } + + res[idx] = p; + segTree.update(idx, 0); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n\log ^ 2 n)$ +* Space complexity: $O(n)$ + +--- + +## 5. Binary Search + Binary Indexed Tree (Fenwick Tree) + +::tabs-start + +```python +class BIT: + def __init__(self, N): + self.n = N + 1 + self.tree = [0] * self.n + for i in range(self.n - 1): + self.update(i, 1) + + def update(self, index, val): + index += 1 + while index < self.n: + self.tree[index] += val + index += index & -index + + def prefix_sum(self, index): + total_sum = 0 + while index > 0: + total_sum += self.tree[index] + index -= index & -index + return total_sum + + def query(self, left, right): + return self.prefix_sum(right + 1) - self.prefix_sum(left) + +class Solution: + def reconstructQueue(self, people: List[List[int]]) -> List[List[int]]: + n = len(people) + people.sort(key=lambda x: (x[0], -x[1])) + res = [[] for _ in range(n)] + + bit = BIT(n) + for p in people: + l, r = 0, n - 1 + idx = 0 + while l <= r: + mid = (l + r) >> 1 + cnt = bit.query(0, mid) + if cnt > p[1]: + idx = mid + r = mid - 1 + else: + l = mid + 1 + + res[idx] = p + bit.update(idx, -1) + + return res +``` + +```java +class BIT { + private int[] tree; + private int n; + + public BIT(int N) { + this.n = N + 1; + this.tree = new int[n]; + for (int i = 0; i < n - 1; i++) { + update(i, 1); + } + } + + public void update(int index, int val) { + index++; + while (index < n) { + tree[index] += val; + index += index & -index; + } + } + + public int prefixSum(int index) { + int totalSum = 0; + while (index > 0) { + totalSum += tree[index]; + index -= index & -index; + } + return totalSum; + } + + public int query(int left, int right) { + return prefixSum(right + 1) - prefixSum(left); + } +} + +public class Solution { + public int[][] reconstructQueue(int[][] people) { + int n = people.length; + Arrays.sort(people, (a, b) -> a[0] == b[0] ? b[1] - a[1] : a[0] - b[0]); + int[][] res = new int[n][2]; + + BIT bit = new BIT(n); + for (int[] p : people) { + int l = 0, r = n - 1, idx = 0; + while (l <= r) { + int mid = (l + r) >> 1; + int cnt = bit.query(0, mid); + if (cnt > p[1]) { + idx = mid; + r = mid - 1; + } else { + l = mid + 1; + } + } + + res[idx] = p; + bit.update(idx, -1); + } + + return res; + } +} +``` + +```cpp +class BIT { + vector tree; + int n; + +public: + BIT(int N) { + n = N + 1; + tree.resize(n, 0); + for (int i = 0; i < n - 1; i++) { + update(i, 1); + } + } + + void update(int index, int val) { + index++; + while (index < n) { + tree[index] += val; + index += index & -index; + } + } + + int prefixSum(int index) { + int totalSum = 0; + while (index > 0) { + totalSum += tree[index]; + index -= index & -index; + } + return totalSum; + } + + int query(int left, int right) { + return prefixSum(right + 1) - prefixSum(left); + } +}; + +class Solution { +public: + vector> reconstructQueue(vector>& people) { + int n = people.size(); + sort(people.begin(), people.end(), [](auto& a, auto& b) { + return a[0] == b[0] ? a[1] > b[1] : a[0] < b[0]; + }); + vector> res(n, vector()); + + BIT bit(n); + for (const auto& p : people) { + int l = 0, r = n - 1, idx = 0; + while (l <= r) { + int mid = (l + r) >> 1; + int cnt = bit.query(0, mid); + if (cnt > p[1]) { + idx = mid; + r = mid - 1; + } else { + l = mid + 1; + } + } + + res[idx] = p; + bit.update(idx, -1); + } + + return res; + } +}; +``` + +```javascript +class BIT { + /** + * @constructor + * @param {number} N + */ + constructor(N) { + this.n = N + 1; + this.tree = new Int32Array(this.n); + for (let i = 0; i < this.n - 1; i++) { + this.update(i, 1); + } + } + + /** + * @param {number} index + * @param {number} val + * @return {void} + */ + update(index, val) { + index++; + while (index < this.n) { + this.tree[index] += val; + index += index & -index; + } + } + + /** + * @param {number} index + * @return {number} + */ + prefixSum(index) { + let totalSum = 0; + while (index > 0) { + totalSum += this.tree[index]; + index -= index & -index; + } + return totalSum; + } + + /** + * @param {number} left + * @param {number} right + * @return {number} + */ + query(left, right) { + return this.prefixSum(right + 1) - this.prefixSum(left); + } +} + +class Solution { + /** + * @param {number[][]} people + * @return {number[][]} + */ + reconstructQueue(people) { + const n = people.length; + people.sort((a, b) => a[0] === b[0] ? b[1] - a[1] : a[0] - b[0]); + const res = Array(n).fill(null); + + const bit = new BIT(n); + for (const p of people) { + let l = 0, r = n - 1, idx = 0; + while (l <= r) { + let mid = (l + r) >> 1; + let cnt = bit.query(0, mid); + if (cnt > p[1]) { + idx = mid; + r = mid - 1; + } else { + l = mid + 1; + } + } + + res[idx] = p; + bit.update(idx, -1); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n\log ^ 2 n)$ +* Space complexity: $O(n)$ + +--- + +## 6. Binary Indexed Tree (Fenwick Tree) + +::tabs-start + +```python +class BIT: + def __init__(self, N): + self.n = N + 1 + self.tree = [0] * self.n + for i in range(self.n - 1): + self.update(i, 1) + + def update(self, index, val): + index += 1 + while index < self.n: + self.tree[index] += val + index += index & -index + + def getIdx(self, cnt, MSB): + idx = 0 + while MSB: + nxtIdx = idx + MSB + if nxtIdx < self.n and cnt >= self.tree[nxtIdx]: + idx = nxtIdx + cnt -= self.tree[nxtIdx] + MSB >>= 1 + return idx + +class Solution: + def reconstructQueue(self, people: List[List[int]]) -> List[List[int]]: + n = len(people) + people.sort(key=lambda x: (x[0], -x[1])) + res = [[] for _ in range(n)] + + bit = BIT(n) + MSB = 1 << int(math.log(n, 2)) + for p in people: + idx = bit.getIdx(p[1], MSB) + res[idx] = p + bit.update(idx, -1) + + return res +``` + +```java +class BIT { + private int[] tree; + private int n; + + public BIT(int N) { + this.n = N + 1; + this.tree = new int[n]; + for (int i = 0; i < n - 1; i++) { + update(i, 1); + } + } + + public void update(int index, int val) { + index++; + while (index < n) { + tree[index] += val; + index += index & -index; + } + } + + public int getIdx(int cnt, int MSB) { + int idx = 0; + while (MSB != 0) { + int nxtIdx = idx + MSB; + if (nxtIdx < n && cnt >= tree[nxtIdx]) { + idx = nxtIdx; + cnt -= tree[nxtIdx]; + } + MSB >>= 1; + } + return idx; + } +} + +public class Solution { + public int[][] reconstructQueue(int[][] people) { + int n = people.length; + Arrays.sort(people, (a, b) -> a[0] == b[0] ? b[1] - a[1] : a[0] - b[0]); + int[][] res = new int[n][2]; + + BIT bit = new BIT(n); + int MSB = 1 << (31 - Integer.numberOfLeadingZeros(n)); + for (int[] p : people) { + int idx = bit.getIdx(p[1], MSB); + res[idx] = p; + bit.update(idx, -1); + } + + return res; + } +} +``` + +```cpp +class BIT { + vector tree; + int n; + +public: + BIT(int N) { + n = N + 1; + tree.resize(n, 0); + for (int i = 0; i < n - 1; i++) { + update(i, 1); + } + } + + void update(int index, int val) { + index++; + while (index < n) { + tree[index] += val; + index += index & -index; + } + } + + int getIdx(int cnt, int MSB) { + int idx = 0; + while (MSB != 0) { + int nxtIdx = idx + MSB; + if (nxtIdx < n && cnt >= tree[nxtIdx]) { + idx = nxtIdx; + cnt -= tree[nxtIdx]; + } + MSB >>= 1; + } + return idx; + } +}; + +class Solution { +public: + vector> reconstructQueue(vector>& people) { + int n = people.size(); + sort(people.begin(), people.end(), [](auto& a, auto& b) { + return a[0] == b[0] ? a[1] > b[1] : a[0] < b[0]; + }); + vector> res(n, vector()); + + BIT bit(n); + int MSB = 1 << (31 - __builtin_clz(n)); + for (const auto& p : people) { + int idx = bit.getIdx(p[1], MSB); + res[idx] = p; + bit.update(idx, -1); + } + + return res; + } +}; +``` + +```javascript +class BIT { + /** + * @constructor + * @param {number} N + */ + constructor(N) { + this.n = N + 1; + this.tree = new Int32Array(this.n); + for (let i = 0; i < this.n - 1; i++) { + this.update(i, 1); + } + } + + /** + * @param {number} index + * @param {number} val + * @return {void} + */ + update(index, val) { + index++; + while (index < this.n) { + this.tree[index] += val; + index += index & -index; + } + } + + /** + * @param {number} cnt + * @param {number} MSB + * @return {number} + */ + getIdx(cnt, MSB) { + let idx = 0; + while (MSB != 0) { + let nxtIdx = idx + MSB; + if (nxtIdx < this.n && cnt >= this.tree[nxtIdx]) { + idx = nxtIdx; + cnt -= this.tree[nxtIdx]; + } + MSB >>= 1; + } + return idx; + } +} + +class Solution { + /** + * @param {number[][]} people + * @return {number[][]} + */ + reconstructQueue(people) { + const n = people.length; + people.sort((a, b) => a[0] === b[0] ? b[1] - a[1] : a[0] - b[0]); + const res = Array(n).fill(null); + + const bit = new BIT(n); + const MSB = 1 << Math.floor(Math.log2(n)); + for (const p of people) { + let idx = bit.getIdx(p[1], MSB); + res[idx] = p; + bit.update(idx, -1); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n\log n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/range-sum-query-mutable.md b/articles/range-sum-query-mutable.md new file mode 100644 index 000000000..358921e03 --- /dev/null +++ b/articles/range-sum-query-mutable.md @@ -0,0 +1,1513 @@ +## 1. Brute Force + +::tabs-start + +```python +class NumArray: + + def __init__(self, nums: List[int]): + self.nums = nums + + def update(self, index: int, val: int) -> None: + self.nums[index] = val + + def sumRange(self, left: int, right: int) -> int: + res = 0 + for i in range(left, right + 1): + res += self.nums[i] + return res +``` + +```java +public class NumArray { + private int[] nums; + + public NumArray(int[] nums) { + this.nums = nums; + } + + public void update(int index, int val) { + nums[index] = val; + } + + public int sumRange(int left, int right) { + int res = 0; + for (int i = left; i <= right; i++) { + res += nums[i]; + } + return res; + } +} +``` + +```cpp +class NumArray { +private: + vector nums; + +public: + NumArray(vector& nums) { + this->nums = nums; + } + + void update(int index, int val) { + nums[index] = val; + } + + int sumRange(int left, int right) { + int res = 0; + for (int i = left; i <= right; i++) { + res += nums[i]; + } + return res; + } +}; +``` + +```javascript +class NumArray { + /** + * @param {number[]} nums + */ + constructor(nums) { + this.nums = nums; + } + + /** + * @param {number} index + * @param {number} val + * @return {void} + */ + update(index, val) { + this.nums[index] = val; + } + + /** + * @param {number} left + * @param {number} right + * @return {number} + */ + sumRange(left, right) { + let res = 0; + for (let i = left; i <= right; i++) { + res += this.nums[i]; + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: + * $O(n)$ for initializing the input array. + * $O(1)$ for each $update()$ function call. + * $O(n)$ for each $sumRange()$ function call. +* Space complexity: $O(n)$ + +--- + +## 2. Recursive Segment Tree + +::tabs-start + +```python +class SegmentTree: + def __init__(self, N, A): + self.n = N + while (self.n & (self.n - 1)) != 0: + self.n += 1 + self.tree = [0] * (2 * self.n) + self.build(0, 0, self.n - 1, A) + + def build(self, node, start, end, A): + if start == end: + if start < len(A): + self.tree[node] = A[start] + else: + mid = (start + end) // 2 + left = 2 * node + 1 + right = 2 * node + 2 + self.build(left, start, mid, A) + self.build(right, mid + 1, end, A) + self.tree[node] = self.tree[left] + self.tree[right] + + def update(self, i, val): + def _update(node, start, end, idx, value): + if start == end: + self.tree[node] = value + else: + mid = (start + end) // 2 + left = 2 * node + 1 + right = 2 * node + 2 + if idx <= mid: + _update(left, start, mid, idx, value) + else: + _update(right, mid + 1, end, idx, value) + self.tree[node] = self.tree[left] + self.tree[right] + _update(0, 0, self.n - 1, i, val) + + def query(self, l, r): + def _query(node, start, end, L, R): + if R < start or L > end: + return 0 + if L <= start and end <= R: + return self.tree[node] + mid = (start + end) // 2 + left = 2 * node + 1 + right = 2 * node + 2 + left_sum = _query(left, start, mid, L, R) + right_sum = _query(right, mid + 1, end, L, R) + return left_sum + right_sum + return _query(0, 0, self.n - 1, l, r) + +class NumArray: + + def __init__(self, nums: List[int]): + self.segTree = SegmentTree(len(nums), nums) + + def update(self, index: int, val: int) -> None: + self.segTree.update(index, val) + + def sumRange(self, left: int, right: int) -> int: + return self.segTree.query(left, right) +``` + +```java +class SegmentTree { + int n; + int[] tree; + + SegmentTree(int N, int[] A) { + this.n = N; + while (Integer.bitCount(n) != 1) { + n++; + } + tree = new int[2 * n]; + build(0, 0, n - 1, A); + } + + void build(int node, int start, int end, int[] A) { + if (start == end) { + tree[node] = (start < A.length) ? A[start] : 0; + return; + } + int mid = start + (end - start) / 2; + build(node * 2 + 1, start, mid, A); + build(node * 2 + 2, mid + 1, end, A); + tree[node] = tree[node * 2 + 1] + tree[node * 2 + 2]; + } + + void update(int idx, int val) { + update(0, 0, n - 1, idx, val); + } + + void update(int node, int start, int end, int idx, int val) { + if (start == end) { + tree[node] = val; + return; + } + int mid = start + (end - start) / 2; + if (idx <= mid) { + update(node * 2 + 1, start, mid, idx, val); + } else { + update(node * 2 + 2, mid + 1, end, idx, val); + } + tree[node] = tree[node * 2 + 1] + tree[node * 2 + 2]; + } + + int query(int l, int r) { + return query(0, 0, n - 1, l, r); + } + + int query(int node, int start, int end, int l, int r) { + if (start > r || end < l) { + return 0; + } + if (start >= l && end <= r) { + return tree[node]; + } + int mid = start + (end - start) / 2; + int leftSum = query(node * 2 + 1, start, mid, l, r); + int rightSum = query(node * 2 + 2, mid + 1, end, l, r); + return leftSum + rightSum; + } +} + +public class NumArray { + private SegmentTree segTree; + + public NumArray(int[] nums) { + this.segTree = new SegmentTree(nums.length, nums); + } + + public void update(int index, int val) { + this.segTree.update(index, val); + } + + public int sumRange(int left, int right) { + return this.segTree.query(left, right); + } +} +``` + +```cpp +class SegmentTree { +public: + int n; + vector tree; + + SegmentTree(int N, vector& A) { + this->n = N; + while (__builtin_popcount(n) != 1) { + n++; + } + tree.resize(2 * n); + build(0, 0, n - 1, A); + } + + void build(int node, int start, int end, vector& A) { + if (start == end) { + tree[node] = (start < A.size()) ? A[start] : 0; + } else { + int mid = (start + end) / 2; + build(2 * node + 1, start, mid, A); + build(2 * node + 2, mid + 1, end, A); + tree[node] = tree[2 * node + 1] + tree[2 * node + 2]; + } + } + + void update(int node, int start, int end, int idx, int val) { + if (start == end) { + tree[node] = val; + } else { + int mid = (start + end) / 2; + if (idx <= mid) { + update(2 * node + 1, start, mid, idx, val); + } else { + update(2 * node + 2, mid + 1, end, idx, val); + } + tree[node] = tree[2 * node + 1] + tree[2 * node + 2]; + } + } + + int query(int node, int start, int end, int l, int r) { + if (r < start || end < l) { + return 0; + } + if (l <= start && end <= r) { + return tree[node]; + } + int mid = (start + end) / 2; + int leftSum = query(2 * node + 1, start, mid, l, r); + int rightSum = query(2 * node + 2, mid + 1, end, l, r); + return leftSum + rightSum; + } + + void update(int idx, int val) { + update(0, 0, n - 1, idx, val); + } + + int query(int l, int r) { + return query(0, 0, n - 1, l, r); + } +}; + +class NumArray { + SegmentTree* segTree; + +public: + NumArray(vector& nums) { + this->segTree = new SegmentTree(nums.size(), nums); + } + + void update(int index, int val) { + this->segTree->update(index, val); + } + + int sumRange(int left, int right) { + return this->segTree->query(left, right); + } +}; +``` + +```javascript +class SegmentTree { + /** + * @constructor + * @param {number} N + * @param {number[]} A + */ + constructor(N, A) { + this.n = N; + while ((this.n & (this.n - 1)) !== 0) { + this.n++; + } + this.tree = new Int32Array(2 * this.n); + this.build(0, 0, this.n - 1, A); + } + + /** + * @param {number} node + * @param {number} start + * @param {number} end + * @param {number[]} A + */ + build(node, start, end, A) { + if (start === end) { + this.tree[node] = start < A.length ? A[start] : 0; + } else { + const mid = Math.floor((start + end) / 2); + this.build(2 * node + 1, start, mid, A); + this.build(2 * node + 2, mid + 1, end, A); + this.tree[node] = this.tree[2 * node + 1] + this.tree[2 * node + 2]; + } + } + + /** + * @param {number} node + * @param {number} start + * @param {number} end + * @param {number} idx + * @param {number} val + * @return {void} + */ + _update(node, start, end, idx, val) { + if (start === end) { + this.tree[node] = val; + } else { + const mid = Math.floor((start + end) / 2); + if (idx <= mid) { + this._update(2 * node + 1, start, mid, idx, val); + } else { + this._update(2 * node + 2, mid + 1, end, idx, val); + } + this.tree[node] = this.tree[2 * node + 1] + this.tree[2 * node + 2]; + } + } + + /** + * @param {number} node + * @param {number} start + * @param {number} end + * @param {number} l + * @param {number} r + * @return {number} + */ + _query(node, start, end, l, r) { + if (r < start || end < l) { + return 0; + } + if (l <= start && end <= r) { + return this.tree[node]; + } + const mid = Math.floor((start + end) / 2); + const leftSum = this._query(2 * node + 1, start, mid, l, r); + const rightSum = this._query(2 * node + 2, mid + 1, end, l, r); + return leftSum + rightSum; + } + + /** + * @param {number} idx + * @param {number} val + * @return {void} + */ + update(idx, val) { + this._update(0, 0, this.n - 1, idx, val); + } + + /** + * @param {number} l + * @param {number} r + * @return {number} + */ + query(l, r) { + return this._query(0, 0, this.n - 1, l, r); + } +} + +class NumArray { + /** + * @param {number[]} nums + */ + constructor(nums) { + this.segTree = new SegmentTree(nums.length, nums); + } + + /** + * @param {number} index + * @param {number} val + * @return {void} + */ + update(index, val) { + this.segTree.update(index, val); + } + + /** + * @param {number} left + * @param {number} right + * @return {number} + */ + sumRange(left, right) { + return this.segTree.query(left, right); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: + * $O(n)$ for initializing the input array. + * $O(\log n)$ for each $update()$ function call. + * $O(\log n)$ for each $sumRange()$ function call. +* Space complexity: $O(n)$ + +--- + +## 3. Iterative Segement Tree + +::tabs-start + +```python +class SegmentTree: + def __init__(self, N, A): + self.n = N + while (self.n & (self.n - 1)) != 0: + self.n += 1 + self.build(N, A) + + def build(self, N, A): + self.tree = [0] * (2 * self.n) + for i in range(N): + self.tree[self.n + i] = A[i] + for i in range(self.n - 1, 0, -1): + self.tree[i] = self.tree[i << 1] + self.tree[i << 1 | 1] + + def update(self, i, val): + self.tree[self.n + i] = val + j = (self.n + i) >> 1 + while j >= 1: + self.tree[j] = self.tree[j << 1] + self.tree[j << 1 | 1] + j >>= 1 + + def query(self, l, r): + res = 0 + l += self.n + r += self.n + 1 + while l < r: + if l & 1: + res += self.tree[l] + l += 1 + if r & 1: + r -= 1 + res += self.tree[r] + l >>= 1 + r >>= 1 + return res + +class NumArray: + + def __init__(self, nums: List[int]): + self.segTree = SegmentTree(len(nums), nums) + + def update(self, index: int, val: int) -> None: + self.segTree.update(index, val) + + def sumRange(self, left: int, right: int) -> int: + return self.segTree.query(left, right) +``` + +```java +class SegmentTree { + int n; + int[] tree; + + SegmentTree(int N, int[] A) { + this.n = N; + while (Integer.bitCount(n) != 1) { + n++; + } + build(N, A); + } + + void build(int N, int[] A) { + tree = new int[2 * n]; + for (int i = 0; i < N; i++) { + tree[n + i] = A[i]; + } + for (int i = n - 1; i > 0; --i) { + tree[i] = tree[i << 1] + tree[i << 1 | 1]; + } + } + + void update(int i, int val) { + tree[n + i] = val; + for (int j = (n + i) >> 1; j >= 1; j >>= 1) { + tree[j] = tree[j << 1] + tree[j << 1 | 1]; + } + } + + int query(int l, int r) { + int res = 0; + for (l += n, r += n + 1; l < r; l >>= 1, r >>= 1) { + if ((l & 1) == 1) res += tree[l++]; + if ((r & 1) == 1) res += tree[--r]; + } + return res; + } +} + +public class NumArray { + private int[] nums; + private SegmentTree segTree; + + public NumArray(int[] nums) { + this.segTree = new SegmentTree(nums.length, nums); + } + + public void update(int index, int val) { + this.segTree.update(index, val); + } + + public int sumRange(int left, int right) { + return this.segTree.query(left, right); + } +} +``` + +```cpp +class SegmentTree { +public: + int n; + vector tree; + + SegmentTree(int N, vector& A) { + this->n = N; + while (__builtin_popcount(n) != 1) { + n++; + } + build(N, A); + } + + void build(int N, vector& A) { + tree.resize(2 * n); + for (int i = 0; i < N; i++) { + tree[n + i] = A[i]; + } + for (int i = n - 1; i > 0; --i) { + tree[i] = tree[i << 1] + tree[i << 1 | 1]; + } + } + + void update(int i, int val) { + tree[n + i] = val; + for (int j = (n + i) >> 1; j >= 1; j >>= 1) { + tree[j] = tree[j << 1] + tree[j << 1 | 1]; + } + } + + int query(int l, int r) { + int res = 0; + for (l += n, r += n + 1; l < r; l >>= 1, r >>= 1) { + if (l & 1) res += tree[l++]; + if (r & 1) res += tree[--r]; + } + return res; + } +}; + +class NumArray { + SegmentTree* segTree; + +public: + NumArray(vector& nums) { + this->segTree = new SegmentTree(nums.size(), nums); + } + + void update(int index, int val) { + this->segTree->update(index, val); + } + + int sumRange(int left, int right) { + return this->segTree->query(left, right); + } +}; +``` + +```javascript +class SegmentTree { + /** + * @constructor + * @param {number} N + * @param {number[]} A + */ + constructor(N, A) { + this.n = N; + while ((this.n & (this.n - 1)) !== 0) { + this.n++; + } + this.build(N, A); + } + + /** + * @param {number} N + * @param {number[]} A + * @return {void} + */ + build(N, A) { + this.tree = new Int32Array(2 * this.n); + for (let i = 0; i < N; i++) { + this.tree[this.n + i] = A[i]; + } + for (let i = this.n - 1; i > 0; i--) { + this.tree[i] = this.tree[i << 1] + this.tree[i << 1 | 1]; + } + } + + /** + * @param {number} i + * @param {number} val + * @return {void} + */ + update(i, val) { + this.tree[this.n + i] = val; + for (let j = (this.n + i) >> 1; j >= 1; j >>= 1) { + this.tree[j] = this.tree[j << 1] + this.tree[j << 1 | 1]; + } + } + + /** + * @param {number} l + * @param {number} r + * @return {number} + */ + query(l, r) { + let res = 0; + l += this.n; + r += this.n + 1; + + while (l < r) { + if (l & 1) res += this.tree[l++]; + if (r & 1) res += this.tree[--r]; + l >>= 1; + r >>= 1; + } + + return res; + } +} + +class NumArray { + /** + * @param {number[]} nums + */ + constructor(nums) { + this.segTree = new SegmentTree(nums.length, nums); + } + + /** + * @param {number} index + * @param {number} val + * @return {void} + */ + update(index, val) { + this.segTree.update(index, val); + } + + /** + * @param {number} left + * @param {number} right + * @return {number} + */ + sumRange(left, right) { + return this.segTree.query(left, right); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: + * $O(n)$ for initializing the input array. + * $O(\log n)$ for each $update()$ function call. + * $O(\log n)$ for each $sumRange()$ function call. +* Space complexity: $O(n)$ + +--- + +## 4. Square Root Decomposition (Update Optimized) + +::tabs-start + +```python +class SqrtDecomposition: + + def __init__(self, nums): + self.A = nums[:] + self.n = len(nums) + self.block_size = int(self.n ** 0.5) + 1 + self.blocks = [0] * self.block_size + + for i in range(self.n): + self.blocks[i // self.block_size] += self.A[i] + + def query(self, left, right): + total_sum = 0 + while left <= right and left % self.block_size != 0: + total_sum += self.A[left] + left += 1 + + while left + self.block_size - 1 <= right: + total_sum += self.blocks[left // self.block_size] + left += self.block_size + + while left <= right: + total_sum += self.A[left] + left += 1 + + return total_sum + + def update(self, index, val): + block_index = index // self.block_size + self.blocks[block_index] += val - self.A[index] + self.A[index] = val + +class NumArray: + + def __init__(self, nums: List[int]): + self.sq = SqrtDecomposition(nums) + + def update(self, index: int, val: int) -> None: + self.sq.update(index, val) + + def sumRange(self, left: int, right: int) -> int: + return self.sq.query(left, right) +``` + +```java +class SqrtDecomposition { + private int[] nums, blocks; + private int blockSize, n; + + public SqrtDecomposition(int[] nums) { + this.nums = nums.clone(); + this.n = nums.length; + this.blockSize = (int) Math.sqrt(n) + 1; + this.blocks = new int[blockSize]; + + for (int i = 0; i < n; i++) { + blocks[i / blockSize] += nums[i]; + } + } + + public int query(int left, int right) { + int totalSum = 0; + while (left <= right && left % blockSize != 0) { + totalSum += nums[left++]; + } + + while (left + blockSize - 1 <= right) { + totalSum += blocks[left / blockSize]; + left += blockSize; + } + + while (left <= right) { + totalSum += nums[left++]; + } + + return totalSum; + } + + public void update(int index, int val) { + int blockIndex = index / blockSize; + blocks[blockIndex] += val - nums[index]; + nums[index] = val; + } +} + +public class NumArray { + private SqrtDecomposition sq; + + public NumArray(int[] nums) { + sq = new SqrtDecomposition(nums); + } + + public void update(int index, int val) { + sq.update(index, val); + } + + public int sumRange(int left, int right) { + return sq.query(left, right); + } +} +``` + +```cpp +class SqrtDecomposition { +private: + vector nums, blocks; + int blockSize, n; + +public: + SqrtDecomposition(vector& nums) { + this->nums = nums; + this->n = nums.size(); + this->blockSize = sqrt(n) + 1; + this->blocks.resize(blockSize, 0); + + for (int i = 0; i < n; i++) { + blocks[i / blockSize] += nums[i]; + } + } + + int query(int left, int right) { + int totalSum = 0; + while (left <= right && left % blockSize != 0) { + totalSum += nums[left++]; + } + + while (left + blockSize - 1 <= right) { + totalSum += blocks[left / blockSize]; + left += blockSize; + } + + while (left <= right) { + totalSum += nums[left++]; + } + + return totalSum; + } + + void update(int index, int val) { + int blockIndex = index / blockSize; + blocks[blockIndex] += val - nums[index]; + nums[index] = val; + } +}; + +class NumArray { +private: + SqrtDecomposition* sq; + +public: + NumArray(vector& nums) { + sq = new SqrtDecomposition(nums); + } + + void update(int index, int val) { + sq->update(index, val); + } + + int sumRange(int left, int right) { + return sq->query(left, right); + } +}; +``` + +```javascript +class SqrtDecomposition { + /** + * @param {number[]} nums + */ + constructor(nums) { + this.nums = [...nums]; + this.n = nums.length; + this.blockSize = Math.floor(Math.sqrt(this.n)) + 1; + this.blocks = new Array(this.blockSize).fill(0); + + for (let i = 0; i < this.n; i++) { + this.blocks[Math.floor(i / this.blockSize)] += nums[i]; + } + } + + /** + * @param {number} left + * @param {number} right + * @return {number} + */ + query(left, right) { + let totalSum = 0; + while (left <= right && left % this.blockSize !== 0) { + totalSum += this.nums[left++]; + } + + while (left + this.blockSize - 1 <= right) { + totalSum += this.blocks[Math.floor(left / this.blockSize)]; + left += this.blockSize; + } + + while (left <= right) { + totalSum += this.nums[left++]; + } + + return totalSum; + } + + /** + * @param {number} index + * @param {number} val + * @return {void} + */ + update(index, val) { + let blockIndex = Math.floor(index / this.blockSize); + this.blocks[blockIndex] += val - this.nums[index]; + this.nums[index] = val; + } +} + +class NumArray { + /** + * @param {number[]} nums + */ + constructor(nums) { + this.sq = new SqrtDecomposition(nums); + } + + /** + * @param {number} index + * @param {number} val + * @return {void} + */ + update(index, val) { + this.sq.update(index, val); + } + + /** + * @param {number} left + * @param {number} right + * @return {number} + */ + sumRange(left, right) { + return this.sq.query(left, right); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: + * $O(n)$ for initializing the input array. + * $O(1)$ for each $update()$ function call. + * $O(\sqrt {n})$ for each $sumRange()$ function call. +* Space complexity: $O(n)$ + +--- + +## 5. Square Root Decomposition (Query Optimized) + +::tabs-start + +```python +class SqrtDecomposition: + + def __init__(self, nums): + self.A = nums[:] + self.prefix_sums = nums[:] + self.n = len(nums) + self.block_size = int(self.n ** 0.5) + 1 + + for i in range(1, self.n): + if i % self.block_size != 0: + self.prefix_sums[i] += self.prefix_sums[i - 1] + + def query(self, left, right): + total_sum = -(self.prefix_sums[left - 1] if left % self.block_size != 0 else 0) + + while left // self.block_size < right // self.block_size: + block_end = min(self.n - 1, (left // self.block_size) * self.block_size + self.block_size - 1) + total_sum += self.prefix_sums[block_end] + left = block_end + 1 + + total_sum += self.prefix_sums[right] + return total_sum + + total_sum += self.prefix_sums[right] + return total_sum + + def update(self, index, val): + diff = val - self.A[index] + self.A[index] = val + + block_end = min(self.n - 1, (index // self.block_size) * self.block_size + self.block_size - 1) + for i in range(index, block_end + 1): + self.prefix_sums[i] += diff + +class NumArray: + + def __init__(self, nums: List[int]): + self.sq = SqrtDecomposition(nums) + + def update(self, index: int, val: int) -> None: + self.sq.update(index, val) + + def sumRange(self, left: int, right: int) -> int: + return self.sq.query(left, right) +``` + +```java +class SqrtDecomposition { + private int[] nums, prefixSums; + private int blockSize, n; + + public SqrtDecomposition(int[] nums) { + this.nums = nums.clone(); + this.n = nums.length; + this.blockSize = (int) Math.sqrt(n) + 1; + this.prefixSums = nums.clone(); + + for (int i = 1; i < n; i++) { + if (i % blockSize != 0) { + prefixSums[i] += prefixSums[i - 1]; + } + } + } + + public int query(int left, int right) { + int totalSum = (left % blockSize != 0) ? -prefixSums[left - 1] : 0; + + while (left / blockSize < right / blockSize) { + int blockEnd = Math.min(n - 1, (left / blockSize) * blockSize + blockSize - 1); + totalSum += prefixSums[blockEnd]; + left = blockEnd + 1; + } + + totalSum += prefixSums[right]; + return totalSum; + } + + public void update(int index, int val) { + int diff = val - nums[index]; + nums[index] = val; + + int blockEnd = Math.min(n - 1, (index / blockSize) * blockSize + blockSize - 1); + for (int i = index; i <= blockEnd; i++) { + prefixSums[i] += diff; + } + } +} + +public class NumArray { + private SqrtDecomposition sq; + + public NumArray(int[] nums) { + sq = new SqrtDecomposition(nums); + } + + public void update(int index, int val) { + sq.update(index, val); + } + + public int sumRange(int left, int right) { + return sq.query(left, right); + } +} +``` + +```cpp +class SqrtDecomposition { +private: + vector nums, prefixSums; + int blockSize, n; + +public: + SqrtDecomposition(vector& nums) { + this->nums = nums; + this->n = nums.size(); + this->blockSize = sqrt(n) + 1; + this->prefixSums = nums; + + for (int i = 1; i < n; i++) { + if (i % blockSize != 0) { + prefixSums[i] += prefixSums[i - 1]; + } + } + } + + int query(int left, int right) { + int totalSum = (left % blockSize != 0) ? -prefixSums[left - 1] : 0; + + while (left / blockSize < right / blockSize) { + int blockEnd = min(n - 1, (left / blockSize) * blockSize + blockSize - 1); + totalSum += prefixSums[blockEnd]; + left = blockEnd + 1; + } + + totalSum += prefixSums[right]; + return totalSum; + } + + void update(int index, int val) { + int diff = val - nums[index]; + nums[index] = val; + + int blockEnd = min(n - 1, (index / blockSize) * blockSize + blockSize - 1); + for (int i = index; i <= blockEnd; i++) { + prefixSums[i] += diff; + } + } +}; + +class NumArray { +private: + SqrtDecomposition* sq; + +public: + NumArray(vector& nums) { + sq = new SqrtDecomposition(nums); + } + + void update(int index, int val) { + sq->update(index, val); + } + + int sumRange(int left, int right) { + return sq->query(left, right); + } +}; +``` + +```javascript +class SqrtDecomposition { + /** + * @param {number[]} nums + */ + constructor(nums) { + this.nums = [...nums]; + this.n = nums.length; + this.blockSize = Math.floor(Math.sqrt(this.n)) + 1; + this.prefixSums = [...nums]; + + for (let i = 1; i < this.n; i++) { + if (i % this.blockSize !== 0) { + this.prefixSums[i] += this.prefixSums[i - 1]; + } + } + } + + /** + * @param {number} left + * @param {number} right + * @return {number} + */ + query(left, right) { + let totalSum = (left % this.blockSize !== 0) ? -this.prefixSums[left - 1] : 0; + + while (Math.floor(left / this.blockSize) < Math.floor(right / this.blockSize)) { + const blockEnd = Math.min(this.n - 1, Math.floor(left / this.blockSize) * this.blockSize + this.blockSize - 1); + totalSum += this.prefixSums[blockEnd]; + left = blockEnd + 1; + } + + totalSum += this.prefixSums[right]; + return totalSum; + } + + /** + * @param {number} index + * @param {number} val + * @return {void} + */ + update(index, val) { + const diff = val - this.nums[index]; + this.nums[index] = val; + + const blockEnd = Math.min(this.n - 1, Math.floor(index / this.blockSize) * this.blockSize + this.blockSize - 1); + for (let i = index; i <= blockEnd; i++) { + this.prefixSums[i] += diff; + } + } +} + +class NumArray { + /** + * @param {number[]} nums + */ + constructor(nums) { + this.sq = new SqrtDecomposition(nums); + } + + /** + * @param {number} index + * @param {number} val + * @return {void} + */ + update(index, val) { + this.sq.update(index, val); + } + + /** + * @param {number} left + * @param {number} right + * @return {number} + */ + sumRange(left, right) { + return this.sq.query(left, right); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: + * $O(n)$ for initializing the input array. + * $O(\sqrt {n})$ for each $update()$ function call. + * $O(\sqrt {n})$ for each $sumRange()$ function call. +* Space complexity: $O(n)$ + +--- + +## 6. Binary Indexed Tree (Fenwick Tree) + +::tabs-start + +```python +class BIT: + def __init__(self, nums): + self.n = len(nums) + 1 + self.nums = [0] * self.n + self.tree = [0] * self.n + for i in range(self.n - 1): + self.update(i, nums[i]) + + def update(self, index, val): + index += 1 + diff = val - self.nums[index] + self.nums[index] = val + while index < self.n: + self.tree[index] += diff + index += index & -index + + def prefix_sum(self, index): + total_sum = 0 + while index > 0: + total_sum += self.tree[index] + index -= index & -index + return total_sum + + def query(self, left, right): + return self.prefix_sum(right + 1) - self.prefix_sum(left) + + +class NumArray: + def __init__(self, nums: List[int]): + self.bit = BIT(nums) + + def update(self, index: int, val: int) -> None: + self.bit.update(index, val) + + def sumRange(self, left: int, right: int) -> int: + return self.bit.query(left, right) +``` + +```java +class BIT { + private int[] nums; + private int[] tree; + private int n; + + public BIT(int[] nums) { + this.n = nums.length + 1; + this.nums = new int[n]; + this.tree = new int[n]; + for (int i = 0; i < n - 1; i++) { + update(i, nums[i]); + } + } + + public void update(int index, int val) { + index++; + int diff = val - nums[index]; + nums[index] = val; + while (index < n) { + tree[index] += diff; + index += index & -index; + } + } + + public int prefixSum(int index) { + int totalSum = 0; + while (index > 0) { + totalSum += tree[index]; + index -= index & -index; + } + return totalSum; + } + + public int query(int left, int right) { + return prefixSum(right + 1) - prefixSum(left); + } +} + +public class NumArray { + private BIT bit; + + public NumArray(int[] nums) { + this.bit = new BIT(nums); + } + + public void update(int index, int val) { + bit.update(index, val); + } + + public int sumRange(int left, int right) { + return bit.query(left, right); + } +} +``` + +```cpp +class BIT { + vector nums; + vector tree; + int n; + +public: + BIT(vector& nums) { + n = nums.size() + 1; + this->nums.resize(n); + tree.resize(n, 0); + for (int i = 0; i < nums.size(); i++) { + update(i, nums[i]); + } + } + + void update(int index, int val) { + index++; + int diff = val - nums[index]; + nums[index] = val; + while (index < n) { + tree[index] += diff; + index += index & -index; + } + } + + int prefixSum(int index) { + int totalSum = 0; + while (index > 0) { + totalSum += tree[index]; + index -= index & -index; + } + return totalSum; + } + + int query(int left, int right) { + return prefixSum(right + 1) - prefixSum(left); + } +}; + +class NumArray { + BIT bit; + +public: + NumArray(vector& nums) : bit(nums) {} + + void update(int index, int val) { + bit.update(index, val); + } + + int sumRange(int left, int right) { + return bit.query(left, right); + } +}; +``` + +```javascript +class BIT { + /** + * @constructor + * @param {number[]} nums + */ + constructor(nums) { + this.n = nums.length + 1; + this.tree = new Int32Array(this.n); + this.nums = new Int32Array(this.n); + for (let i = 0; i < this.n - 1; i++) { + this.update(i, nums[i]); + } + } + + /** + * @param {number} index + * @param {number} val + * @return {void} + */ + update(index, val) { + index++; + const diff = val - this.nums[index]; + this.nums[index] = val; + while (index < this.n) { + this.tree[index] += diff; + index += index & -index; + } + } + + /** + * @param {number} index + * @return {number} + */ + prefixSum(index) { + let totalSum = 0; + while (index > 0) { + totalSum += this.tree[index]; + index -= index & -index; + } + return totalSum; + } + + /** + * @param {number} left + * @param {number} right + * @return {number} + */ + query(left, right) { + return this.prefixSum(right + 1) - this.prefixSum(left); + } +} + +class NumArray { + /** + * @param {number[]} nums + */ + constructor(nums) { + this.bit = new BIT(nums); + } + + /** + * @param {number} index + * @param {number} val + * @return {void} + */ + update(index, val) { + this.bit.update(index, val); + } + + /** + * @param {number} left + * @param {number} right + * @return {number} + */ + sumRange(left, right) { + return this.bit.query(left, right); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: + * $O(n)$ for initializing the input array. + * $O(\log n)$ for each $update()$ function call. + * $O(\log n)$ for each $sumRange()$ function call. +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/sliding-window-maximum.md b/articles/sliding-window-maximum.md index 6a03e1625..c4accb268 100644 --- a/articles/sliding-window-maximum.md +++ b/articles/sliding-window-maximum.md @@ -150,28 +150,19 @@ class Solution { ```python class SegmentTree: - def __init__(self, N, a): + def __init__(self, N, A): self.n = N - self.A = a[:] while (self.n & (self.n - 1)) != 0: - self.A.append(float('-inf')) self.n += 1 - self.tree = [0] * (2 * self.n) - self.build() + self.build(N, A) - def build(self): - for i in range(self.n): - self.tree[self.n + i] = self.A[i] + def build(self, N, A): + self.tree = [float('-inf')] * (2 * self.n) + for i in range(N): + self.tree[self.n + i] = A[i] for i in range(self.n - 1, 0, -1): self.tree[i] = max(self.tree[i << 1], self.tree[i << 1 | 1]) - def update(self, i, val): - self.tree[self.n + i] = val - j = (self.n + i) >> 1 - while j >= 1: - self.tree[j] = max(self.tree[j << 1], self.tree[j << 1 | 1]) - j >>= 1 - def query(self, l, r): res = float('-inf') l += self.n @@ -199,30 +190,22 @@ class Solution: ``` ```java -public class SegmentTree { +class SegmentTree { int n; - int[] A; int[] tree; - final int NEG_INF = Integer.MIN_VALUE; - SegmentTree(int N, int[] a) { + SegmentTree(int N, int[] A) { this.n = N; while (Integer.bitCount(n) != 1) { n++; } - A = new int[n]; - for (int i = 0; i < N; i++) { - A[i] = a[i]; - } - for (int i = N; i < n; i++) { - A[i] = NEG_INF; - } - tree = new int[2 * n]; - build(); + build(N, A); } - void build() { - for (int i = 0; i < n; i++) { + void build(int N, int[] A) { + tree = new int[2 * n]; + Arrays.fill(tree, Integer.MIN_VALUE); + for (int i = 0; i < N; i++) { tree[n + i] = A[i]; } for (int i = n - 1; i > 0; --i) { @@ -230,15 +213,8 @@ public class SegmentTree { } } - void update(int i, int val) { - tree[n + i] = val; - for (int j = (n + i) >> 1; j >= 1; j >>= 1) { - tree[j] = Math.max(tree[j << 1], tree[j << 1 | 1]); - } - } - int query(int l, int r) { - int res = NEG_INF; + int res = Integer.MIN_VALUE; for (l += n, r += n + 1; l < r; l >>= 1, r >>= 1) { if ((l & 1) == 1) res = Math.max(res, tree[l++]); if ((r & 1) == 1) res = Math.max(res, tree[--r]); @@ -261,26 +237,22 @@ public class Solution { ``` ```cpp -class Segment_tree { +class SegmentTree { public: int n; - vector A; vector tree; - const int NEG_INF = -1e9; - Segment_tree(int N, vector& a) { + SegmentTree(int N, vector& A) { this->n = N; - this->A = a; while (__builtin_popcount(n) != 1) { - A.push_back(NEG_INF); n++; } - tree.resize(2 * n); - build(); + build(N, A); } - void build() { - for (int i = 0; i < n; i++) { + void build(int N, vector& A) { + tree.resize(2 * n, INT_MIN); + for (int i = 0; i < N; i++) { tree[n + i] = A[i]; } for (int i = n - 1; i > 0; --i) { @@ -288,15 +260,8 @@ public: } } - void update(int i, int val) { - tree[n + i] = val; - for (int j = (n + i) >> 1; j >= 1; j >>= 1) { - tree[j] = max(tree[j << 1], tree[j << 1 | 1]); - } - } - int query(int l, int r) { - int res = NEG_INF; + int res = INT_MIN; for (l += n, r += n + 1; l < r; l >>= 1, r >>= 1) { if (l & 1) res = max(res, tree[l++]); if (r & 1) res = max(res, tree[--r]); @@ -309,7 +274,7 @@ class Solution { public: vector maxSlidingWindow(vector& nums, int k) { int n = nums.size(); - Segment_tree segTree(n, nums); + SegmentTree segTree(n, nums); vector output; for (int i = 0; i <= n - k; i++) { output.push_back(segTree.query(i, i + k - 1)); @@ -324,49 +289,38 @@ class SegmentTree { /** * @constructor * @param {number} N - * @param {number[]} a + * @param {number[]} A */ - constructor(N, a) { + constructor(N, A) { this.n = N; - this.A = [...a]; - this.NEG_INF = -Infinity; - while ((this.n & (this.n - 1)) !== 0) { - this.A.push(this.NEG_INF); this.n++; } - - this.tree = new Array(2 * this.n).fill(0); - this.build(); + this.build(N, A); } - build() { - for (let i = 0; i < this.n; i++) { - this.tree[this.n + i] = this.A[i]; + /** + * @param {number} N + * @param {number[]} A + * @return {void} + */ + build(N, A) { + this.tree = new Array(2 * this.n).fill(-Infinity); + for (let i = 0; i < N; i++) { + this.tree[this.n + i] = A[i]; } for (let i = this.n - 1; i > 0; i--) { this.tree[i] = Math.max(this.tree[i << 1], this.tree[i << 1 | 1]); } } - /** - * @param {number} i - * @param {number} val - */ - update(i, val) { - this.tree[this.n + i] = val; - for (let j = (this.n + i) >> 1; j >= 1; j >>= 1) { - this.tree[j] = Math.max(this.tree[j << 1], this.tree[j << 1 | 1]); - } - } - /** * @param {number} l * @param {number} r * @return {number} */ query(l, r) { - let res = this.NEG_INF; + let res = -Infinity; l += this.n; r += this.n + 1; @@ -404,28 +358,20 @@ class Solution { ```csharp public class SegmentTree { public int n; - public int[] A; public int[] tree; - public const int NEG_INF = int.MinValue; - public SegmentTree(int N, int[] a) { + public SegmentTree(int N, int[] A) { this.n = N; while (System.Numerics.BitOperations.PopCount((uint)n) != 1) { n++; } - A = new int[n]; - for (int i = 0; i < N; i++) { - A[i] = a[i]; - } - for (int i = N; i < n; i++) { - A[i] = NEG_INF; - } - tree = new int[2 * n]; - Build(); + Build(N, A); } - public void Build() { - for (int i = 0; i < n; i++) { + public void Build(int N, int[] A) { + tree = new int[2 * n]; + Array.Fill(tree, int.MinValue); + for (int i = 0; i < N; i++) { tree[n + i] = A[i]; } for (int i = n - 1; i > 0; --i) { @@ -433,15 +379,8 @@ public class SegmentTree { } } - public void Update(int i, int val) { - tree[n + i] = val; - for (int j = (n + i) >> 1; j >= 1; j >>= 1) { - tree[j] = Math.Max(tree[j << 1], tree[j << 1 | 1]); - } - } - public int Query(int l, int r) { - int res = NEG_INF; + int res = int.MinValue; l += n; r += n + 1; while (l < r) { @@ -473,34 +412,37 @@ type SegmentTree struct { tree []int } -func NewSegmentTree(nums []int) *SegmentTree { - n := len(nums) - A := append([]int{}, nums...) - for (n & (n - 1)) != 0 { - A = append(A, math.MinInt64) +func NewSegmentTree(N int, A []int) *SegmentTree { + n := N + for bits.OnesCount(uint(n)) != 1 { n++ } - tree := make([]int, 2*n) - for i := 0; i < n; i++ { - tree[n+i] = A[i] - } - for i := n - 1; i > 0; i-- { - tree[i] = max(tree[i<<1], tree[i<<1|1]) + + st := &SegmentTree{ + n: n, } - return &SegmentTree{n, tree} + st.build(N, A) + return st } -func (st *SegmentTree) Update(i, value int) { - st.tree[st.n+i] = value - for j := (st.n + i) >> 1; j >= 1; j >>= 1 { - st.tree[j] = max(st.tree[j<<1], st.tree[j<<1|1]) +func (st *SegmentTree) build(N int, A []int) { + st.tree = make([]int, 2*st.n) + for i := range st.tree { + st.tree[i] = math.MinInt + } + for i := 0; i < N; i++ { + st.tree[st.n+i] = A[i] + } + for i := st.n - 1; i > 0; i-- { + st.tree[i] = max(st.tree[i<<1], st.tree[i<<1|1]) } } func (st *SegmentTree) Query(l, r int) int { - res := math.MinInt64 + res := math.MinInt l += st.n r += st.n + 1 + for l < r { if l&1 == 1 { res = max(res, st.tree[l]) @@ -516,72 +458,82 @@ func (st *SegmentTree) Query(l, r int) int { return res } +func max(a, b int) int { + if a > b { + return a + } + return b +} + func maxSlidingWindow(nums []int, k int) []int { n := len(nums) - segTree := NewSegmentTree(nums) + segTree := NewSegmentTree(n, nums) output := make([]int, n-k+1) + for i := 0; i <= n-k; i++ { output[i] = segTree.Query(i, i+k-1) } + return output } - -func max(a, b int) int { - if a > b { - return a - } - return b -} ``` ```kotlin -class SegmentTree(A: IntArray) { - private val n: Int - private val tree: IntArray - - init { - var size = A.size - var array = A - if (size and (size - 1) != 0) { - val newSize = Integer.highestOneBit(size) shl 1 - array = A.copyOf(newSize) - for (i in size until newSize) { - array[i] = Int.MIN_VALUE - } - size = newSize - } - n = size - tree = IntArray(2 * n) - build(array) - } - - private fun build(array: IntArray) { - System.arraycopy(array, 0, tree, n, n) - for (i in n - 1 downTo 1) { - tree[i] = maxOf(tree[i shl 1], tree[i shl 1 or 1]) - } - } - - fun query(l: Int, r: Int): Int { - var res = Int.MIN_VALUE - var left = l + n - var right = r + n + 1 - while (left < right) { - if (left and 1 == 1) res = maxOf(res, tree[left++]) - if (right and 1 == 1) res = maxOf(res, tree[--right]) - left = left shr 1 - right = right shr 1 - } - return res - } +class SegmentTree(N: Int, A: IntArray) { + private var n: Int = N + private var tree: IntArray = IntArray(0) + + init { + var size = N + while (Integer.bitCount(size) != 1) { + size++ + } + n = size + build(N, A) + } + + private fun build(N: Int, A: IntArray) { + tree = IntArray(2 * n) + tree.fill(Int.MIN_VALUE) + for (i in 0 until N) { + tree[n + i] = A[i] + } + for (i in n - 1 downTo 1) { + tree[i] = maxOf(tree[i * 2], tree[i * 2 + 1]) + } + } + + fun query(l: Int, r: Int): Int { + var res = Int.MIN_VALUE + var left = l + n + var right = r + n + 1 + + while (left < right) { + if (left % 2 == 1) { + res = maxOf(res, tree[left]) + left++ + } + if (right % 2 == 1) { + right-- + res = maxOf(res, tree[right]) + } + left /= 2 + right /= 2 + } + return res + } } class Solution { - fun maxSlidingWindow(nums: IntArray, k: Int): IntArray { - val n = nums.size - val segTree = SegmentTree(nums) - return IntArray(n - k + 1) { i -> segTree.query(i, i + k - 1) } - } + fun maxSlidingWindow(nums: IntArray, k: Int): IntArray { + val n = nums.size + val segTree = SegmentTree(n, nums) + val result = IntArray(n - k + 1) + for (i in 0..n - k) { + result[i] = segTree.query(i, i + k - 1) + } + return result + } } ``` diff --git a/articles/triangle.md b/articles/triangle.md new file mode 100644 index 000000000..53b994a33 --- /dev/null +++ b/articles/triangle.md @@ -0,0 +1,535 @@ +## 1. Recursion + +::tabs-start + +```python +class Solution: + def minimumTotal(self, triangle: List[List[int]]) -> int: + def dfs(row, col): + if row >= len(triangle): + return 0 + return triangle[row][col] + min(dfs(row + 1, col), dfs(row + 1, col + 1)) + + return dfs(0, 0) +``` + +```java +public class Solution { + public int minimumTotal(List> triangle) { + return dfs(0, 0, triangle); + } + + private int dfs(int row, int col, List> triangle) { + if (row >= triangle.size()) { + return 0; + } + return triangle.get(row).get(col) + Math.min(dfs(row + 1, col, triangle), dfs(row + 1, col + 1, triangle)); + } +} +``` + +```cpp +class Solution { +public: + int minimumTotal(vector>& triangle) { + return dfs(0, 0, triangle); + } + +private: + int dfs(int row, int col, vector>& triangle) { + if (row >= triangle.size()) { + return 0; + } + return triangle[row][col] + min(dfs(row + 1, col, triangle), dfs(row + 1, col + 1, triangle)); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} triangle + * @return {number} + */ + minimumTotal(triangle) { + const dfs = (row, col) => { + if (row >= triangle.length) { + return 0; + } + return triangle[row][col] + Math.min(dfs(row + 1, col), dfs(row + 1, col + 1)); + }; + + return dfs(0, 0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(2 ^ n)$ +* Space complexity: $O(n)$ for recursion stack. + +--- + +## 2. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def minimumTotal(self, triangle: List[List[int]]) -> int: + memo = [[0] * len(triangle[r]) for r in range(len(triangle))] + INF = float("inf") + for r in range(len(triangle)): + for c in range(len(triangle[r])): + memo[r][c] = INF + + def dfs(row, col): + if row >= len(triangle): + return 0 + if memo[row][col] != INF: + return memo[row][col] + + memo[row][col] = triangle[row][col] + min(dfs(row + 1, col), dfs(row + 1, col + 1)) + return memo[row][col] + + return dfs(0, 0) +``` + +```java +public class Solution { + public int minimumTotal(List> triangle) { + int[][] memo = new int[triangle.size()][]; + int INF = Integer.MAX_VALUE; + for (int r = 0; r < triangle.size(); r++) { + memo[r] = new int[triangle.get(r).size()]; + Arrays.fill(memo[r], INF); + } + + return dfs(0, 0, triangle, memo); + } + + private int dfs(int row, int col, List> triangle, int[][] memo) { + if (row >= triangle.size()) { + return 0; + } + if (memo[row][col] != Integer.MAX_VALUE) { + return memo[row][col]; + } + + memo[row][col] = triangle.get(row).get(col) + Math.min(dfs(row + 1, col, triangle, memo), dfs(row + 1, col + 1, triangle, memo)); + return memo[row][col]; + } +} +``` + +```cpp +class Solution { +public: + int minimumTotal(vector>& triangle) { + vector> memo(triangle.size(), vector(0)); + int INF = INT_MAX; + for (int r = 0; r < triangle.size(); ++r) { + memo[r].resize(triangle[r].size(), INF); + } + + return dfs(0, 0, triangle, memo); + } + +private: + int dfs(int row, int col, vector>& triangle, vector>& memo) { + if (row >= triangle.size()) { + return 0; + } + if (memo[row][col] != INT_MAX) { + return memo[row][col]; + } + + memo[row][col] = triangle[row][col] + min(dfs(row + 1, col, triangle, memo), dfs(row + 1, col + 1, triangle, memo)); + return memo[row][col]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} triangle + * @return {number} + */ + minimumTotal(triangle) { + const memo = Array.from({ length: triangle.length }, (_, r) => Array(triangle[r].length).fill(Infinity)); + + const dfs = (row, col) => { + if (row >= triangle.length) { + return 0; + } + if (memo[row][col] !== Infinity) { + return memo[row][col]; + } + + memo[row][col] = triangle[row][col] + Math.min(dfs(row + 1, col), dfs(row + 1, col + 1)); + return memo[row][col]; + }; + + return dfs(0, 0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n ^ 2)$ + +--- + +## 3. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def minimumTotal(self, triangle: List[List[int]]) -> int: + n = len(triangle) + dp = [[0] * len(triangle[row]) for row in range(n)] + dp[-1] = triangle[-1][:] + + for row in range(n - 2, -1, -1): + for col in range(len(triangle[row])): + dp[row][col] = triangle[row][col] + min(dp[row + 1][col], dp[row + 1][col + 1]) + + return dp[0][0] +``` + +```java +public class Solution { + public int minimumTotal(List> triangle) { + int n = triangle.size(); + int[][] dp = new int[n][n]; + for (int col = 0; col < triangle.get(n - 1).size(); col++) { + dp[n - 1][col] = triangle.get(n - 1).get(col); + } + + for (int row = n - 2; row >= 0; row--) { + for (int col = 0; col < triangle.get(row).size(); col++) { + dp[row][col] = triangle.get(row).get(col) + Math.min(dp[row + 1][col], dp[row + 1][col + 1]); + } + } + + return dp[0][0]; + } +} +``` + +```cpp +class Solution { +public: + int minimumTotal(vector>& triangle) { + int n = triangle.size(); + vector> dp(n, vector(n, 0)); + for (int col = 0; col < triangle[n - 1].size(); ++col) { + dp[n - 1][col] = triangle[n - 1][col]; + } + + for (int row = n - 2; row >= 0; --row) { + for (int col = 0; col < triangle[row].size(); ++col) { + dp[row][col] = triangle[row][col] + min(dp[row + 1][col], dp[row + 1][col + 1]); + } + } + + return dp[0][0]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} triangle + * @return {number} + */ + minimumTotal(triangle) { + const n = triangle.length; + const dp = Array.from({ length: n }, (_, i) => Array(triangle[i].length).fill(0)); + for (let col = 0; col < triangle[n - 1].length; col++) { + dp[n - 1][col] = triangle[n - 1][col]; + } + + for (let row = n - 2; row >= 0; row--) { + for (let col = 0; col < triangle[row].length; col++) { + dp[row][col] = triangle[row][col] + Math.min(dp[row + 1][col], dp[row + 1][col + 1]); + } + } + + return dp[0][0]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n ^ 2)$ + +--- + +## 4. Dynamic Programming (Space Optimized) - I + +::tabs-start + +```python +class Solution: + def minimumTotal(self, triangle: List[List[int]]) -> int: + n = len(triangle) + dp = triangle[0][:] + + for row in range(1, n): + nxtDp = [0] * len(triangle[row]) + nxtDp[0] = dp[0] + triangle[row][0] + for col in range(1, len(triangle[row]) - 1): + nxtDp[col] = triangle[row][col] + min(dp[col], dp[col - 1]) + nxtDp[-1] = dp[-1] + triangle[row][-1] + dp = nxtDp + + return min(dp) +``` + +```java +public class Solution { + public int minimumTotal(List> triangle) { + int n = triangle.size(); + int[] dp = new int[n]; + dp[0] = triangle.get(0).get(0); + + for (int row = 1; row < n; row++) { + int[] nxtDp = new int[row + 1]; + nxtDp[0] = dp[0] + triangle.get(row).get(0); + for (int col = 1; col < row; col++) { + nxtDp[col] = triangle.get(row).get(col) + Math.min(dp[col], dp[col - 1]); + } + nxtDp[row] = dp[row - 1] + triangle.get(row).get(row); + dp = nxtDp; + } + + int minPath = Integer.MAX_VALUE; + for (int value : dp) { + minPath = Math.min(minPath, value); + } + return minPath; + } +} +``` + +```cpp +class Solution { +public: + int minimumTotal(vector>& triangle) { + int n = triangle.size(); + vector dp = triangle[0]; + + for (int row = 1; row < n; row++) { + vector nxtDp(row + 1, 0); + nxtDp[0] = dp[0] + triangle[row][0]; + for (int col = 1; col < row; col++) { + nxtDp[col] = triangle[row][col] + min(dp[col], dp[col - 1]); + } + nxtDp[row] = dp[row - 1] + triangle[row][row]; + dp = nxtDp; + } + + return *min_element(dp.begin(), dp.end()); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} triangle + * @return {number} + */ + minimumTotal(triangle) { + let n = triangle.length; + let dp = [...triangle[0]]; + + for (let row = 1; row < n; row++) { + let nxtDp = new Array(row + 1).fill(0); + nxtDp[0] = dp[0] + triangle[row][0]; + for (let col = 1; col < row; col++) { + nxtDp[col] = triangle[row][col] + Math.min(dp[col], dp[col - 1]); + } + nxtDp[row] = dp[row - 1] + triangle[row][row]; + dp = nxtDp; + } + + return Math.min(...dp); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n)$ extra space. + +--- + +## 5. Dynamic Programming (Space Optimized) - II + +::tabs-start + +```python +class Solution: + def minimumTotal(self, triangle: List[List[int]]) -> int: + n = len(triangle) + dp = triangle[-1][:] + + for row in range(n - 2, -1, -1): + for col in range(len(triangle[row])): + dp[col] = triangle[row][col] + min(dp[col], dp[col + 1]) + + return dp[0] +``` + +```java +public class Solution { + public int minimumTotal(List> triangle) { + int n = triangle.size(); + int[] dp = new int[n]; + for (int i = 0; i < n; i++) { + dp[i] = triangle.get(n - 1).get(i); + } + + for (int row = n - 2; row >= 0; row--) { + for (int col = 0; col < triangle.get(row).size(); col++) { + dp[col] = triangle.get(row).get(col) + Math.min(dp[col], dp[col + 1]); + } + } + + return dp[0]; + } +} +``` + +```cpp +class Solution { +public: + int minimumTotal(vector>& triangle) { + int n = triangle.size(); + vector dp(triangle.back()); + + for (int row = n - 2; row >= 0; --row) { + for (int col = 0; col < triangle[row].size(); ++col) { + dp[col] = triangle[row][col] + min(dp[col], dp[col + 1]); + } + } + + return dp[0]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} triangle + * @return {number} + */ + minimumTotal(triangle) { + const n = triangle.length; + const dp = [...triangle[n - 1]]; + + for (let row = n - 2; row >= 0; row--) { + for (let col = 0; col < triangle[row].length; col++) { + dp[col] = triangle[row][col] + Math.min(dp[col], dp[col + 1]); + } + } + + return dp[0]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n)$ extra space. + +--- + +## 6. Dynamic Programming (In-Place) + +::tabs-start + +```python +class Solution: + def minimumTotal(self, triangle: List[List[int]]) -> int: + for row in range(len(triangle) - 2, -1, -1): + for col in range(len(triangle[row])): + triangle[row][col] += min(triangle[row + 1][col], triangle[row + 1][col + 1]) + + return triangle[0][0] +``` + +```java +public class Solution { + public int minimumTotal(List> triangle) { + for (int row = triangle.size() - 2; row >= 0; row--) { + for (int col = 0; col < triangle.get(row).size(); col++) { + triangle.get(row).set(col, triangle.get(row).get(col) + + Math.min(triangle.get(row + 1).get(col), triangle.get(row + 1).get(col + 1))); + } + } + return triangle.get(0).get(0); + } +} +``` + +```cpp +class Solution { +public: + int minimumTotal(vector>& triangle) { + for (int row = triangle.size() - 2; row >= 0; row--) { + for (int col = 0; col < triangle[row].size(); col++) { + triangle[row][col] += min(triangle[row + 1][col], triangle[row + 1][col + 1]); + } + } + return triangle[0][0]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} triangle + * @return {number} + */ + minimumTotal(triangle) { + for (let row = triangle.length - 2; row >= 0; row--) { + for (let col = 0; col < triangle[row].length; col++) { + triangle[row][col] += Math.min(triangle[row + 1][col], triangle[row + 1][col + 1]); + } + } + return triangle[0][0]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(1)$ extra space. \ No newline at end of file From 232799235f32e3c03730ce7f0e6a05d9982d7e6b Mon Sep 17 00:00:00 2001 From: Sri Hari <94112314+Srihari2222@users.noreply.github.com> Date: Wed, 25 Dec 2024 02:13:34 +0530 Subject: [PATCH 20/45] Sri Hari: Batch-4/Neetcode-All/Added-articles (#3773) * Batch-4/Neetcode-All/Added-articles * Batch-4/Neetcode-All/Added-articles * Batch-4/Neetcode-All/Added-articles * Batch-4/Neetcode-All/Added-articles --- articles/best-team-with-no-conflicts.md | 649 ++++++++++++++++ articles/count-ways-to-build-good-strings.md | 283 +++++++ articles/integer-break.md | 661 +++++++++++++++++ articles/last-stone-weight-ii.md | 619 ++++++++++++++++ articles/longest-palindromic-subsequence.md | 557 ++++++++++++++ articles/longest-string-chain.md | 435 +++++++++++ articles/maximal-square.md | 560 ++++++++++++++ .../maximum-alternating-subsequence-sum.md | 351 +++++++++ articles/minimum-path-sum.md | 395 ++++++++++ articles/n-th-tribonacci-number.md | 301 ++++++++ articles/new-21-game.md | 485 ++++++++++++ ...umber-of-longest-increasing-subsequence.md | 699 ++++++++++++++++++ articles/ones-and-zeroes.md | 531 +++++++++++++ articles/solving-questions-with-brainpower.md | 233 ++++++ articles/stickers-to-spell-word.md | 580 +++++++++++++++ articles/stone-game.md | 525 +++++++++++++ articles/uncrossed-lines.md | 560 ++++++++++++++ articles/unique-paths-ii.md | 481 ++++++++++++ 18 files changed, 8905 insertions(+) create mode 100644 articles/best-team-with-no-conflicts.md create mode 100644 articles/count-ways-to-build-good-strings.md create mode 100644 articles/integer-break.md create mode 100644 articles/last-stone-weight-ii.md create mode 100644 articles/longest-palindromic-subsequence.md create mode 100644 articles/longest-string-chain.md create mode 100644 articles/maximal-square.md create mode 100644 articles/maximum-alternating-subsequence-sum.md create mode 100644 articles/minimum-path-sum.md create mode 100644 articles/n-th-tribonacci-number.md create mode 100644 articles/new-21-game.md create mode 100644 articles/number-of-longest-increasing-subsequence.md create mode 100644 articles/ones-and-zeroes.md create mode 100644 articles/solving-questions-with-brainpower.md create mode 100644 articles/stickers-to-spell-word.md create mode 100644 articles/stone-game.md create mode 100644 articles/uncrossed-lines.md create mode 100644 articles/unique-paths-ii.md diff --git a/articles/best-team-with-no-conflicts.md b/articles/best-team-with-no-conflicts.md new file mode 100644 index 000000000..a4e77a374 --- /dev/null +++ b/articles/best-team-with-no-conflicts.md @@ -0,0 +1,649 @@ +## 1. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def bestTeamScore(self, scores: List[int], ages: List[int]) -> int: + pairs = [[scores[i], ages[i]] for i in range(len(scores))] + pairs.sort() + dp = {} + + def dfs(i, j): + if i == len(pairs): + return 0 + if (i, j) in dp: + return dp[(i, j)] + + mScore, mAge = pairs[j] if j >= 0 else [0, 0] + score, age = pairs[i] + res = 0 + if not (score > mScore and age < mAge): + res = dfs(i + 1, i) + score # add score + dp[(i, j)] = max(res, dfs(i + 1, j)) # skip score + return dp[(i, j)] + + return dfs(0, -1) +``` + +```java +public class Solution { + private int[][] pairs; + private int[][] dp; + + public int bestTeamScore(int[] scores, int[] ages) { + int n = scores.length; + pairs = new int[n][2]; + for (int i = 0; i < n; i++) { + pairs[i][0] = scores[i]; + pairs[i][1] = ages[i]; + } + Arrays.sort(pairs, (a, b) -> a[0] == b[0] ? Integer.compare(a[1], b[1]) : Integer.compare(a[0], b[0])); + + dp = new int[n][n + 1]; + for (int[] row : dp) { + Arrays.fill(row, -1); + } + + return dfs(0, -1); + } + + private int dfs(int i, int j) { + if (i == pairs.length) { + return 0; + } + if (dp[i][j + 1] != -1) { + return dp[i][j + 1]; + } + + int mScore = j >= 0 ? pairs[j][0] : 0; + int mAge = j >= 0 ? pairs[j][1] : 0; + int score = pairs[i][0]; + int age = pairs[i][1]; + + int res = 0; + if (!(score > mScore && age < mAge)) { + res = dfs(i + 1, i) + score; + } + dp[i][j + 1] = Math.max(res, dfs(i + 1, j)); + return dp[i][j + 1]; + } +} +``` + +```cpp +class Solution { +private: + vector> pairs; + vector> dp; + +public: + int bestTeamScore(vector& scores, vector& ages) { + int n = scores.size(); + pairs.resize(n); + for (int i = 0; i < n; i++) { + pairs[i] = {scores[i], ages[i]}; + } + sort(pairs.begin(), pairs.end()); + + dp = vector>(n, vector(n + 1, -1)); + return dfs(0, -1); + } + +private: + int dfs(int i, int j) { + if (i == pairs.size()) { + return 0; + } + if (dp[i][j + 1] != -1) { + return dp[i][j + 1]; + } + + int mScore = j >= 0 ? pairs[j].first : 0; + int mAge = j >= 0 ? pairs[j].second : 0; + int score = pairs[i].first; + int age = pairs[i].second; + + int res = 0; + if (!(score > mScore && age < mAge)) { + res = dfs(i + 1, i) + score; + } + dp[i][j + 1] = max(res, dfs(i + 1, j)); + return dp[i][j + 1]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} scores + * @param {number[]} ages + * @return {number} + */ + bestTeamScore(scores, ages) { + const n = scores.length; + const pairs = []; + for (let i = 0; i < n; i++) { + pairs.push([scores[i], ages[i]]); + } + pairs.sort((a, b) => (a[0] === b[0] ? a[1] - b[1] : a[0] - b[0])); + + const dp = Array.from({ length: n }, () => new Array(n + 1).fill(-1)); + + const dfs = (i, j) => { + if (i === n) { + return 0; + } + if (dp[i][j + 1] !== -1) { + return dp[i][j + 1]; + } + + const [mScore, mAge] = j >= 0 ? pairs[j] : [0, 0]; + const [score, age] = pairs[i]; + + let res = 0; + if (!(score > mScore && age < mAge)) { + res = dfs(i + 1, i) + score; + } + dp[i][j + 1] = Math.max(res, dfs(i + 1, j)); + return dp[i][j + 1]; + }; + + return dfs(0, -1); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n ^ 2)$ + +--- + +## 2. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def bestTeamScore(self, scores: List[int], ages: List[int]) -> int: + pairs = [[scores[i], ages[i]] for i in range(len(scores))] + pairs.sort() + dp = [pairs[i][0] for i in range(len(pairs))] + + for i in range(len(pairs)): + mScore, mAge = pairs[i] + for j in range(i): + score, age = pairs[j] + if mAge >= age: + dp[i] = max(dp[i], mScore + dp[j]) + + return max(dp) +``` + +```java +public class Solution { + public int bestTeamScore(int[] scores, int[] ages) { + int n = scores.length; + int[][] pairs = new int[n][2]; + for (int i = 0; i < n; i++) { + pairs[i][0] = scores[i]; + pairs[i][1] = ages[i]; + } + Arrays.sort(pairs, (a, b) -> a[0] == b[0] ? Integer.compare(a[1], b[1]) : Integer.compare(a[0], b[0])); + + int[] dp = new int[n]; + for (int i = 0; i < n; i++) { + dp[i] = pairs[i][0]; + } + + for (int i = 0; i < n; i++) { + int mScore = pairs[i][0], mAge = pairs[i][1]; + for (int j = 0; j < i; j++) { + int score = pairs[j][0], age = pairs[j][1]; + if (mAge >= age) { + dp[i] = Math.max(dp[i], mScore + dp[j]); + } + } + } + + int maxScore = 0; + for (int score : dp) { + maxScore = Math.max(maxScore, score); + } + return maxScore; + } +} +``` + +```cpp +class Solution { +public: + int bestTeamScore(vector& scores, vector& ages) { + int n = scores.size(); + vector> pairs(n); + for (int i = 0; i < n; i++) { + pairs[i] = {scores[i], ages[i]}; + } + sort(pairs.begin(), pairs.end()); + + vector dp(n); + for (int i = 0; i < n; i++) { + dp[i] = pairs[i].first; + } + + for (int i = 0; i < n; i++) { + int mScore = pairs[i].first, mAge = pairs[i].second; + for (int j = 0; j < i; j++) { + int score = pairs[j].first, age = pairs[j].second; + if (mAge >= age) { + dp[i] = max(dp[i], mScore + dp[j]); + } + } + } + + return *max_element(dp.begin(), dp.end()); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} scores + * @param {number[]} ages + * @return {number} + */ + bestTeamScore(scores, ages) { + const n = scores.length; + const pairs = []; + for (let i = 0; i < n; i++) { + pairs.push([scores[i], ages[i]]); + } + pairs.sort((a, b) => (a[0] === b[0] ? a[1] - b[1] : a[0] - b[0])); + + const dp = new Array(n).fill(0); + for (let i = 0; i < n; i++) { + dp[i] = pairs[i][0]; + } + + for (let i = 0; i < n; i++) { + const [mScore, mAge] = pairs[i]; + for (let j = 0; j < i; j++) { + const [score, age] = pairs[j]; + if (mAge >= age) { + dp[i] = Math.max(dp[i], mScore + dp[j]); + } + } + } + + return Math.max(...dp); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n)$ + +--- + +## 3. Dynamic Programming (Segment Tree) + +::tabs-start + +```python +class SegmentTree: + def __init__(self, N): + self.n = N + while (self.n & (self.n - 1)) != 0: + self.n += 1 + self.build() + + def build(self): + self.tree = [0] * (2 * self.n) + + def update(self, i, val): + pos = self.n + i + self.tree[pos] = max(self.tree[pos], val) + pos >>= 1 + while pos >= 1: + self.tree[pos] = max(self.tree[pos << 1], self.tree[pos << 1 | 1]) + pos >>= 1 + + def query(self, l, r): + res = 0 + l += self.n + r += self.n + 1 + while l < r: + if l & 1: + res = max(res, self.tree[l]) + l += 1 + if r & 1: + r -= 1 + res = max(res, self.tree[r]) + l >>= 1 + r >>= 1 + return res + +class Solution: + def bestTeamScore(self, scores: list[int], ages: list[int]) -> int: + pairs = [[scores[i], ages[i]] for i in range(len(scores))] + pairs.sort() + + dp = [pairs[i][0] for i in range(len(pairs))] + unique_ages = sorted({ age for _, age in pairs }) + ageId = { val: idx for idx, val in enumerate(unique_ages) } + + segtree = SegmentTree(len(pairs)) + + res = 0 + for i in range(len(pairs)): + mScore, mAge = pairs[i] + idx = ageId[mAge] + j = segtree.query(0, idx) + dp[i] = j + mScore + segtree.update(idx, dp[i]) + res = max(res, dp[i]) + + return res +``` + +```java +class SegmentTree { + private int n; + private int[] tree; + + public SegmentTree(int N) { + n = N; + while ((n & (n - 1)) != 0) { + n++; + } + build(); + } + + private void build() { + tree = new int[2 * n]; + } + + public void update(int i, int val) { + int pos = n + i; + tree[pos] = Math.max(tree[pos], val); + pos >>= 1; + while (pos >= 1) { + tree[pos] = Math.max(tree[pos << 1], tree[pos << 1 | 1]); + pos >>= 1; + } + } + + public int query(int l, int r) { + int res = 0; + l += n; + r += n + 1; + while (l < r) { + if ((l & 1) == 1) { + res = Math.max(res, tree[l]); + l++; + } + if ((r & 1) == 1) { + r--; + res = Math.max(res, tree[r]); + } + l >>= 1; + r >>= 1; + } + return res; + } +} + +public class Solution { + public int bestTeamScore(int[] scores, int[] ages) { + int n = scores.length; + int[][] pairs = new int[n][2]; + for (int i = 0; i < n; i++) { + pairs[i][0] = scores[i]; + pairs[i][1] = ages[i]; + } + Arrays.sort(pairs, (a, b) -> a[0] == b[0] ? Integer.compare(a[1], b[1]) : Integer.compare(a[0], b[0])); + + int[] dp = new int[n]; + for (int i = 0; i < n; i++) { + dp[i] = pairs[i][0]; + } + + Set uniqueAgesSet = new TreeSet<>(); + for (int[] pair : pairs) { + uniqueAgesSet.add(pair[1]); + } + List uniqueAges = new ArrayList<>(uniqueAgesSet); + Map ageId = new HashMap<>(); + for (int i = 0; i < uniqueAges.size(); i++) { + ageId.put(uniqueAges.get(i), i); + } + + SegmentTree segtree = new SegmentTree(uniqueAges.size()); + + int res = 0; + for (int i = 0; i < n; i++) { + int mScore = pairs[i][0]; + int mAge = pairs[i][1]; + int idx = ageId.get(mAge); + int j = segtree.query(0, idx); + dp[i] = j + mScore; + segtree.update(idx, dp[i]); + res = Math.max(res, dp[i]); + } + + return res; + } +} +``` + +```cpp +class SegmentTree { +private: + int n; + vector tree; + +public: + SegmentTree(int N) { + n = N; + while ((n & (n - 1)) != 0) { + n++; + } + build(); + } + + void build() { + tree.resize(2 * n, 0); + } + + void update(int i, int val) { + int pos = n + i; + tree[pos] = max(tree[pos], val); + pos >>= 1; + while (pos >= 1) { + tree[pos] = max(tree[pos << 1], tree[pos << 1 | 1]); + pos >>= 1; + } + } + + int query(int l, int r) { + int res = 0; + l += n; + r += n + 1; + while (l < r) { + if (l & 1) { + res = max(res, tree[l]); + l++; + } + if (r & 1) { + r--; + res = max(res, tree[r]); + } + l >>= 1; + r >>= 1; + } + return res; + } +}; + +class Solution { +public: + int bestTeamScore(vector& scores, vector& ages) { + int n = scores.size(); + vector> pairs(n); + for (int i = 0; i < n; i++) { + pairs[i] = {scores[i], ages[i]}; + } + sort(pairs.begin(), pairs.end()); + + vector dp(n); + for (int i = 0; i < n; i++) { + dp[i] = pairs[i].first; + } + + set uniqueAgesSet; + for (auto& pair : pairs) { + uniqueAgesSet.insert(pair.second); + } + vector uniqueAges(uniqueAgesSet.begin(), uniqueAgesSet.end()); + map ageId; + for (int i = 0; i < uniqueAges.size(); i++) { + ageId[uniqueAges[i]] = i; + } + + SegmentTree segtree(uniqueAges.size()); + + int res = 0; + for (int i = 0; i < n; i++) { + int mScore = pairs[i].first; + int mAge = pairs[i].second; + int idx = ageId[mAge]; + int j = segtree.query(0, idx); + dp[i] = j + mScore; + segtree.update(idx, dp[i]); + res = max(res, dp[i]); + } + + return res; + } +}; +``` + +```javascript +class SegmentTree { + /** + * @constructor + * @param {number} N + */ + constructor(N) { + this.n = N; + while ((this.n & (this.n - 1)) !== 0) { + this.n++; + } + this.build(); + } + + /** + * @return {void} + */ + build() { + this.tree = new Array(2 * this.n).fill(0); + } + + /** + * @param {number} i + * @param {number} val + * @return {void} + */ + update(i, val) { + let pos = this.n + i; + this.tree[pos] = Math.max(this.tree[pos], val); + pos >>= 1; + while (pos >= 1) { + this.tree[pos] = Math.max(this.tree[pos << 1], this.tree[pos << 1 | 1]); + pos >>= 1; + } + } + + /** + * @param {number} l + * @param {number} r + * @return {number} + */ + query(l, r) { + let res = 0; + l += this.n; + r += this.n + 1; + while (l < r) { + if (l & 1) { + res = Math.max(res, this.tree[l]); + l++; + } + if (r & 1) { + r--; + res = Math.max(res, this.tree[r]); + } + l >>= 1; + r >>= 1; + } + return res; + } +} + +class Solution { + /** + * @param {number[]} scores + * @param {number[]} ages + * @return {number} + */ + bestTeamScore(scores, ages) { + const n = scores.length; + const pairs = []; + + for (let i = 0; i < n; i++) { + pairs.push([scores[i], ages[i]]); + } + pairs.sort((a, b) => (a[0] === b[0] ? a[1] - b[1] : a[0] - b[0])); + + const dp = new Array(n).fill(0); + for (let i = 0; i < n; i++) { + dp[i] = pairs[i][0]; + } + + const uniqueAges = [...new Set(pairs.map((p) => p[1]))].sort((a, b) => a - b); + const ageId = new Map(); + uniqueAges.forEach((val, idx) => ageId.set(val, idx)); + + const segtree = new SegmentTree(uniqueAges.length); + + let res = 0; + + for (let i = 0; i < n; i++) { + const [mScore, mAge] = pairs[i]; + const idx = ageId.get(mAge); + const j = segtree.query(0, idx); + dp[i] = j + mScore; + segtree.update(idx, dp[i]); + res = Math.max(res, dp[i]); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/count-ways-to-build-good-strings.md b/articles/count-ways-to-build-good-strings.md new file mode 100644 index 000000000..d1808c1e9 --- /dev/null +++ b/articles/count-ways-to-build-good-strings.md @@ -0,0 +1,283 @@ +## 1. Recursion + +::tabs-start + +```python +class Solution: + def countGoodStrings(self, low: int, high: int, zero: int, one: int) -> int: + mod = 10**9 + 7 + + def dfs(length): + if length > high: + return 0 + res = 1 if length >= low else 0 + res += dfs(length + zero) + dfs(length + one) + return res % mod + + return dfs(0) +``` + +```java +public class Solution { + final int mod = 1_000_000_007; + + public int countGoodStrings(int low, int high, int zero, int one) { + return dfs(low, high, zero, one, 0); + } + + private int dfs(int low, int high, int zero, int one, int length) { + if (length > high) return 0; + int res = (length >= low) ? 1 : 0; + res = (res + dfs(low, high, zero, one, length + zero)) % mod; + res = (res + dfs(low, high, zero, one, length + one)) % mod; + return res; + } +} +``` + +```cpp +class Solution { +public: + int countGoodStrings(int low, int high, int zero, int one) { + const int mod = 1e9 + 7; + + function dfs = [&](int length) { + if (length > high) return 0; + int res = (length >= low) ? 1 : 0; + res = (res + dfs(length + zero)) % mod; + res = (res + dfs(length + one)) % mod; + return res; + }; + + return dfs(0); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} low + * @param {number} high + * @param {number} zero + * @param {number} one + * @return {number} + */ + countGoodStrings(low, high, zero, one) { + const mod = 1e9 + 7; + + const dfs = length => { + if (length > high) return 0; + let res = length >= low ? 1 : 0; + res = (res + dfs(length + zero)) % mod; + res = (res + dfs(length + one)) % mod; + return res; + }; + + return dfs(0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(2 ^ n)$ +* Space complexity: $O(n)$ + +> Where $n$ is equal to the given $high$ value. + +--- + +## 2. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def countGoodStrings(self, low: int, high: int, zero: int, one: int) -> int: + mod = 10**9 + 7 + dp = {} + + def dfs(length): + if length > high: + return 0 + if length in dp: + return dp[length] + + dp[length] = 1 if length >= low else 0 + dp[length] += dfs(length + zero) + dfs(length + one) + return dp[length] % mod + + return dfs(0) +``` + +```java +public class Solution { + final int mod = 1_000_000_007; + private int[] dp; + + public int countGoodStrings(int low, int high, int zero, int one) { + dp = new int[high + 1]; + Arrays.fill(dp, -1); + return dfs(low, high, zero, one, 0); + } + + private int dfs(int low, int high, int zero, int one, int length) { + if (length > high) return 0; + if (dp[length] != -1) return dp[length]; + + dp[length] = (length >= low) ? 1 : 0; + dp[length] = (dp[length] + dfs(low, high, zero, one, length + zero)) % mod; + dp[length] = (dp[length] + dfs(low, high, zero, one, length + one)) % mod; + return dp[length]; + } +} +``` + +```cpp +class Solution { + const int mod = 1e9 + 7; + vector dp; + +public: + int countGoodStrings(int low, int high, int zero, int one) { + dp.assign(high + 1, -1); + return dfs(low, high, zero, one, 0); + } + +private: + int dfs(int low, int high, int zero, int one, int length) { + if (length > high) return 0; + if (dp[length] != -1) return dp[length]; + dp[length] = (length >= low) ? 1 : 0; + dp[length] = (dp[length] + dfs(low, high, zero, one, length + zero)) % mod; + dp[length] = (dp[length] + dfs(low, high, zero, one, length + one)) % mod; + return dp[length]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} low + * @param {number} high + * @param {number} zero + * @param {number} one + * @return {number} + */ + countGoodStrings(low, high, zero, one) { + const mod = 1e9 + 7; + const dp = new Array(high + 1).fill(-1); + + const dfs = length => { + if (length > high) return 0; + if (dp[length] !== -1) return dp[length]; + dp[length] = length >= low ? 1 : 0; + dp[length] = (dp[length] + dfs(length + zero)) % mod; + dp[length] = (dp[length] + dfs(length + one)) % mod; + return dp[length]; + }; + + return dfs(0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +> Where $n$ is equal to the given $high$ value. + +--- + +## 3. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def countGoodStrings(self, low: int, high: int, zero: int, one: int) -> int: + dp = { 0 : 1 } + mod = 10**9 + 7 + + for i in range(1, high + 1): + dp[i] = (dp.get(i - one, 0) + dp.get(i - zero, 0)) % mod + + return sum(dp[i] for i in range(low, high + 1)) % mod +``` + +```java +public class Solution { + public int countGoodStrings(int low, int high, int zero, int one) { + int[] dp = new int[high + 1]; + int mod = 1_000_000_007, res = 0; + dp[0] = 1; + + for (int i = 1; i <= high; i++) { + if (i >= zero) dp[i] = (dp[i] + dp[i - zero]) % mod; + if (i >= one) dp[i] = (dp[i] + dp[i - one]) % mod; + if (i >= low) res = (res + dp[i]) % mod; + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int countGoodStrings(int low, int high, int zero, int one) { + vector dp(high + 1); + int mod = 1e9 + 7, res = 0; + dp[0] = 1; + + for (int i = 1; i <= high; i++) { + if (i >= zero) dp[i] = (dp[i] + dp[i - zero]) % mod; + if (i >= one) dp[i] = (dp[i] + dp[i - one]) % mod; + if (i >= low) res = (res + dp[i]) % mod; + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} low + * @param {number} high + * @param {number} zero + * @param {number} one + * @return {number} + */ + countGoodStrings(low, high, zero, one) { + const mod = 1e9 + 7; + const dp = new Int32Array(high + 1); + let res = 0; + dp[0] = 1; + + for (let i = 1; i <= high; i++) { + if (i >= zero) dp[i] = (dp[i] + dp[i - zero]) % mod; + if (i >= one) dp[i] = (dp[i] + dp[i - one]) % mod; + if (i >= low) res = (res + dp[i]) % mod; + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +> Where $n$ is equal to the given $high$ value. \ No newline at end of file diff --git a/articles/integer-break.md b/articles/integer-break.md new file mode 100644 index 000000000..eadd8856d --- /dev/null +++ b/articles/integer-break.md @@ -0,0 +1,661 @@ +## 1. Recursion (Brute Force) + +::tabs-start + +```python +class Solution: + def integerBreak(self, n: int) -> int: + def dfs(num): + if num == 1: + return 1 + res = 0 if num == n else num + for i in range(1, num): + val = dfs(i) * dfs(num - i) + res = max(res, val) + return res + return dfs(n) +``` + +```java +public class Solution { + public int integerBreak(int n) { + return dfs(n, n); + } + + private int dfs(int num, int original) { + if (num == 1) return 1; + + int res = (num == original) ? 0 : num; + for (int i = 1; i < num; i++) { + int val = dfs(i, original) * dfs(num - i, original); + res = Math.max(res, val); + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int integerBreak(int n) { + return dfs(n, n); + } + +private: + int dfs(int num, int original) { + if (num == 1) return 1; + + int res = (num == original) ? 0 : num; + for (int i = 1; i < num; i++) { + int val = dfs(i, original) * dfs(num - i, original); + res = max(res, val); + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {number} + */ + integerBreak(n) { + const dfs = (num) => { + if (num === 1) return 1; + + let res = (num === n) ? 0 : num; + for (let i = 1; i < num; i++) { + const val = dfs(i) * dfs(num - i); + res = Math.max(res, val); + } + return res; + }; + + return dfs(n, n); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ n)$ +* Space complexity: $O(n)$ for recursion stack. + +--- + +## 2. Recursion + +::tabs-start + +```python +class Solution: + def integerBreak(self, n: int) -> int: + def dfs(num, i): + if min(num, i) == 0: + return 1 + + if i > num: + return dfs(num, num) + + return max(i * dfs(num - i, i), dfs(num, i - 1)) + + return dfs(n, n - 1) +``` + +```java +public class Solution { + public int integerBreak(int n) { + return dfs(n, n - 1); + } + + private int dfs(int num, int i) { + if (Math.min(num, i) == 0) { + return 1; + } + + if (i > num) { + return dfs(num, num); + } + + return Math.max(i * dfs(num - i, i), dfs(num, i - 1)); + } +} +``` + +```cpp +class Solution { +public: + int integerBreak(int n) { + return dfs(n, n - 1); + } + +private: + int dfs(int num, int i) { + if (min(num, i) == 0) { + return 1; + } + + if (i > num) { + return dfs(num, num); + } + + return max(i * dfs(num - i, i), dfs(num, i - 1)); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {number} + */ + integerBreak(n) { + const dfs = (num, i) => { + if (Math.min(num, i) === 0) { + return 1; + } + + if (i > num) { + return dfs(num, num); + } + + return Math.max(i * dfs(num - i, i), dfs(num, i - 1)); + }; + + return dfs(n, n - 1); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n)$ for recursion stack. + +--- + +## 3. Dynamic Programming (Top-Down) - I + +::tabs-start + +```python +class Solution: + def integerBreak(self, n: int) -> int: + dp = {1: 1} + + def dfs(num): + if num in dp: + return dp[num] + + dp[num] = 0 if num == n else num + for i in range(1, num): + val = dfs(i) * dfs(num - i) + dp[num] = max(dp[num], val) + return dp[num] + + return dfs(n) +``` + +```java +public class Solution { + private Map dp; + + public int integerBreak(int n) { + dp = new HashMap<>(); + dp.put(1, 1); + return dfs(n, n); + } + + private int dfs(int num, int n) { + if (dp.containsKey(num)) { + return dp.get(num); + } + + int res = (num == n) ? 0 : num; + for (int i = 1; i < num; i++) { + int val = dfs(i, n) * dfs(num - i, n); + res = Math.max(res, val); + } + + dp.put(num, res); + return res; + } +} +``` + +```cpp +class Solution { + unordered_map dp; + +public: + int integerBreak(int n) { + dp[1] = 1; + return dfs(n, n); + } + +private: + int dfs(int num, int n) { + if (dp.find(num) != dp.end()) { + return dp[num]; + } + + int res = (num == n) ? 0 : num; + for (int i = 1; i < num; i++) { + int val = dfs(i, n) * dfs(num - i, n); + res = max(res, val); + } + + dp[num] = res; + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {number} + */ + integerBreak(n) { + const dp = new Map(); + dp.set(1, 1); + + const dfs = (num) => { + if (dp.has(num)) { + return dp.get(num); + } + + let res = (num === n) ? 0 : num; + for (let i = 1; i < num; i++) { + const val = dfs(i) * dfs(num - i); + res = Math.max(res, val); + } + + dp.set(num, res); + return res; + }; + + return dfs(n); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n)$ + +--- + +## 4. Dynamic Programming (Top-Down) - II + +::tabs-start + +```python +class Solution: + def integerBreak(self, n: int) -> int: + dp = {} + def dfs(num, i): + if min(num, i) == 0: + return 1 + if (num, i) in dp: + return dp[(num, i)] + if i > num: + dp[(num, i)] = dfs(num, num) + return dp[(num, i)] + + dp[(num, i)] = max(i * dfs(num - i, i), dfs(num, i - 1)) + return dp[(num, i)] + + return dfs(n, n - 1) +``` + +```java +public class Solution { + private int[][] dp; + + public int integerBreak(int n) { + dp = new int[n + 1][n]; + for (int i = 0; i <= n; i++) { + for (int j = 0; j < n; j++) { + dp[i][j] = -1; + } + } + return dfs(n, n - 1); + } + + private int dfs(int num, int i) { + if (Math.min(num, i) == 0) { + return 1; + } + if (dp[num][i] != -1) { + return dp[num][i]; + } + if (i > num) { + dp[num][i] = dfs(num, num); + return dp[num][i]; + } + dp[num][i] = Math.max(i * dfs(num - i, i), dfs(num, i - 1)); + return dp[num][i]; + } +} +``` + +```cpp +class Solution { + vector> dp; + +public: + int integerBreak(int n) { + dp.assign(n + 1, vector(n, -1)); + return dfs(n, n - 1); + } + +private: + int dfs(int num, int i) { + if (min(num, i) == 0) { + return 1; + } + if (dp[num][i] != -1) { + return dp[num][i]; + } + if (i > num) { + dp[num][i] = dfs(num, num); + return dp[num][i]; + } + dp[num][i] = max(i * dfs(num - i, i), dfs(num, i - 1)); + return dp[num][i]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {number} + */ + integerBreak(n) { + const dp = Array.from({ length: n + 1 }, () => Array(n).fill(-1)); + + const dfs = (num, i) => { + if (Math.min(num, i) === 0) { + return 1; + } + if (dp[num][i] !== -1) { + return dp[num][i]; + } + if (i > num) { + dp[num][i] = dfs(num, num); + return dp[num][i]; + } + dp[num][i] = Math.max(i * dfs(num - i, i), dfs(num, i - 1)); + return dp[num][i]; + }; + + return dfs(n, n - 1); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n ^ 2)$ + +--- + +## 5. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def integerBreak(self, n: int) -> int: + dp = [0] * (n + 1) + dp[1] = 1 + + for num in range(2, n + 1): + dp[num] = 0 if num == n else num + for i in range(1, num): + dp[num] = max(dp[num], dp[i] * dp[num - i]) + + return dp[n] +``` + +```java +public class Solution { + public int integerBreak(int n) { + int[] dp = new int[n + 1]; + dp[1] = 1; + + for (int num = 2; num <= n; num++) { + dp[num] = (num == n) ? 0 : num; + for (int i = 1; i < num; i++) { + dp[num] = Math.max(dp[num], dp[i] * dp[num - i]); + } + } + + return dp[n]; + } +} +``` + +```cpp +class Solution { +public: + int integerBreak(int n) { + vector dp(n + 1, 0); + dp[1] = 1; + + for (int num = 2; num <= n; num++) { + dp[num] = (num == n) ? 0 : num; + for (int i = 1; i < num; i++) { + dp[num] = max(dp[num], dp[i] * dp[num - i]); + } + } + + return dp[n]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {number} + */ + integerBreak(n) { + const dp = new Array(n + 1).fill(0); + dp[1] = 1; + + for (let num = 2; num <= n; num++) { + dp[num] = (num === n) ? 0 : num; + for (let i = 1; i < num; i++) { + dp[num] = Math.max(dp[num], dp[i] * dp[num - i]); + } + } + + return dp[n]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n)$ + +--- + +## 6. Math + +::tabs-start + +```python +class Solution: + def integerBreak(self, n: int) -> int: + if n <= 3: + return n - 1 + + res = 1 + while n > 4: + res *= 3 + n -= 3 + return res * n +``` + +```java +public class Solution { + public int integerBreak(int n) { + if (n <= 3) return n - 1; + + int res = 1; + while (n > 4) { + res *= 3; + n -= 3; + } + return res * n; + } +} +``` + +```cpp +class Solution { +public: + int integerBreak(int n) { + if (n <= 3) return n - 1; + + int res = 1; + while (n > 4) { + res *= 3; + n -= 3; + } + return res * n; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {number} + */ + integerBreak(n) { + if (n <= 3) return n - 1; + + let res = 1; + while (n > 4) { + res *= 3; + n -= 3; + } + return res * n; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ + +--- + +## 7. Math (Optimal) + +::tabs-start + +```python +class Solution: + def integerBreak(self, n: int) -> int: + if n <= 3: + return n - 1 + + res = 3 ** (n // 3) + if n % 3 == 1: + return (res // 3) * 4 + + return res * max(1, (n % 3)) +``` + +```java +public class Solution { + public int integerBreak(int n) { + if (n <= 3) { + return n - 1; + } + + int res = (int) Math.pow(3, n / 3); + if (n % 3 == 1) { + return (res / 3) * 4; + } + + return res * Math.max(1, n % 3); + } +} +``` + +```cpp +class Solution { +public: + int integerBreak(int n) { + if (n <= 3) { + return n - 1; + } + + int res = pow(3, n / 3); + if (n % 3 == 1) { + return (res / 3) * 4; + } + + return res * max(1, n % 3); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {number} + */ + integerBreak(n) { + if (n <= 3) { + return n - 1; + } + + let res = Math.pow(3, Math.floor(n / 3)); + if (n % 3 === 1) { + return Math.floor(res / 3) * 4; + } + + return res * Math.max(1, n % 3); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(\log n)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/last-stone-weight-ii.md b/articles/last-stone-weight-ii.md new file mode 100644 index 000000000..f902d45d2 --- /dev/null +++ b/articles/last-stone-weight-ii.md @@ -0,0 +1,619 @@ +## 1. Recursion + +::tabs-start + +```python +class Solution: + def lastStoneWeightII(self, stones: List[int]) -> int: + stoneSum = sum(stones) + target = (stoneSum + 1) // 2 + + def dfs(i, total): + if total >= target or i == len(stones): + return abs(total - (stoneSum - total)) + return min(dfs(i + 1, total), dfs(i + 1, total + stones[i])) + + return dfs(0, 0) +``` + +```java +public class Solution { + public int lastStoneWeightII(int[] stones) { + int stoneSum = 0; + for (int stone : stones) { + stoneSum += stone; + } + int target = (stoneSum + 1) / 2; + + return dfs(0, 0, stones, stoneSum, target); + } + + private int dfs(int i, int total, int[] stones, int stoneSum, int target) { + if (total >= target || i == stones.length) { + return Math.abs(total - (stoneSum - total)); + } + return Math.min( + dfs(i + 1, total, stones, stoneSum, target), + dfs(i + 1, total + stones[i], stones, stoneSum, target) + ); + } +} +``` + +```cpp +class Solution { +public: + int lastStoneWeightII(vector& stones) { + int stoneSum = accumulate(stones.begin(), stones.end(), 0); + int target = (stoneSum + 1) / 2; + return dfs(0, 0, stones, stoneSum, target); + } + +private: + int dfs(int i, int total, const vector& stones, int stoneSum, int target) { + if (total >= target || i == stones.size()) { + return abs(total - (stoneSum - total)); + } + return min( + dfs(i + 1, total, stones, stoneSum, target), + dfs(i + 1, total + stones[i], stones, stoneSum, target) + ); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} stones + * @return {number} + */ + lastStoneWeightII(stones) { + const stoneSum = stones.reduce((a, b) => a + b, 0); + const target = Math.ceil(stoneSum / 2); + + const dfs = (i, total) => { + if (total >= target || i === stones.length) { + return Math.abs(total - (stoneSum - total)); + } + return Math.min( + dfs(i + 1, total), + dfs(i + 1, total + stones[i]) + ); + }; + + return dfs(0, 0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(2 ^ n)$ +* Space complexity: $O(min(n, m))$ for recursion stack. + +> Where $n$ is the number of stones and $m$ is the sum of the weights of the stones. + +--- + +## 2. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def lastStoneWeightII(self, stones: List[int]) -> int: + stoneSum = sum(stones) + target = (stoneSum + 1) // 2 + dp = {} + + def dfs(i, total): + if total >= target or i == len(stones): + return abs(total - (stoneSum - total)) + if (i, total) in dp: + return dp[(i, total)] + + dp[(i, total)] = min(dfs(i + 1, total), dfs(i + 1, total + stones[i])) + return dp[(i, total)] + + return dfs(0, 0) +``` + +```java +public class Solution { + private int[][] dp; + + public int lastStoneWeightII(int[] stones) { + int stoneSum = 0; + for (int stone : stones) { + stoneSum += stone; + } + int target = (stoneSum + 1) / 2; + dp = new int[stones.length][target + 1]; + for (int i = 0; i < stones.length; i++) { + for (int j = 0; j <= target; j++) { + dp[i][j] = -1; + } + } + + return dfs(0, 0, stones, stoneSum, target); + } + + private int dfs(int i, int total, int[] stones, int stoneSum, int target) { + if (total >= target || i == stones.length) { + return Math.abs(total - (stoneSum - total)); + } + if (dp[i][total] != -1) { + return dp[i][total]; + } + + dp[i][total] = Math.min( + dfs(i + 1, total, stones, stoneSum, target), + dfs(i + 1, total + stones[i], stones, stoneSum, target) + ); + return dp[i][total]; + } +} +``` + +```cpp +class Solution { +private: + vector> dp; + +public: + int lastStoneWeightII(vector& stones) { + int stoneSum = accumulate(stones.begin(), stones.end(), 0); + int target = (stoneSum + 1) / 2; + dp = vector>(stones.size(), vector(target + 1, -1)); + return dfs(0, 0, stones, stoneSum, target); + } + +private: + int dfs(int i, int total, const vector& stones, int stoneSum, int target) { + if (total >= target || i == stones.size()) { + return abs(total - (stoneSum - total)); + } + if (dp[i][total] != -1) { + return dp[i][total]; + } + + dp[i][total] = min( + dfs(i + 1, total, stones, stoneSum, target), + dfs(i + 1, total + stones[i], stones, stoneSum, target) + ); + return dp[i][total]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} stones + * @return {number} + */ + lastStoneWeightII(stones) { + const stoneSum = stones.reduce((a, b) => a + b, 0); + const target = Math.ceil(stoneSum / 2); + const dp = Array.from({ length: stones.length }, () => Array(target + 1).fill(-1)); + + const dfs = (i, total) => { + if (total >= target || i === stones.length) { + return Math.abs(total - (stoneSum - total)); + } + if (dp[i][total] !== -1) { + return dp[i][total]; + } + + dp[i][total] = Math.min( + dfs(i + 1, total), + dfs(i + 1, total + stones[i]) + ); + return dp[i][total]; + }; + + return dfs(0, 0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * m)$ +* Space complexity: $O(n * m)$ + +> Where $n$ is the number of stones and $m$ is the sum of the weights of the stones. + +--- + +## 3. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def lastStoneWeightII(self, stones: List[int]) -> int: + stoneSum = sum(stones) + target = stoneSum // 2 + n = len(stones) + + dp = [[0] * (target + 1) for _ in range(n + 1)] + + for i in range(1, n + 1): + for t in range(target + 1): + if t >= stones[i - 1]: + dp[i][t] = max(dp[i - 1][t], dp[i - 1][t - stones[i - 1]] + stones[i - 1]) + else: + dp[i][t] = dp[i - 1][t] + + return stoneSum - 2 * dp[n][target] +``` + +```java +public class Solution { + public int lastStoneWeightII(int[] stones) { + int stoneSum = 0; + for (int stone : stones) { + stoneSum += stone; + } + int target = stoneSum / 2; + int n = stones.length; + + int[][] dp = new int[n + 1][target + 1]; + + for (int i = 1; i <= n; i++) { + for (int t = 0; t <= target; t++) { + if (t >= stones[i - 1]) { + dp[i][t] = Math.max(dp[i - 1][t], dp[i - 1][t - stones[i - 1]] + stones[i - 1]); + } else { + dp[i][t] = dp[i - 1][t]; + } + } + } + + return stoneSum - 2 * dp[n][target]; + } +} +``` + +```cpp +class Solution { +public: + int lastStoneWeightII(vector& stones) { + int stoneSum = accumulate(stones.begin(), stones.end(), 0); + int target = stoneSum / 2; + int n = stones.size(); + + vector> dp(n + 1, vector(target + 1, 0)); + + for (int i = 1; i <= n; i++) { + for (int t = 0; t <= target; t++) { + if (t >= stones[i - 1]) { + dp[i][t] = max(dp[i - 1][t], dp[i - 1][t - stones[i - 1]] + stones[i - 1]); + } else { + dp[i][t] = dp[i - 1][t]; + } + } + } + + return stoneSum - 2 * dp[n][target]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} stones + * @return {number} + */ + lastStoneWeightII(stones) { + const stoneSum = stones.reduce((a, b) => a + b, 0); + const target = Math.floor(stoneSum / 2); + const n = stones.length; + + const dp = Array.from({ length: n + 1 }, () => Array(target + 1).fill(0)); + + for (let i = 1; i <= n; i++) { + for (let t = 0; t <= target; t++) { + if (t >= stones[i - 1]) { + dp[i][t] = Math.max(dp[i - 1][t], dp[i - 1][t - stones[i - 1]] + stones[i - 1]); + } else { + dp[i][t] = dp[i - 1][t]; + } + } + } + + return stoneSum - 2 * dp[n][target]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * m)$ +* Space complexity: $O(n * m)$ + +> Where $n$ is the number of stones and $m$ is the sum of the weights of the stones. + +--- + +## 4. Dynamic Programming (Space Optimized) + +::tabs-start + +```python +class Solution: + def lastStoneWeightII(self, stones: List[int]) -> int: + stoneSum = sum(stones) + target = stoneSum // 2 + dp = [0] * (target + 1) + + for stone in stones: + for t in range(target, stone - 1, -1): + dp[t] = max(dp[t], dp[t - stone] + stone) + + return stoneSum - 2 * dp[target] +``` + +```java +public class Solution { + public int lastStoneWeightII(int[] stones) { + int stoneSum = 0; + for (int stone : stones) { + stoneSum += stone; + } + int target = stoneSum / 2; + int[] dp = new int[target + 1]; + + for (int stone : stones) { + for (int t = target; t >= stone; t--) { + dp[t] = Math.max(dp[t], dp[t - stone] + stone); + } + } + + return stoneSum - 2 * dp[target]; + } +} +``` + +```cpp +class Solution { +public: + int lastStoneWeightII(vector& stones) { + int stoneSum = accumulate(stones.begin(), stones.end(), 0); + int target = stoneSum / 2; + vector dp(target + 1, 0); + + for (int stone : stones) { + for (int t = target; t >= stone; t--) { + dp[t] = max(dp[t], dp[t - stone] + stone); + } + } + + return stoneSum - 2 * dp[target]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} stones + * @return {number} + */ + lastStoneWeightII(stones) { + const stoneSum = stones.reduce((a, b) => a + b, 0); + const target = Math.floor(stoneSum / 2); + const dp = new Array(target + 1).fill(0); + + for (const stone of stones) { + for (let t = target; t >= stone; t--) { + dp[t] = Math.max(dp[t], dp[t - stone] + stone); + } + } + + return stoneSum - 2 * dp[target]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * m)$ +* Space complexity: $O(m)$ + +> Where $n$ is the number of stones and $m$ is the sum of the weights of the stones. + +--- + +## 5. Dynamic Programming (Hash Set) + +::tabs-start + +```python +class Solution: + def lastStoneWeightII(self, stones: List[int]) -> int: + stoneSum = sum(stones) + target = stoneSum // 2 + dp = {0} + + for stone in stones: + new_dp = set(dp) + for val in dp: + if val + stone == target: + return stoneSum - 2 * target + if val + stone < target: + new_dp.add(val + stone) + dp = new_dp + + return stoneSum - 2 * max(dp) +``` + +```java +public class Solution { + public int lastStoneWeightII(int[] stones) { + int stoneSum = 0; + for (int stone : stones) { + stoneSum += stone; + } + int target = stoneSum / 2; + + Set dp = new HashSet<>(); + dp.add(0); + + for (int stone : stones) { + Set newDp = new HashSet<>(dp); + for (int val : dp) { + if (val + stone == target) { + return stoneSum - 2 * target; + } + if (val + stone < target) { + newDp.add(val + stone); + } + } + dp = newDp; + } + + int maxVal = 0; + for (int val : dp) { + maxVal = Math.max(maxVal, val); + } + + return stoneSum - 2 * maxVal; + } +} +``` + +```cpp +class Solution { +public: + int lastStoneWeightII(vector& stones) { + int stoneSum = accumulate(stones.begin(), stones.end(), 0); + int target = stoneSum / 2; + + unordered_set dp = {0}; + + for (int& stone : stones) { + unordered_set newDp(dp); + for (int val : dp) { + if (val + stone == target) { + return stoneSum - 2 * target; + } + if (val + stone < target) { + newDp.insert(val + stone); + } + } + dp = newDp; + } + + int maxVal = 0; + for (int val : dp) { + maxVal = max(maxVal, val); + } + + return stoneSum - 2 * maxVal; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} stones + * @return {number} + */ + lastStoneWeightII(stones) { + const stoneSum = stones.reduce((a, b) => a + b, 0); + const target = Math.floor(stoneSum / 2); + + let dp = new Set([0]); + + for (const stone of stones) { + const newDp = new Set(dp); + for (const val of dp) { + if (val + stone === target) { + return stoneSum - 2 * target; + } + if (val + stone < target) { + newDp.add(val + stone); + } + } + dp = newDp; + } + + return stoneSum - 2 * Math.max(...dp); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * m)$ +* Space complexity: $O(m)$ + +> Where $n$ is the number of stones and $m$ is the sum of the weights of the stones. + +--- + +## 6. Dynamic Programming (Bitset) + +::tabs-start + +```python +class Solution: + def lastStoneWeightII(self, stones: List[int]) -> int: + stoneSum = sum(stones) + target = stoneSum // 2 + dp = 1 + + for stone in stones: + dp |= dp << stone + + for t in range(target, -1, -1): + if dp & (1 << t): + return stoneSum - 2 * t +``` + +```cpp +class Solution { +public: + int lastStoneWeightII(vector& stones) { + int stoneSum = accumulate(stones.begin(), stones.end(), 0); + int target = stoneSum / 2; + bitset<3001> dp; + dp[0] = true; + + for (int stone : stones) { + dp |= (dp << stone); + } + + for (int t = target; t >= 0; --t) { + if (dp[t]) { + return stoneSum - 2 * t; + } + } + return 0; + } +}; +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * m)$ +* Space complexity: $O(m)$ + +> Where $n$ is the number of stones and $m$ is the sum of the weights of the stones. \ No newline at end of file diff --git a/articles/longest-palindromic-subsequence.md b/articles/longest-palindromic-subsequence.md new file mode 100644 index 000000000..c216ab542 --- /dev/null +++ b/articles/longest-palindromic-subsequence.md @@ -0,0 +1,557 @@ +## 1. Dynamic Programming (Top Down) + +::tabs-start + +```python +class Solution: + def longestPalindromeSubseq(self, s: str) -> int: + n = len(s) + dp = [[-1] * n for _ in range(n)] + + def dfs(i, j): + if i < 0 or j == n: + return 0 + if dp[i][j] != -1: + return dp[i][j] + + if s[i] == s[j]: + length = 1 if i == j else 2 + dp[i][j] = length + dfs(i - 1, j + 1) + else: + dp[i][j] = max(dfs(i - 1, j), dfs(i, j + 1)) + + return dp[i][j] + + for i in range(n): + dfs(i, i) # odd length + dfs(i, i + 1) # even length + + return max(max(row) for row in dp if row != -1) +``` + +```java +public class Solution { + private int[][] dp; + + public int longestPalindromeSubseq(String s) { + int n = s.length(); + dp = new int[n][n]; + + for (int[] row : dp) { + Arrays.fill(row, -1); + } + + for (int i = 0; i < n; i++) { + dfs(i, i, s); // Odd length + dfs(i, i + 1, s); // Even length + } + + int maxLength = 0; + for (int[] row : dp) { + for (int val : row) { + maxLength = Math.max(maxLength, val); + } + } + + return maxLength; + } + + private int dfs(int i, int j, String s) { + if (i < 0 || j == s.length()) { + return 0; + } + if (dp[i][j] != -1) { + return dp[i][j]; + } + + if (s.charAt(i) == s.charAt(j)) { + int length = (i == j) ? 1 : 2; + dp[i][j] = length + dfs(i - 1, j + 1, s); + } else { + dp[i][j] = Math.max(dfs(i - 1, j, s), dfs(i, j + 1, s)); + } + + return dp[i][j]; + } +} +``` + +```cpp +class Solution { +private: + vector> dp; + +public: + int longestPalindromeSubseq(string s) { + int n = s.size(); + dp.resize(n, vector(n, -1)); + + for (int i = 0; i < n; i++) { + dfs(i, i, s); // Odd length + dfs(i, i + 1, s); // Even length + } + + int maxLength = 0; + for (const auto& row : dp) { + for (int val : row) { + maxLength = max(maxLength, val); + } + } + + return maxLength; + } + +private: + int dfs(int i, int j, const string& s) { + if (i < 0 || j == s.size()) { + return 0; + } + if (dp[i][j] != -1) { + return dp[i][j]; + } + + if (s[i] == s[j]) { + int length = (i == j) ? 1 : 2; + dp[i][j] = length + dfs(i - 1, j + 1, s); + } else { + dp[i][j] = max(dfs(i - 1, j, s), dfs(i, j + 1, s)); + } + + return dp[i][j]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {number} + */ + longestPalindromeSubseq(s) { + const n = s.length; + const dp = Array.from({ length: n }, () => Array(n).fill(-1)); + + const dfs = (i, j) => { + if (i < 0 || j === n) { + return 0; + } + if (dp[i][j] !== -1) { + return dp[i][j]; + } + + if (s[i] === s[j]) { + const length = i === j ? 1 : 2; + dp[i][j] = length + dfs(i - 1, j + 1); + } else { + dp[i][j] = Math.max(dfs(i - 1, j), dfs(i, j + 1)); + } + + return dp[i][j]; + }; + + for (let i = 0; i < n; i++) { + dfs(i, i); // Odd length + dfs(i, i + 1); // Even length + } + + let maxLength = 0; + for (const row of dp) { + for (const val of row) { + maxLength = Math.max(maxLength, val); + } + } + + return maxLength; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n ^ 2)$ + +--- + +## 2. Dynamic Programming (Top-Down Optimized) + +::tabs-start + +```python +class Solution: + def longestPalindromeSubseq(self, s: str) -> int: + cache = {} + + def dfs(i, j): + if i > j: + return 0 + if i == j: + return 1 + if (i, j) in cache: + return cache[(i, j)] + + if s[i] == s[j]: + cache[(i, j)] = dfs(i + 1, j - 1) + 2 + else: + cache[(i, j)] = max(dfs(i + 1, j), dfs(i, j - 1)) + + return cache[(i, j)] + + return dfs(0, len(s) - 1) +``` + +```java +public class Solution { + private int[][] dp; + + public int longestPalindromeSubseq(String s) { + int n = s.length(); + dp = new int[n][n]; + + for (int[] row : dp) { + Arrays.fill(row, -1); + } + + return dfs(0, n - 1, s); + } + + private int dfs(int i, int j, String s) { + if (i > j) { + return 0; + } + if (i == j) { + return 1; + } + if (dp[i][j] != -1) { + return dp[i][j]; + } + + if (s.charAt(i) == s.charAt(j)) { + dp[i][j] = dfs(i + 1, j - 1, s) + 2; + } else { + dp[i][j] = Math.max(dfs(i + 1, j, s), dfs(i, j - 1, s)); + } + + return dp[i][j]; + } +} +``` + +```cpp +class Solution { +private: + vector> dp; + +public: + int longestPalindromeSubseq(string s) { + int n = s.size(); + dp.resize(n, vector(n, -1)); + return dfs(0, n - 1, s); + } + +private: + int dfs(int i, int j, const string& s) { + if (i > j) { + return 0; + } + if (i == j) { + return 1; + } + if (dp[i][j] != -1) { + return dp[i][j]; + } + + if (s[i] == s[j]) { + dp[i][j] = dfs(i + 1, j - 1, s) + 2; + } else { + dp[i][j] = max(dfs(i + 1, j, s), dfs(i, j - 1, s)); + } + + return dp[i][j]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {number} + */ + longestPalindromeSubseq(s) { + const n = s.length; + const dp = Array.from({ length: n }, () => Array(n).fill(-1)); + + const dfs = (i, j) => { + if (i > j) { + return 0; + } + if (i === j) { + return 1; + } + if (dp[i][j] !== -1) { + return dp[i][j]; + } + + if (s[i] === s[j]) { + dp[i][j] = dfs(i + 1, j - 1) + 2; + } else { + dp[i][j] = Math.max(dfs(i + 1, j), dfs(i, j - 1)); + } + + return dp[i][j]; + }; + + return dfs(0, n - 1); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n ^ 2)$ + +--- + +## 3. Dynamic Programming (Using LCS Idea) + +::tabs-start + +```python +class Solution: + def longestPalindromeSubseq(self, s: str) -> int: + return self.longestCommonSubsequence(s, s[::-1]) + + def longestCommonSubsequence(self, s1: str, s2: str) -> int: + N, M = len(s1), len(s2) + dp = [[0] * (M + 1) for _ in range(N + 1)] + + for i in range(N): + for j in range(M): + if s1[i] == s2[j]: + dp[i + 1][j + 1] = 1 + dp[i][j] + else: + dp[i + 1][j + 1] = max(dp[i + 1][j], dp[i][j + 1]) + + return dp[N][M] +``` + +```java +public class Solution { + public int longestPalindromeSubseq(String s) { + return longestCommonSubsequence(s, new StringBuilder(s).reverse().toString()); + } + + public int longestCommonSubsequence(String s1, String s2) { + int N = s1.length(), M = s2.length(); + int[][] dp = new int[N + 1][M + 1]; + + for (int i = 0; i < N; i++) { + for (int j = 0; j < M; j++) { + if (s1.charAt(i) == s2.charAt(j)) { + dp[i + 1][j + 1] = 1 + dp[i][j]; + } else { + dp[i + 1][j + 1] = Math.max(dp[i + 1][j], dp[i][j + 1]); + } + } + } + + return dp[N][M]; + } +} +``` + +```cpp +class Solution { +public: + int longestPalindromeSubseq(string s) { + string reversedS = s; + reverse(reversedS.begin(), reversedS.end()); + return longestCommonSubsequence(s, reversedS); + } + + int longestCommonSubsequence(const string& s1, const string& s2) { + int N = s1.size(), M = s2.size(); + vector> dp(N + 1, vector(M + 1, 0)); + + for (int i = 0; i < N; i++) { + for (int j = 0; j < M; j++) { + if (s1[i] == s2[j]) { + dp[i + 1][j + 1] = 1 + dp[i][j]; + } else { + dp[i + 1][j + 1] = max(dp[i + 1][j], dp[i][j + 1]); + } + } + } + + return dp[N][M]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {number} + */ + longestPalindromeSubseq(s) { + return this.longestCommonSubsequence(s, s.split('').reverse().join('')); + } + + /** + * @param {string} s1 + * @param {string} s2 + * @return {number} + */ + longestCommonSubsequence(s1, s2) { + const N = s1.length, M = s2.length; + const dp = Array.from({ length: N + 1 }, () => Array(M + 1).fill(0)); + + for (let i = 0; i < N; i++) { + for (let j = 0; j < M; j++) { + if (s1[i] === s2[j]) { + dp[i + 1][j + 1] = 1 + dp[i][j]; + } else { + dp[i + 1][j + 1] = Math.max(dp[i + 1][j], dp[i][j + 1]); + } + } + } + + return dp[N][M]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n ^ 2)$ + +--- + +## 4. Dynamic Programming (Space Optimized) + +::tabs-start + +```python +class Solution: + def longestPalindromeSubseq(self, s: str) -> int: + n = len(s) + dp = [0] * n + + for i in range(n - 1, -1, -1): + dp[i] = 1 + prev = 0 + for j in range(i + 1, n): + temp = dp[j] + + if s[i] == s[j]: + dp[j] = prev + 2 + else: + dp[j] = max(dp[j], dp[j - 1]) + + prev = temp + + return dp[n - 1] +``` + +```java +public class Solution { + public int longestPalindromeSubseq(String s) { + int n = s.length(); + int[] dp = new int[n]; + + for (int i = n - 1; i >= 0; i--) { + dp[i] = 1; + int prev = 0; + for (int j = i + 1; j < n; j++) { + int temp = dp[j]; + + if (s.charAt(i) == s.charAt(j)) { + dp[j] = prev + 2; + } else { + dp[j] = Math.max(dp[j], dp[j - 1]); + } + prev = temp; + } + } + + return dp[n - 1]; + } +} +``` + +```cpp +class Solution { +public: + int longestPalindromeSubseq(string s) { + int n = s.size(); + vector dp(n, 0); + + for (int i = n - 1; i >= 0; i--) { + dp[i] = 1; + int prev = 0; + for (int j = i + 1; j < n; j++) { + int temp = dp[j]; + + if (s[i] == s[j]) { + dp[j] = prev + 2; + } else { + dp[j] = max(dp[j], dp[j - 1]); + } + + prev = temp; + } + } + + return dp[n - 1]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {number} + */ + longestPalindromeSubseq(s) { + const n = s.length; + const dp = new Array(n).fill(0); + + for (let i = n - 1; i >= 0; i--) { + dp[i] = 1; + let prev = 0; + for (let j = i + 1; j < n; j++) { + const temp = dp[j]; + + if (s[i] === s[j]) { + dp[j] = prev + 2; + } else { + dp[j] = Math.max(dp[j], dp[j - 1]); + } + + prev = temp; + } + } + + return dp[n - 1]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/longest-string-chain.md b/articles/longest-string-chain.md new file mode 100644 index 000000000..b033bad4a --- /dev/null +++ b/articles/longest-string-chain.md @@ -0,0 +1,435 @@ +## 1. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def longestStrChain(self, words: List[str]) -> int: + words.sort(key=lambda w: -len(w)) + word_index = {} # map word to index + for i, w in enumerate(words): + word_index[w] = i + + dp = {} # index of word -> length of longest chain + def dfs(i): + if i in dp: + return dp[i] + res = 1 + for j in range(len(words[i])): + w = words[i] + pred = w[:j] + w[j+1:] + if pred in word_index: + res = max(res, 1 + dfs(word_index[pred])) + dp[i] = res + return res + + for i in range(len(words)): + dfs(i) + return max(dp.values()) +``` + +```java +public class Solution { + public int longestStrChain(String[] words) { + Arrays.sort(words, (a, b) -> Integer.compare(b.length(), a.length())); + Map wordIndex = new HashMap<>(); + for (int i = 0; i < words.length; i++) { + wordIndex.put(words[i], i); + } + + int[] dp = new int[words.length]; + Arrays.fill(dp, -1); + + int maxChain = 1; + for (int i = 0; i < words.length; i++) { + maxChain = Math.max(maxChain, dfs(i, words, wordIndex, dp)); + } + return maxChain; + } + + private int dfs(int i, String[] words, Map wordIndex, int[] dp) { + if (dp[i] != -1) { + return dp[i]; + } + + int res = 1; + String w = words[i]; + for (int j = 0; j < w.length(); j++) { + String pred = w.substring(0, j) + w.substring(j + 1); + if (wordIndex.containsKey(pred)) { + res = Math.max(res, 1 + dfs(wordIndex.get(pred), words, wordIndex, dp)); + } + } + dp[i] = res; + return res; + } +} +``` + +```cpp +class Solution { +public: + int longestStrChain(vector& words) { + sort(words.begin(), words.end(), [](const string& a, const string& b) { + return b.length() < a.length(); + }); + + unordered_map wordIndex; + for (int i = 0; i < words.size(); i++) { + wordIndex[words[i]] = i; + } + + vector dp(words.size(), -1); + int maxChain = 1; + for (int i = 0; i < words.size(); i++) { + maxChain = max(maxChain, dfs(i, words, wordIndex, dp)); + } + return maxChain; + } + +private: + int dfs(int i, vector& words, unordered_map& wordIndex, vector& dp) { + if (dp[i] != -1) { + return dp[i]; + } + + int res = 1; + string w = words[i]; + for (int j = 0; j < w.length(); j++) { + string pred = w.substr(0, j) + w.substr(j + 1); + if (wordIndex.find(pred) != wordIndex.end()) { + res = max(res, 1 + dfs(wordIndex[pred], words, wordIndex, dp)); + } + } + dp[i] = res; + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} words + * @return {number} + */ + longestStrChain(words) { + words.sort((a, b) => b.length - a.length); + const wordIndex = new Map(); + for (let i = 0; i < words.length; i++) { + wordIndex.set(words[i], i); + } + + const dp = new Array(words.length).fill(-1); + + const dfs = (i) => { + if (dp[i] !== -1) { + return dp[i]; + } + + let res = 1; + const w = words[i]; + for (let j = 0; j < w.length; j++) { + const pred = w.slice(0, j) + w.slice(j + 1); + if (wordIndex.has(pred)) { + res = Math.max(res, 1 + dfs(wordIndex.get(pred))); + } + } + dp[i] = res; + return res; + }; + + let maxChain = 1; + for (let i = 0; i < words.length; i++) { + maxChain = Math.max(maxChain, dfs(i)); + } + return maxChain; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * m ^ 2)$ +* Space complexity: $O(n * m)$ + +> Where $n$ is the number of words and $m$ is the average length of each word. + +--- + +## 2. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def longestStrChain(self, words: List[str]) -> int: + def isPred(w1, w2): + i = 0 + for c in w2: + if i == len(w1): + return True + if w1[i] == c: + i += 1 + return i == len(w1) + + words.sort(key=len) + n = len(words) + dp = [1] * n + + for i in range(1, n): + for j in range(i - 1, -1, -1): + if len(words[j]) + 1 < len(words[i]): + break + if len(words[j]) + 1 > len(words[i]) or not isPred(words[j], words[i]): + continue + dp[i] = max(dp[i], 1 + dp[j]) + + return max(dp) +``` + +```java +public class Solution { + public int longestStrChain(String[] words) { + Arrays.sort(words, Comparator.comparingInt(String::length)); + int n = words.length; + int[] dp = new int[n]; + Arrays.fill(dp, 1); + + for (int i = 1; i < n; i++) { + for (int j = i - 1; j >= 0; j--) { + if (words[j].length() + 1 < words[i].length()) { + break; + } + if (words[j].length() + 1 > words[i].length() || !isPred(words[j], words[i])) { + continue; + } + dp[i] = Math.max(dp[i], 1 + dp[j]); + } + } + + int maxChain = 0; + for (int chain : dp) { + maxChain = Math.max(maxChain, chain); + } + return maxChain; + } + + private boolean isPred(String w1, String w2) { + int i = 0; + for (char c : w2.toCharArray()) { + if (i == w1.length()) { + return true; + } + if (w1.charAt(i) == c) { + i++; + } + } + return i == w1.length(); + } +} +``` + +```cpp +class Solution { +public: + int longestStrChain(vector& words) { + sort(words.begin(), words.end(), [](const string& a, const string& b) { + return a.length() < b.length(); + }); + + int n = words.size(); + vector dp(n, 1); + + for (int i = 1; i < n; i++) { + for (int j = i - 1; j >= 0; j--) { + if (words[j].length() + 1 < words[i].length()) { + break; + } + if (words[j].length() + 1 > words[i].length() || !isPred(words[j], words[i])) { + continue; + } + dp[i] = max(dp[i], 1 + dp[j]); + } + } + + return *max_element(dp.begin(), dp.end()); + } + +private: + bool isPred(const string& w1, const string& w2) { + int i = 0; + for (char c : w2) { + if (i == w1.length()) { + return true; + } + if (w1[i] == c) { + i++; + } + } + return i == w1.length(); + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} words + * @return {number} + */ + longestStrChain(words) { + words.sort((a, b) => a.length - b.length); + const n = words.length; + const dp = new Array(n).fill(1); + + const isPred = (w1, w2) => { + let i = 0; + for (const c of w2) { + if (i === w1.length) { + return true; + } + if (w1[i] === c) { + i++; + } + } + return i === w1.length; + }; + + for (let i = 1; i < n; i++) { + for (let j = i - 1; j >= 0; j--) { + if (words[j].length + 1 < words[i].length) { + break; + } + if (words[j].length + 1 > words[i].length || !isPred(words[j], words[i])) { + continue; + } + dp[i] = Math.max(dp[i], 1 + dp[j]); + } + } + + return Math.max(...dp); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2 * m)$ +* Space complexity: $O(n)$ + +> Where $n$ is the number of words and $m$ is the average length of each word. + +--- + +## 3. Dynamic Programming (Bottom-Up Optimized) + +::tabs-start + +```python +class Solution: + def longestStrChain(self, words: List[str]) -> int: + words.sort(key=len) + dp = {} + res = 0 + + for word in words: + dp[word] = 1 + for i in range(len(word)): + pred = word[:i] + word[i+1:] + if pred in dp: + dp[word] = max(dp[word], dp[pred] + 1) + res = max(res, dp[word]) + + return res +``` + +```java +public class Solution { + public int longestStrChain(String[] words) { + Arrays.sort(words, Comparator.comparingInt(String::length)); + Map dp = new HashMap<>(); + int res = 0; + + for (String word : words) { + dp.put(word, 1); + for (int i = 0; i < word.length(); i++) { + String pred = word.substring(0, i) + word.substring(i + 1); + if (dp.containsKey(pred)) { + dp.put(word, Math.max(dp.get(word), dp.get(pred) + 1)); + } + } + res = Math.max(res, dp.get(word)); + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int longestStrChain(vector& words) { + sort(words.begin(), words.end(), [](const string& a, const string& b) { + return a.length() < b.length(); + }); + + unordered_map dp; + int res = 0; + + for (const string& word : words) { + dp[word] = 1; + for (int i = 0; i < word.length(); i++) { + string pred = word.substr(0, i) + word.substr(i + 1); + if (dp.find(pred) != dp.end()) { + dp[word] = max(dp[word], dp[pred] + 1); + } + } + res = max(res, dp[word]); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} words + * @return {number} + */ + longestStrChain(words) { + words.sort((a, b) => a.length - b.length); + const dp = new Map(); + let res = 0; + + for (const word of words) { + dp.set(word, 1); + for (let i = 0; i < word.length; i++) { + const pred = word.slice(0, i) + word.slice(i + 1); + if (dp.has(pred)) { + dp.set(word, Math.max(dp.get(word), dp.get(pred) + 1)); + } + } + res = Math.max(res, dp.get(word)); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * m ^ 2)$ +* Space complexity: $O(n * m)$ + +> Where $n$ is the number of words and $m$ is the average length of each word. \ No newline at end of file diff --git a/articles/maximal-square.md b/articles/maximal-square.md new file mode 100644 index 000000000..ab7d2fce0 --- /dev/null +++ b/articles/maximal-square.md @@ -0,0 +1,560 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def maximalSquare(self, matrix: List[List[str]]) -> int: + m, n = len(matrix), len(matrix[0]) + res = 0 + + for r in range(m): + for c in range(n): + if matrix[r][c] == "0": + continue + k = 1 + while True: + if r + k > m or c + k > n: + break + flag = True + + for i in range(r, r + k): + if matrix[i][c + k - 1] == "0": + flag = False + break + for j in range(c, c + k): + if matrix[r + k - 1][j] == "0": + flag = False + break + + if not flag: + break + res = max(res, k * k) + k += 1 + + return res +``` + +```java +public class Solution { + public int maximalSquare(char[][] matrix) { + int m = matrix.length, n = matrix[0].length; + int res = 0; + + for (int r = 0; r < m; r++) { + for (int c = 0; c < n; c++) { + if (matrix[r][c] == '0') { + continue; + } + int k = 1; + while (true) { + if (r + k > m || c + k > n) { + break; + } + boolean flag = true; + + for (int i = r; i < r + k; i++) { + if (matrix[i][c + k - 1] == '0') { + flag = false; + break; + } + } + for (int j = c; j < c + k; j++) { + if (matrix[r + k - 1][j] == '0') { + flag = false; + break; + } + } + + if (!flag) { + break; + } + res = Math.max(res, k * k); + k++; + } + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int maximalSquare(vector>& matrix) { + int m = matrix.size(), n = matrix[0].size(); + int res = 0; + + for (int r = 0; r < m; r++) { + for (int c = 0; c < n; c++) { + if (matrix[r][c] == '0') { + continue; + } + int k = 1; + while (true) { + if (r + k > m || c + k > n) { + break; + } + bool flag = true; + + for (int i = r; i < r + k; i++) { + if (matrix[i][c + k - 1] == '0') { + flag = false; + break; + } + } + for (int j = c; j < c + k; j++) { + if (matrix[r + k - 1][j] == '0') { + flag = false; + break; + } + } + + if (!flag) { + break; + } + res = max(res, k * k); + k++; + } + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {character[][]} matrix + * @return {number} + */ + maximalSquare(matrix) { + const m = matrix.length, n = matrix[0].length; + let res = 0; + + for (let r = 0; r < m; r++) { + for (let c = 0; c < n; c++) { + if (matrix[r][c] === "0") { + continue; + } + let k = 1; + while (true) { + if (r + k > m || c + k > n) { + break; + } + let flag = true; + + for (let i = r; i < r + k; i++) { + if (matrix[i][c + k - 1] === "0") { + flag = false; + break; + } + } + for (let j = c; j < c + k; j++) { + if (matrix[r + k - 1][j] === "0") { + flag = false; + break; + } + } + + if (!flag) { + break; + } + res = Math.max(res, k * k); + k++; + } + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O((m * n) ^ 2)$ +* Space complexity: $O(1)$ + +> Where $m$ is the number of rows and $n$ is the number columns. + +--- + +## 2. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def maximalSquare(self, matrix: List[List[str]]) -> int: + ROWS, COLS = len(matrix), len(matrix[0]) + cache = {} + + def dfs(r, c): + if r >= ROWS or c >= COLS: + return 0 + if (r, c) not in cache: + down = dfs(r + 1, c) + right = dfs(r, c + 1) + diag = dfs(r + 1, c + 1) + cache[(r, c)] = 0 + if matrix[r][c] == "1": + cache[(r, c)] = 1 + min(down, right, diag) + return cache[(r, c)] + + dfs(0, 0) + return max(cache.values()) ** 2 +``` + +```java +public class Solution { + private int[][] dp; + + public int maximalSquare(char[][] matrix) { + int ROWS = matrix.length, COLS = matrix[0].length; + dp = new int[ROWS][COLS]; + for (int i = 0; i < ROWS; i++) { + for (int j = 0; j < COLS; j++) { + dp[i][j] = -1; + } + } + + dfs(0, 0, matrix); + int maxSquare = 0; + for (int i = 0; i < ROWS; i++) { + for (int j = 0; j < COLS; j++) { + maxSquare = Math.max(maxSquare, dp[i][j]); + } + } + return maxSquare * maxSquare; + } + + private int dfs(int r, int c, char[][] matrix) { + if (r >= matrix.length || c >= matrix[0].length) { + return 0; + } + if (dp[r][c] != -1) { + return dp[r][c]; + } + int down = dfs(r + 1, c, matrix); + int right = dfs(r, c + 1, matrix); + int diag = dfs(r + 1, c + 1, matrix); + dp[r][c] = 0; + if (matrix[r][c] == '1') { + dp[r][c] = 1 + Math.min(down, Math.min(right, diag)); + } + return dp[r][c]; + } +} +``` + +```cpp +class Solution { +private: + vector> dp; + +public: + int maximalSquare(vector>& matrix) { + int ROWS = matrix.size(), COLS = matrix[0].size(); + dp = vector>(ROWS, vector(COLS, -1)); + + dfs(0, 0, matrix); + int maxSquare = 0; + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + maxSquare = max(maxSquare, dp[r][c]); + } + } + return maxSquare * maxSquare; + } + + int dfs(int r, int c, vector>& matrix) { + if (r >= matrix.size() || c >= matrix[0].size()) { + return 0; + } + if (dp[r][c] != -1) { + return dp[r][c]; + } + int down = dfs(r + 1, c, matrix); + int right = dfs(r, c + 1, matrix); + int diag = dfs(r + 1, c + 1, matrix); + dp[r][c] = 0; + if (matrix[r][c] == '1') { + dp[r][c] = 1 + min(down, min(right, diag)); + } + return dp[r][c]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {character[][]} matrix + * @return {number} + */ + maximalSquare(matrix) { + const ROWS = matrix.length, COLS = matrix[0].length; + const dp = Array.from({ length: ROWS }, () => Array(COLS).fill(-1)); + + const dfs = (r, c) => { + if (r >= ROWS || c >= COLS) { + return 0; + } + if (dp[r][c] !== -1) { + return dp[r][c]; + } + const down = dfs(r + 1, c); + const right = dfs(r, c + 1); + const diag = dfs(r + 1, c + 1); + dp[r][c] = 0; + if (matrix[r][c] === "1") { + dp[r][c] = 1 + Math.min(down, Math.min(right, diag)); + } + return dp[r][c]; + }; + + dfs(0, 0); + let maxSquare = 0; + for (let r = 0; r < ROWS; r++) { + for (let c = 0; c < COLS; c++) { + maxSquare = Math.max(maxSquare, dp[r][c]); + } + } + return maxSquare * maxSquare; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n)$ +* Space complexity: $O(m * n)$ + +> Where $m$ is the number of rows and $n$ is the number columns. + +--- + +## 3. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def maximalSquare(self, matrix: List[List[str]]) -> int: + m, n = len(matrix), len(matrix[0]) + dp = [[0] * (n + 1) for _ in range(m + 1)] + max_square = 0 + + for r in range(m - 1, -1, -1): + for c in range(n - 1, -1, -1): + if matrix[r][c] == "1": + dp[r][c] = 1 + min(dp[r + 1][c], dp[r][c + 1], dp[r + 1][c + 1]) + max_square = max(max_square, dp[r][c]) + + return max_square * max_square +``` + +```java +public class Solution { + public int maximalSquare(char[][] matrix) { + int m = matrix.length, n = matrix[0].length; + int[][] dp = new int[m + 1][n + 1]; + int maxSquare = 0; + + for (int r = m - 1; r >= 0; r--) { + for (int c = n - 1; c >= 0; c--) { + if (matrix[r][c] == '1') { + dp[r][c] = 1 + Math.min(dp[r + 1][c], Math.min(dp[r][c + 1], dp[r + 1][c + 1])); + maxSquare = Math.max(maxSquare, dp[r][c]); + } + } + } + + return maxSquare * maxSquare; + } +} +``` + +```cpp +class Solution { +public: + int maximalSquare(vector>& matrix) { + int m = matrix.size(), n = matrix[0].size(); + vector> dp(m + 1, vector(n + 1, 0)); + int maxSquare = 0; + + for (int r = m - 1; r >= 0; r--) { + for (int c = n - 1; c >= 0; c--) { + if (matrix[r][c] == '1') { + dp[r][c] = 1 + min({dp[r + 1][c], dp[r][c + 1], dp[r + 1][c + 1]}); + maxSquare = max(maxSquare, dp[r][c]); + } + } + } + + return maxSquare * maxSquare; + } +}; +``` + +```javascript +class Solution { + /** + * @param {character[][]} matrix + * @return {number} + */ + maximalSquare(matrix) { + const m = matrix.length, n = matrix[0].length; + const dp = Array.from({ length: m + 1 }, () => Array(n + 1).fill(0)); + let maxSquare = 0; + + for (let r = m - 1; r >= 0; r--) { + for (let c = n - 1; c >= 0; c--) { + if (matrix[r][c] === "1") { + dp[r][c] = 1 + Math.min(dp[r + 1][c], dp[r][c + 1], dp[r + 1][c + 1]); + maxSquare = Math.max(maxSquare, dp[r][c]); + } + } + } + + return maxSquare * maxSquare; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n)$ +* Space complexity: $O(m * n)$ + +> Where $m$ is the number of rows and $n$ is the number columns. + +--- + +## 4. Dynamic Programming (Space Optimized) + +::tabs-start + +```python +class Solution: + def maximalSquare(self, matrix: List[List[str]]) -> int: + m, n = len(matrix), len(matrix[0]) + dp = [0] * (n + 1) + max_square = 0 + + for r in range(m - 1, -1, -1): + prev = 0 + for c in range(n - 1, -1, -1): + temp = dp[c] + if matrix[r][c] == "1": + dp[c] = 1 + min(dp[c], dp[c + 1], prev) + max_square = max(max_square, dp[c]) + else: + dp[c] = 0 + prev = temp + + return max_square * max_square +``` + +```java +public class Solution { + public int maximalSquare(char[][] matrix) { + int m = matrix.length, n = matrix[0].length; + int[] dp = new int[n + 1]; + int maxSquare = 0; + + for (int r = m - 1; r >= 0; r--) { + int prev = 0; + for (int c = n - 1; c >= 0; c--) { + int temp = dp[c]; + if (matrix[r][c] == '1') { + dp[c] = 1 + Math.min(dp[c], Math.min(dp[c + 1], prev)); + maxSquare = Math.max(maxSquare, dp[c]); + } else { + dp[c] = 0; + } + prev = temp; + } + } + + return maxSquare * maxSquare; + } +} +``` + +```cpp +class Solution { +public: + int maximalSquare(vector>& matrix) { + int m = matrix.size(), n = matrix[0].size(); + vector dp(n + 1, 0); + int maxSquare = 0; + + for (int r = m - 1; r >= 0; r--) { + int prev = 0; + for (int c = n - 1; c >= 0; c--) { + int temp = dp[c]; + if (matrix[r][c] == '1') { + dp[c] = 1 + min({dp[c], dp[c + 1], prev}); + maxSquare = max(maxSquare, dp[c]); + } else { + dp[c] = 0; + } + prev = temp; + } + } + + return maxSquare * maxSquare; + } +}; +``` + +```javascript +class Solution { + /** + * @param {character[][]} matrix + * @return {number} + */ + maximalSquare(matrix) { + const m = matrix.length, n = matrix[0].length; + const dp = new Array(n + 1).fill(0); + let maxSquare = 0; + let prev = 0; + + for (let r = m - 1; r >= 0; r--) { + for (let c = n - 1; c >= 0; c--) { + const temp = dp[c]; + if (matrix[r][c] === "1") { + dp[c] = 1 + Math.min(dp[c], dp[c + 1], prev); + maxSquare = Math.max(maxSquare, dp[c]); + } else { + dp[c] = 0; + } + prev = temp; + } + } + + return maxSquare * maxSquare; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n)$ +* Space complexity: $O(n)$ + +> Where $m$ is the number of rows and $n$ is the number columns. \ No newline at end of file diff --git a/articles/maximum-alternating-subsequence-sum.md b/articles/maximum-alternating-subsequence-sum.md new file mode 100644 index 000000000..0f296306c --- /dev/null +++ b/articles/maximum-alternating-subsequence-sum.md @@ -0,0 +1,351 @@ +## 1. Recursion + +::tabs-start + +```python +class Solution: + def maxAlternatingSum(self, nums: List[int]) -> int: + def dfs(i, even): + if i == len(nums): + return 0 + total = nums[i] if even else -nums[i] + return max(total + dfs(i + 1, not even), dfs(i + 1, even)) + + return dfs(0, True) +``` + +```java +public class Solution { + public long maxAlternatingSum(int[] nums) { + return dfs(nums, 0, true); + } + + private long dfs(int[] nums, int i, boolean even) { + if (i == nums.length) { + return 0; + } + long total = even ? nums[i] : -nums[i]; + return Math.max(total + dfs(nums, i + 1, !even), dfs(nums, i + 1, even)); + } +} +``` + +```cpp +class Solution { +public: + long long maxAlternatingSum(vector& nums) { + return dfs(nums, 0, true); + } + +private: + long long dfs(vector& nums, int i, bool even) { + if (i == nums.size()) { + return 0; + } + long long total = even ? nums[i] : -nums[i]; + return max(total + dfs(nums, i + 1, !even), dfs(nums, i + 1, even)); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + maxAlternatingSum(nums) { + const dfs = (i, even) => { + if (i === nums.length) { + return 0; + } + + const total = even ? nums[i] : -nums[i]; + return Math.max(total + dfs(i + 1, !even), dfs(i + 1, even)); + }; + + return dfs(0, true); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(2 ^ n)$ +* Space complexity: $O(n)$ for recursion stack. + +--- + +## 2. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def maxAlternatingSum(self, nums: List[int]) -> int: + dp = {} + + def dfs(i, even): + if i == len(nums): + return 0 + if (i, even) in dp: + return dp[(i, even)] + + total = nums[i] if even else -nums[i] + dp[(i, even)] = max(total + dfs(i + 1, not even), dfs(i + 1, even)) + return dp[(i, even)] + + return dfs(0, True) +``` + +```java +public class Solution { + private long dp[][]; + + public long maxAlternatingSum(int[] nums) { + int n = nums.length; + dp = new long[n][2]; + for (int i = 0; i < n; i++) { + dp[i][0] = -1; + dp[i][1] = -1; + } + return dfs(nums, 0, 1); + } + + private long dfs(int[] nums, int i, int even) { + if (i == nums.length) { + return 0; + } + if (dp[i][even] != -1) { + return dp[i][even]; + } + + long total = even == 1 ? nums[i] : -nums[i]; + dp[i][even] = Math.max(total + dfs(nums, i + 1, 1 - even), dfs(nums, i + 1, even)); + return dp[i][even]; + } +} +``` + +```cpp +class Solution { + vector> dp; + +public: + long long maxAlternatingSum(vector& nums) { + dp.assign(nums.size(), vector(2, -1)); + return dfs(nums, 0, true); + } + +private: + long long dfs(vector& nums, int i, bool even) { + if (i == nums.size()) { + return 0; + } + if (dp[i][even] != -1) { + return dp[i][even]; + } + long long total = even ? nums[i] : -nums[i]; + dp[i][even] = max(total + dfs(nums, i + 1, !even), dfs(nums, i + 1, even)); + return dp[i][even]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + maxAlternatingSum(nums) { + const n = nums.length; + const dp = Array.from({ length: n }, () => Array(2).fill(-1)); + + const dfs = (i, even) => { + if (i === n) { + return 0; + } + if (dp[i][even] !== -1) { + return dp[i][even]; + } + + const total = even === 1 ? nums[i] : -nums[i]; + dp[i][even] = Math.max(total + dfs(i + 1, 1 - even), dfs(i + 1, even)); + return dp[i][even]; + }; + + return dfs(0, 1); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def maxAlternatingSum(self, nums: List[int]) -> int: + n = len(nums) + dp = [[0] * 2 for _ in range(n + 1)] # dp[i][0] -> odd, dp[i][1] -> even + + for i in range(n - 1, -1, -1): + dp[i][1] = max(nums[i] + dp[i + 1][0], dp[i + 1][1]) # even + dp[i][0] = max(-nums[i] + dp[i + 1][1], dp[i + 1][0]) # odd + + return dp[0][1] +``` + +```java +public class Solution { + public long maxAlternatingSum(int[] nums) { + int n = nums.length; + long[][] dp = new long[n + 1][2]; // dp[i][0] -> odd, dp[i][1] -> even + + for (int i = n - 1; i >= 0; i--) { + dp[i][1] = Math.max(nums[i] + dp[i + 1][0], dp[i + 1][1]); // even + dp[i][0] = Math.max(-nums[i] + dp[i + 1][1], dp[i + 1][0]); // odd + } + + return dp[0][1]; + } +} +``` + +```cpp +class Solution { +public: + long long maxAlternatingSum(vector& nums) { + int n = nums.size(); + vector> dp(n + 1, vector(2, 0)); // dp[i][0] -> odd, dp[i][1] -> even + + for (int i = n - 1; i >= 0; i--) { + dp[i][1] = max(nums[i] + dp[i + 1][0], dp[i + 1][1]); // even + dp[i][0] = max(-nums[i] + dp[i + 1][1], dp[i + 1][0]); // odd + } + + return dp[0][1]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + maxAlternatingSum(nums) { + const n = nums.length; + const dp = Array.from({ length: n + 1 }, () => [0, 0]); // dp[i][0] -> odd, dp[i][1] -> even + + for (let i = n - 1; i >= 0; i--) { + dp[i][1] = Math.max(nums[i] + dp[i + 1][0], dp[i + 1][1]); // even + dp[i][0] = Math.max(-nums[i] + dp[i + 1][1], dp[i + 1][0]); // odd + } + + return dp[0][1]; // Result starts with even index + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 4. Dynamic Programming (Space Optimized) + +::tabs-start + +```python +class Solution: + def maxAlternatingSum(self, nums: List[int]) -> int: + sumEven = sumOdd = 0 + + for i in range(len(nums) - 1, -1, -1): + tmpEven = max(sumOdd + nums[i], sumEven) + tmpOdd = max(sumEven - nums[i], sumOdd) + sumEven, sumOdd = tmpEven, tmpOdd + + return sumEven +``` + +```java +public class Solution { + public long maxAlternatingSum(int[] nums) { + long sumEven = 0, sumOdd = 0; + + for (int i = nums.length - 1; i >= 0; i--) { + long tmpEven = Math.max(nums[i] + sumOdd, sumEven); + long tmpOdd = Math.max(-nums[i] + sumEven, sumOdd); + sumEven = tmpEven; + sumOdd = tmpOdd; + } + + return sumEven; + } +} +``` + +```cpp +class Solution { +public: + long long maxAlternatingSum(vector& nums) { + long long sumEven = 0, sumOdd = 0; + + for (int i = nums.size() - 1; i >= 0; i--) { + long long tmpEven = max(nums[i] + sumOdd, sumEven); + long long tmpOdd = max(-nums[i] + sumEven, sumOdd); + sumEven = tmpEven; + sumOdd = tmpOdd; + } + + return sumEven; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + maxAlternatingSum(nums) { + let sumEven = 0, sumOdd = 0; + + for (let i = nums.length - 1; i >= 0; i--) { + let tmpEven = Math.max(nums[i] + sumOdd, sumEven); + let tmpOdd = Math.max(-nums[i] + sumEven, sumOdd); + sumEven = tmpEven; + sumOdd = tmpOdd; + } + + return sumEven; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. \ No newline at end of file diff --git a/articles/minimum-path-sum.md b/articles/minimum-path-sum.md new file mode 100644 index 000000000..37968cf67 --- /dev/null +++ b/articles/minimum-path-sum.md @@ -0,0 +1,395 @@ +## 1. Recursion + +::tabs-start + +```python +class Solution: + def minPathSum(self, grid: List[List[int]]) -> int: + def dfs(r, c): + if r == len(grid) - 1 and c == len(grid[0]) - 1: + return grid[r][c] + if r == len(grid) or c == len(grid[0]): + return float('inf') + return grid[r][c] + min(dfs(r + 1, c), dfs(r, c + 1)) + + return dfs(0, 0) +``` + +```java +public class Solution { + public int minPathSum(int[][] grid) { + return dfs(0, 0, grid); + } + + public int dfs(int r, int c, int[][] grid) { + if (r == grid.length - 1 && c == grid[0].length - 1) { + return grid[r][c]; + } + if (r == grid.length || c == grid[0].length) { + return Integer.MAX_VALUE; + } + return grid[r][c] + Math.min(dfs(r + 1, c, grid), dfs(r, c + 1, grid)); + } +} +``` + +```cpp +class Solution { +public: + int minPathSum(vector>& grid) { + return dfs(0, 0, grid); + } + + int dfs(int r, int c, vector>& grid) { + if (r == grid.size() - 1 && c == grid[0].size() - 1) { + return grid[r][c]; + } + if (r == grid.size() || c == grid[0].size()) { + return INT_MAX; + } + return grid[r][c] + min(dfs(r + 1, c, grid), dfs(r, c + 1, grid)); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} grid + * @return {number} + */ + minPathSum(grid) { + const dfs = (r, c) => { + if (r === grid.length - 1 && c === grid[0].length - 1) { + return grid[r][c]; + } + if (r === grid.length || c === grid[0].length) { + return Infinity; + } + return grid[r][c] + Math.min(dfs(r + 1, c), dfs(r, c + 1)); + }; + + return dfs(0, 0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(2 ^ {m + n})$ +* Space complexity: $O(m + n)$ for recursion stack. + +> Where $m$ is the number of rows and $n$ is the number of columns. + +--- + +## 2. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def minPathSum(self, grid: List[List[int]]) -> int: + m, n = len(grid), len(grid[0]) + dp = [[-1] * n for _ in range(m)] + + def dfs(r, c): + if r == m - 1 and c == n - 1: + return grid[r][c] + if r == m or c == n: + return float('inf') + if dp[r][c] != -1: + return dp[r][c] + + dp[r][c] = grid[r][c] + min(dfs(r + 1, c), dfs(r, c + 1)) + return dp[r][c] + + return dfs(0, 0) +``` + +```java +public class Solution { + private int[][] dp; + + public int minPathSum(int[][] grid) { + int m = grid.length, n = grid[0].length; + dp = new int[m][n]; + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + dp[i][j] = -1; + } + } + return dfs(0, 0, grid); + } + + public int dfs(int r, int c, int[][] grid) { + if (r == grid.length - 1 && c == grid[0].length - 1) { + return grid[r][c]; + } + if (r == grid.length || c == grid[0].length) { + return Integer.MAX_VALUE; + } + if (dp[r][c] != -1) { + return dp[r][c]; + } + + dp[r][c] = grid[r][c] + Math.min(dfs(r + 1, c, grid), dfs(r, c + 1, grid)); + return dp[r][c]; + } +} +``` + +```cpp +class Solution { +private: + vector> dp; + +public: + int minPathSum(vector>& grid) { + int m = grid.size(), n = grid[0].size(); + dp = vector>(m, vector(n, -1)); + return dfs(0, 0, grid); + } + + int dfs(int r, int c, vector>& grid) { + if (r == grid.size() - 1 && c == grid[0].size() - 1) { + return grid[r][c]; + } + if (r == grid.size() || c == grid[0].size()) { + return INT_MAX; + } + if (dp[r][c] != -1) { + return dp[r][c]; + } + + dp[r][c] = grid[r][c] + min(dfs(r + 1, c, grid), dfs(r, c + 1, grid)); + return dp[r][c]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} grid + * @return {number} + */ + minPathSum(grid) { + const m = grid.length, n = grid[0].length; + const dp = Array.from({ length: m }, () => Array(n).fill(-1)); + + const dfs = (r, c) => { + if (r === m - 1 && c === n - 1) { + return grid[r][c]; + } + if (r === m || c === n) { + return Infinity; + } + if (dp[r][c] !== -1) { + return dp[r][c]; + } + + dp[r][c] = grid[r][c] + Math.min(dfs(r + 1, c), dfs(r, c + 1)); + return dp[r][c]; + }; + + return dfs(0, 0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n)$ +* Space complexity: $O(m * n)$ + +> Where $m$ is the number of rows and $n$ is the number of columns. + +--- + +## 3. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def minPathSum(self, grid: List[List[int]]) -> int: + ROWS, COLS = len(grid), len(grid[0]) + dp = [[float("inf")] * (COLS + 1) for _ in range(ROWS + 1)] + dp[ROWS - 1][COLS] = 0 + + for r in range(ROWS - 1, -1, -1): + for c in range(COLS - 1, -1, -1): + dp[r][c] = grid[r][c] + min(dp[r + 1][c], dp[r][c + 1]) + + return dp[0][0] +``` + +```java +public class Solution { + public int minPathSum(int[][] grid) { + int ROWS = grid.length, COLS = grid[0].length; + int[][] dp = new int[ROWS + 1][COLS + 1]; + + for (int r = 0; r <= ROWS; r++) { + for (int c = 0; c <= COLS; c++) { + dp[r][c] = Integer.MAX_VALUE; + } + } + dp[ROWS - 1][COLS] = 0; + + for (int r = ROWS - 1; r >= 0; r--) { + for (int c = COLS - 1; c >= 0; c--) { + dp[r][c] = grid[r][c] + Math.min(dp[r + 1][c], dp[r][c + 1]); + } + } + + return dp[0][0]; + } +} +``` + +```cpp +class Solution { +public: + int minPathSum(vector>& grid) { + int ROWS = grid.size(), COLS = grid[0].size(); + vector> dp(ROWS + 1, vector(COLS + 1, INT_MAX)); + dp[ROWS - 1][COLS] = 0; + + for (int r = ROWS - 1; r >= 0; r--) { + for (int c = COLS - 1; c >= 0; c--) { + dp[r][c] = grid[r][c] + min(dp[r + 1][c], dp[r][c + 1]); + } + } + + return dp[0][0]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} grid + * @return {number} + */ + minPathSum(grid) { + const ROWS = grid.length, COLS = grid[0].length; + const dp = Array.from({ length: ROWS + 1 }, () => Array(COLS + 1).fill(Infinity)); + dp[ROWS - 1][COLS] = 0; + + for (let r = ROWS - 1; r >= 0; r--) { + for (let c = COLS - 1; c >= 0; c--) { + dp[r][c] = grid[r][c] + Math.min(dp[r + 1][c], dp[r][c + 1]); + } + } + + return dp[0][0]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n)$ +* Space complexity: $O(m * n)$ + +> Where $m$ is the number of rows and $n$ is the number of columns. + +--- + +## 4. Dynamic Programming (Space Optimized) + +::tabs-start + +```python +class Solution: + def minPathSum(self, grid: List[List[int]]) -> int: + ROWS, COLS = len(grid), len(grid[0]) + dp = [float("inf")] * (COLS + 1) + dp[COLS - 1] = 0 + + for r in range(ROWS - 1, -1, -1): + for c in range(COLS - 1, -1, -1): + dp[c] = grid[r][c] + min(dp[c], dp[c + 1]) + + return dp[0] +``` + +```java +public class Solution { + public int minPathSum(int[][] grid) { + int ROWS = grid.length, COLS = grid[0].length; + int[] dp = new int[COLS + 1]; + for (int c = 0; c <= COLS; c++) { + dp[c] = Integer.MAX_VALUE; + } + dp[COLS - 1] = 0; + + for (int r = ROWS - 1; r >= 0; r--) { + for (int c = COLS - 1; c >= 0; c--) { + dp[c] = grid[r][c] + Math.min(dp[c], dp[c + 1]); + } + } + + return dp[0]; + } +} +``` + +```cpp +class Solution { +public: + int minPathSum(vector>& grid) { + int ROWS = grid.size(), COLS = grid[0].size(); + vector dp(COLS + 1, INT_MAX); + dp[COLS - 1] = 0; + + for (int r = ROWS - 1; r >= 0; r--) { + for (int c = COLS - 1; c >= 0; c--) { + dp[c] = grid[r][c] + min(dp[c], dp[c + 1]); + } + } + + return dp[0]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} grid + * @return {number} + */ + minPathSum(grid) { + const ROWS = grid.length, COLS = grid[0].length; + const dp = new Array(COLS + 1).fill(Infinity); + dp[COLS - 1] = 0; + + for (let r = ROWS - 1; r >= 0; r--) { + for (let c = COLS - 1; c >= 0; c--) { + dp[c] = grid[r][c] + Math.min(dp[c], dp[c + 1]); + } + } + + return dp[0]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n)$ +* Space complexity: $O(n)$ + +> Where $m$ is the number of rows and $n$ is the number of columns. \ No newline at end of file diff --git a/articles/n-th-tribonacci-number.md b/articles/n-th-tribonacci-number.md new file mode 100644 index 000000000..bf04e98f7 --- /dev/null +++ b/articles/n-th-tribonacci-number.md @@ -0,0 +1,301 @@ +## 1. Recursion + +::tabs-start + +```python +class Solution: + def tribonacci(self, n: int) -> int: + if n <= 2: + return 1 if n != 0 else 0 + return self.tribonacci(n - 1) + self.tribonacci(n - 2) + self.tribonacci(n - 3) +``` + +```java +public class Solution { + public int tribonacci(int n) { + if (n <= 2) { + return n == 0 ? 0 : 1; + } + return tribonacci(n - 1) + tribonacci(n - 2) + tribonacci(n - 3); + } +} +``` + +```cpp +class Solution { +public: + int tribonacci(int n) { + if (n <= 2) { + return n == 0 ? 0 : 1; + } + return tribonacci(n - 1) + tribonacci(n - 2) + tribonacci(n - 3); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {number} + */ + tribonacci(n) { + if (n <= 2) { + return n === 0 ? 0 : 1; + } + return this.tribonacci(n - 1) + this.tribonacci(n - 2) + this.tribonacci(n - 3); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(3 ^ n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def __init__(self): + self.dp = {} + + def tribonacci(self, n: int) -> int: + if n <= 2: + return 1 if n != 0 else 0 + if n in self.dp: + return self.dp[n] + + self.dp[n] = self.tribonacci(n - 1) + self.tribonacci(n - 2) + self.tribonacci(n - 3) + return self.dp[n] +``` + +```java +public class Solution { + private HashMap dp = new HashMap<>(); + + public int tribonacci(int n) { + if (n <= 2) { + return n == 0 ? 0 : 1; + } + if (dp.containsKey(n)) { + return dp.get(n); + } + + dp.put(n, tribonacci(n - 1) + tribonacci(n - 2) + tribonacci(n - 3)); + return dp.get(n); + } +} +``` + +```cpp +class Solution { + unordered_map dp; + +public: + int tribonacci(int n) { + if (n <= 2) { + return n == 0 ? 0 : 1; + } + if (dp.count(n)) return dp[n]; + + dp[n] = tribonacci(n - 1) + tribonacci(n - 2) + tribonacci(n - 3); + return dp[n]; + } +}; +``` + +```javascript +class Solution { + /** + * @constructor + */ + constructor() { + this.dp = new Map(); + } + + /** + * @param {number} n + * @return {number} + */ + tribonacci(n) { + if (n <= 2) { + return n === 0 ? 0 : 1; + } + if (this.dp.has(n)) { + return this.dp.get(n); + } + const result = this.tribonacci(n - 1) + this.tribonacci(n - 2) + this.tribonacci(n - 3); + this.dp.set(n, result); + return result; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def tribonacci(self, n: int) -> int: + if n <= 2: + return 1 if n != 0 else 0 + + dp = [0] * (n + 1) + dp[1] = dp[2] = 1 + for i in range(3, n + 1): + dp[i] = dp[i - 1] + dp[i - 2] + dp[i - 3] + return dp[n] +``` + +```java +public class Solution { + public int tribonacci(int n) { + if (n <= 2) { + return n == 0 ? 0 : 1; + } + + int[] dp = new int[n + 1]; + dp[1] = dp[2] = 1; + for (int i = 3; i <= n; i++) { + dp[i] = dp[i - 1] + dp[i - 2] + dp[i - 3]; + } + return dp[n]; + } +} +``` + +```cpp +class Solution { +public: + int tribonacci(int n) { + if (n <= 2) { + return n == 0 ? 0 : 1; + } + + vector dp(n + 1, 0); + dp[1] = dp[2] = 1; + for (int i = 3; i <= n; i++) { + dp[i] = dp[i - 1] + dp[i - 2] + dp[i - 3]; + } + return dp[n]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {number} + */ + tribonacci(n) { + if (n <= 2) { + return n === 0 ? 0 : 1; + } + + const dp = new Array(n + 1).fill(0); + dp[1] = dp[2] = 1; + for (let i = 3; i <= n; i++) { + dp[i] = dp[i - 1] + dp[i - 2] + dp[i - 3]; + } + return dp[n]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 4. Dynamic Programming (Space Optimized) + +::tabs-start + +```python +class Solution: + def tribonacci(self, n: int) -> int: + t = [0, 1, 1] + + if n < 3: + return t[n] + + for i in range(3, n + 1): + t[i % 3] = sum(t) + return t[n % 3] +``` + +```java +public class Solution { + public int tribonacci(int n) { + int t[] = {0, 1, 1}; + if (n < 3) return t[n]; + + for (int i = 3; i <= n; ++i) { + t[i % 3] = t[0] + t[1] + t[2]; + } + return t[n % 3]; + } +} +``` + +```cpp +class Solution { +public: + int tribonacci(int n) { + int t[] = {0, 1, 1}; + if (n < 3) return t[n]; + + for (int i = 3; i <= n; ++i) { + t[i % 3] = t[0] + t[1] + t[2]; + } + return t[n % 3]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {number} + */ + tribonacci(n) { + const t = [0, 1, 1] + if (n < 3) return t[n]; + + for (let i = 3; i <= n; ++i) { + t[i % 3] = t[0] + t[1] + t[2]; + } + return t[n % 3]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/new-21-game.md b/articles/new-21-game.md new file mode 100644 index 000000000..e65fcd1da --- /dev/null +++ b/articles/new-21-game.md @@ -0,0 +1,485 @@ +## 1. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def new21Game(self, n: int, k: int, maxPts: int) -> float: + cache = {} + + def dfs(score): + if score >= k: + return 1 if score <= n else 0 + if score in cache: + return cache[score] + + prob = 0 + for i in range(1, maxPts + 1): + prob += dfs(score + i) + + cache[score] = prob / maxPts + return cache[score] + + return dfs(0) +``` + +```java +public class Solution { + private double[] dp; + + public double new21Game(int n, int k, int maxPts) { + dp = new double[k]; + for (int i = 0; i < dp.length; i++) dp[i] = -1.0; + return dfs(0, n, k, maxPts); + } + + private double dfs(int score, int n, int k, int maxPts) { + if (score >= k) { + return score <= n ? 1.0 : 0.0; + } + if (dp[score] != -1.0) { + return dp[score]; + } + + double prob = 0; + for (int i = 1; i <= maxPts; i++) { + prob += dfs(score + i, n, k, maxPts); + } + + dp[score] = prob / maxPts; + return dp[score]; + } +} +``` + +```cpp +class Solution { +private: + vector dp; + +public: + double new21Game(int n, int k, int maxPts) { + dp.resize(k, -1.0); + return dfs(0, n, k, maxPts); + } + +private: + double dfs(int score, int n, int k, int maxPts) { + if (score >= k) { + return score <= n ? 1.0 : 0.0; + } + if (dp[score] != -1.0) { + return dp[score]; + } + + double prob = 0; + for (int i = 1; i <= maxPts; i++) { + prob += dfs(score + i, n, k, maxPts); + } + + dp[score] = prob / maxPts; + return dp[score]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number} k + * @param {number} maxPts + * @return {number} + */ + new21Game(n, k, maxPts) { + const dp = new Array(k).fill(-1); + + const dfs = (score) => { + if (score >= k) { + return score <= n ? 1 : 0; + } + if (dp[score] !== -1) { + return dp[score]; + } + + let prob = 0; + for (let i = 1; i <= maxPts; i++) { + prob += dfs(score + i); + } + + dp[score] = prob / maxPts; + return dp[score]; + }; + + return dfs(0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(k * m)$ +* Space complexity: $O(k)$ + +> Where $k$ is the threshold score, $m$ is the maximum points per draw and $n$ is the upper bound on score. + +--- + +## 2. Dynamic Programming (Top-Down Optimized) + +::tabs-start + +```python +class Solution: + def new21Game(self, n: int, k: int, maxPts: int) -> float: + cache = {} + + def dfs(score): + if score == k - 1: + return min(n - k + 1, maxPts) / maxPts + if score > n: + return 0 + if score >= k: + return 1.0 + + if score in cache: + return cache[score] + + cache[score] = dfs(score + 1) + cache[score] -= (dfs(score + 1 + maxPts) - dfs(score + 1)) / maxPts + return cache[score] + + return dfs(0) +``` + +```java +public class Solution { + private double[] dp; + + public double new21Game(int n, int k, int maxPts) { + dp = new double[k + maxPts]; + for (int i = 0; i < dp.length; i++) dp[i] = -1.0; + return dfs(0, n, k, maxPts); + } + + private double dfs(int score, int n, int k, int maxPts) { + if (score == k - 1) { + return Math.min(n - k + 1, maxPts) / (double) maxPts; + } + if (score > n) { + return 0.0; + } + if (score >= k) { + return 1.0; + } + if (dp[score] != -1.0) { + return dp[score]; + } + + dp[score] = dfs(score + 1, n, k, maxPts); + dp[score] -= (dfs(score + 1 + maxPts, n, k, maxPts) - dfs(score + 1, n, k, maxPts)) / maxPts; + return dp[score]; + } +} +``` + +```cpp +class Solution { +private: + vector dp; + +public: + double new21Game(int n, int k, int maxPts) { + dp.resize(k + maxPts, -1.0); + return dfs(0, n, k, maxPts); + } + +private: + double dfs(int score, int n, int k, int maxPts) { + if (score == k - 1) { + return min(n - k + 1, maxPts) / (double)maxPts; + } + if (score > n) { + return 0.0; + } + if (score >= k) { + return 1.0; + } + if (dp[score] != -1.0) { + return dp[score]; + } + + dp[score] = dfs(score + 1, n, k, maxPts); + dp[score] -= (dfs(score + 1 + maxPts, n, k, maxPts) - dfs(score + 1, n, k, maxPts)) / maxPts; + return dp[score]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number} k + * @param {number} maxPts + * @return {number} + */ + new21Game(n, k, maxPts) { + const dp = new Array(k + maxPts).fill(-1); + + const dfs = (score) => { + if (score === k - 1) { + return Math.min(n - k + 1, maxPts) / maxPts; + } + if (score > n) { + return 0; + } + if (score >= k) { + return 1.0; + } + if (dp[score] !== -1) { + return dp[score]; + } + + dp[score] = dfs(score + 1); + dp[score] -= (dfs(score + 1 + maxPts) - dfs(score + 1)) / maxPts; + return dp[score]; + }; + + return dfs(0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(k + m)$ +* Space complexity: $O(n)$ + +> Where $k$ is the threshold score, $m$ is the maximum points per draw and $n$ is the upper bound on score. + +--- + +## 3. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def new21Game(self, n: int, k: int, maxPts: int) -> float: + dp = [0] * (n + 1) + dp[0] = 1.0 + + for score in range(1, n + 1): + for draw in range(1, maxPts + 1): + if score - draw >= 0 and score - draw < k: + dp[score] += dp[score - draw] / maxPts + + return sum(dp[k:n + 1]) +``` + +```java +public class Solution { + public double new21Game(int n, int k, int maxPts) { + double[] dp = new double[n + 1]; + dp[0] = 1.0; + + for (int score = 1; score <= n; score++) { + for (int draw = 1; draw <= maxPts; draw++) { + if (score - draw >= 0 && score - draw < k) { + dp[score] += dp[score - draw] / maxPts; + } + } + } + + double result = 0.0; + for (int i = k; i <= n; i++) { + result += dp[i]; + } + + return result; + } +} +``` + +```cpp +class Solution { +public: + double new21Game(int n, int k, int maxPts) { + vector dp(n + 1, 0.0); + dp[0] = 1.0; + + for (int score = 1; score <= n; score++) { + for (int draw = 1; draw <= maxPts; draw++) { + if (score - draw >= 0 && score - draw < k) { + dp[score] += dp[score - draw] / maxPts; + } + } + } + + double result = 0.0; + for (int i = k; i <= n; i++) { + result += dp[i]; + } + + return result; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number} k + * @param {number} maxPts + * @return {number} + */ + new21Game(n, k, maxPts) { + const dp = new Array(n + 1).fill(0); + dp[0] = 1.0; + + for (let score = 1; score <= n; score++) { + for (let draw = 1; draw <= maxPts; draw++) { + if (score - draw >= 0 && score - draw < k) { + dp[score] += dp[score - draw] / maxPts; + } + } + } + + let result = 0.0; + for (let i = k; i <= n; i++) { + result += dp[i]; + } + + return result; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * m)$ +* Space complexity: $O(n)$ + +> Where $k$ is the threshold score, $m$ is the maximum points per draw and $n$ is the upper bound on score. + +--- + +## 4. Dynamic Programming (Sliding Window) + +::tabs-start + +```python +class Solution: + def new21Game(self, n: int, k: int, maxPts: int) -> float: + if k == 0: + return 1.0 + + windowSum = 0 + for i in range(k, k + maxPts): + windowSum += 1 if i <= n else 0 + + dp = {} + for i in range(k - 1, -1, -1): + dp[i] = windowSum / maxPts + remove = 0 + if i + maxPts <= n: + remove = dp.get(i + maxPts, 1) + windowSum += dp[i] - remove + return dp[0] +``` + +```java +class Solution { + public double new21Game(int n, int k, int maxPts) { + if (k == 0) { + return 1.0; + } + double windowSum = 0.0; + for (int i = k; i < k + maxPts; i++) { + windowSum += (i <= n) ? 1.0 : 0.0; + } + HashMap dp = new HashMap<>(); + for (int i = k - 1; i >= 0; i--) { + dp.put(i, windowSum / maxPts); + double remove = 0.0; + if (i + maxPts <= n) { + remove = dp.getOrDefault(i + maxPts, 1.0); + } + windowSum += dp.get(i) - remove; + } + return dp.get(0); + } +} +``` + +```cpp +class Solution { +public: + double new21Game(int n, int k, int maxPts) { + if (k == 0) { + return 1.0; + } + double windowSum = 0.0; + for (int i = k; i < k + maxPts; i++) { + windowSum += (i <= n) ? 1.0 : 0.0; + } + unordered_map dp; + for (int i = k - 1; i >= 0; i--) { + dp[i] = windowSum / maxPts; + double remove = 0.0; + if (i + maxPts <= n) { + remove = (dp.find(i + maxPts) != dp.end()) ? dp[i + maxPts] : 1.0; + } + windowSum += dp[i] - remove; + } + return dp[0]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number} k + * @param {number} maxPts + * @return {number} + */ + new21Game(n, k, maxPts) { + if (k === 0) { + return 1.0; + } + let windowSum = 0.0; + for (let i = k; i < k + maxPts; i++) { + windowSum += (i <= n) ? 1.0 : 0.0; + } + let dp = {}; + for (let i = k - 1; i >= 0; i--) { + dp[i] = windowSum / maxPts; + let remove = 0.0; + if (i + maxPts <= n) { + remove = (dp[i + maxPts] !== undefined) ? dp[i + maxPts] : 1.0; + } + windowSum += dp[i] - remove; + } + return dp[0]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(k + m)$ +* Space complexity: $O(n)$ + +> Where $k$ is the threshold score, $m$ is the maximum points per draw and $n$ is the upper bound on score. \ No newline at end of file diff --git a/articles/number-of-longest-increasing-subsequence.md b/articles/number-of-longest-increasing-subsequence.md new file mode 100644 index 000000000..f11ef6599 --- /dev/null +++ b/articles/number-of-longest-increasing-subsequence.md @@ -0,0 +1,699 @@ +## 1. Recursion + +::tabs-start + +```python +class Solution: + def findNumberOfLIS(self, nums: List[int]) -> int: + LIS = 0 + res = 0 + + def dfs(i, length): + nonlocal LIS, res + if LIS < length: + LIS = length + res = 1 + elif LIS == length: + res += 1 + + for j in range(i + 1, len(nums)): + if nums[j] <= nums[i]: + continue + dfs(j, length + 1) + + for i in range(len(nums)): + dfs(i, 1) + return res +``` + +```java +public class Solution { + private int LIS = 0; + private int res = 0; + + public int findNumberOfLIS(int[] nums) { + for (int i = 0; i < nums.length; i++) { + dfs(nums, i, 1); + } + return res; + } + + private void dfs(int[] nums, int i, int length) { + if (LIS < length) { + LIS = length; + res = 1; + } else if (LIS == length) { + res++; + } + + for (int j = i + 1; j < nums.length; j++) { + if (nums[j] <= nums[i]) { + continue; + } + dfs(nums, j, length + 1); + } + } +} +``` + +```cpp +class Solution { + int LIS = 0; + int res = 0; + + void dfs(vector& nums, int i, int length) { + if (LIS < length) { + LIS = length; + res = 1; + } else if (LIS == length) { + res++; + } + + for (int j = i + 1; j < nums.size(); j++) { + if (nums[j] <= nums[i]) { + continue; + } + dfs(nums, j, length + 1); + } + } + +public: + int findNumberOfLIS(vector& nums) { + for (int i = 0; i < nums.size(); i++) { + dfs(nums, i, 1); + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + findNumberOfLIS(nums) { + let LIS = 0; + let res = 0; + + const dfs = (i, length) => { + if (LIS < length) { + LIS = length; + res = 1; + } else if (LIS === length) { + res++; + } + + for (let j = i + 1; j < nums.length; j++) { + if (nums[j] <= nums[i]) continue; + dfs(j, length + 1); + } + }; + + for (let i = 0; i < nums.length; i++) { + dfs(i, 1); + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def findNumberOfLIS(self, nums: List[int]) -> int: + dp = {} + + def dfs(i): + if i in dp: + return + + maxLen = maxCnt = 1 + for j in range(i + 1, len(nums)): + if nums[j] > nums[i]: + dfs(j) + length, count = dp[j] + if 1 + length > maxLen: + maxLen = length + 1 + maxCnt = count + elif 1 + length == maxLen: + maxCnt += count + dp[i] = (maxLen, maxCnt) + + lenLIS = res = 0 + for i in range(len(nums)): + dfs(i) + maxLen, maxCnt = dp[i] + if maxLen > lenLIS: + lenLIS = maxLen + res = maxCnt + elif maxLen == lenLIS: + res += maxCnt + + return res +``` + +```java +public class Solution { + private int[][] dp; + + public int findNumberOfLIS(int[] nums) { + int n = nums.length; + dp = new int[n][2]; // dp[i][0] = maxLen, dp[i][1] = maxCnt + + for (int i = 0; i < n; i++) { + dp[i][0] = dp[i][1] = -1; + } + + int lenLIS = 0, res = 0; + for (int i = 0; i < n; i++) { + dfs(nums, i); + int[] result = dp[i]; + if (result[0] > lenLIS) { + lenLIS = result[0]; + res = result[1]; + } else if (result[0] == lenLIS) { + res += result[1]; + } + } + return res; + } + + private void dfs(int[] nums, int i) { + if (dp[i][0] != -1) return; + + int maxLen = 1, maxCnt = 1; + for (int j = i + 1; j < nums.length; j++) { + if (nums[j] > nums[i]) { + dfs(nums, j); + int[] next = dp[j]; + if (1 + next[0] > maxLen) { + maxLen = 1 + next[0]; + maxCnt = next[1]; + } else if (1 + next[0] == maxLen) { + maxCnt += next[1]; + } + } + } + + dp[i] = new int[]{maxLen, maxCnt}; + } +} +``` + +```cpp +class Solution { +private: + vector> dp; + + void dfs(vector& nums, int i) { + if (dp[i][0] != -1) return; + + int maxLen = 1, maxCnt = 1; + for (int j = i + 1; j < nums.size(); j++) { + if (nums[j] > nums[i]) { + dfs(nums, j); + int length = dp[j][0]; + int count = dp[j][1]; + if (1 + length > maxLen) { + maxLen = 1 + length; + maxCnt = count; + } else if (1 + length == maxLen) { + maxCnt += count; + } + } + } + dp[i] = {maxLen, maxCnt}; + } + +public: + int findNumberOfLIS(vector& nums) { + int n = nums.size(); + dp.assign(n, vector(2, -1)); + + int lenLIS = 0, res = 0; + for (int i = 0; i < n; i++) { + dfs(nums, i); + int maxLen = dp[i][0]; + int maxCnt = dp[i][1]; + if (maxLen > lenLIS) { + lenLIS = maxLen; + res = maxCnt; + } else if (maxLen == lenLIS) { + res += maxCnt; + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + findNumberOfLIS(nums) { + const dp = new Map(); + + const dfs = (i) => { + if (dp.has(i)) return; + + let maxLen = 1, maxCnt = 1; + for (let j = i + 1; j < nums.length; j++) { + if (nums[j] > nums[i]) { + dfs(j); + const [length, count] = dp.get(j); + if (1 + length > maxLen) { + maxLen = 1 + length; + maxCnt = count; + } else if (1 + length === maxLen) { + maxCnt += count; + } + } + } + dp.set(i, [maxLen, maxCnt]); + }; + + let lenLIS = 0, res = 0; + for (let i = 0; i < nums.length; i++) { + dfs(i); + const [maxLen, maxCnt] = dp.get(i); + if (maxLen > lenLIS) { + lenLIS = maxLen; + res = maxCnt; + } else if (maxLen === lenLIS) { + res += maxCnt; + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n)$ + +--- + +## 3. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def findNumberOfLIS(self, nums: List[int]) -> int: + n = len(nums) + dp = [[0, 0] for _ in range(n)] + lenLIS, res = 0, 0 + + for i in range(n - 1, -1, -1): + maxLen, maxCnt = 1, 1 + for j in range(i + 1, n): + if nums[j] > nums[i]: + length, count = dp[j] + if length + 1 > maxLen: + maxLen, maxCnt = length + 1, count + elif length + 1 == maxLen: + maxCnt += count + + if maxLen > lenLIS: + lenLIS, res = maxLen, maxCnt + elif maxLen == lenLIS: + res += maxCnt + dp[i] = [maxLen, maxCnt] + + return res +``` + +```java +public class Solution { + public int findNumberOfLIS(int[] nums) { + int n = nums.length; + int[][] dp = new int[n][2]; + int lenLIS = 0, res = 0; + + for (int i = n - 1; i >= 0; i--) { + int maxLen = 1, maxCnt = 1; + for (int j = i + 1; j < n; j++) { + if (nums[j] > nums[i]) { + int length = dp[j][0]; + int count = dp[j][1]; + if (length + 1 > maxLen) { + maxLen = length + 1; + maxCnt = count; + } else if (length + 1 == maxLen) { + maxCnt += count; + } + } + } + + if (maxLen > lenLIS) { + lenLIS = maxLen; + res = maxCnt; + } else if (maxLen == lenLIS) { + res += maxCnt; + } + dp[i][0] = maxLen; + dp[i][1] = maxCnt; + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int findNumberOfLIS(vector& nums) { + int n = nums.size(); + vector> dp(n, vector(2, 0)); + int lenLIS = 0, res = 0; + + for (int i = n - 1; i >= 0; i--) { + int maxLen = 1, maxCnt = 1; + for (int j = i + 1; j < n; j++) { + if (nums[j] > nums[i]) { + int length = dp[j][0]; + int count = dp[j][1]; + if (length + 1 > maxLen) { + maxLen = length + 1; + maxCnt = count; + } else if (length + 1 == maxLen) { + maxCnt += count; + } + } + } + + if (maxLen > lenLIS) { + lenLIS = maxLen; + res = maxCnt; + } else if (maxLen == lenLIS) { + res += maxCnt; + } + dp[i][0] = maxLen; + dp[i][1] = maxCnt; + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + findNumberOfLIS(nums) { + const n = nums.length; + const dp = Array.from({ length: n }, () => [0, 0]); + let lenLIS = 0, res = 0; + + for (let i = n - 1; i >= 0; i--) { + let maxLen = 1, maxCnt = 1; + for (let j = i + 1; j < n; j++) { + if (nums[j] > nums[i]) { + const [length, count] = dp[j]; + if (length + 1 > maxLen) { + maxLen = length + 1; + maxCnt = count; + } else if (length + 1 === maxLen) { + maxCnt += count; + } + } + } + + if (maxLen > lenLIS) { + lenLIS = maxLen; + res = maxCnt; + } else if (maxLen === lenLIS) { + res += maxCnt; + } + dp[i] = [maxLen, maxCnt]; + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n)$ + +--- + +## 4. Dynamic Programming (Binary Search + Prefix Sum) + +::tabs-start + +```python +class Solution: + def findNumberOfLIS(self, nums: List[int]) -> int: + n = len(nums) + dp = [[[0, 0], [nums[0], 1]]] + + def bs1(num): + l, r = 0, len(dp) - 1 + j = len(dp) - 1 + while l <= r: + mid = (l + r) // 2 + if dp[mid][-1][0] < num: + l = mid + 1 + else: + j = mid + r = mid - 1 + return j + + def bs2(i, num): + if i < 0: + return 1 + l, r = 1, len(dp[i]) - 1 + j = 0 + while l <= r: + mid = (l + r) // 2 + if dp[i][mid][0] >= num: + j = mid + l = mid + 1 + else: + r = mid - 1 + return dp[i][-1][1] - dp[i][j][1] + + LIS = 1 + for i in range(1, n): + num = nums[i] + if num > dp[-1][-1][0]: + count = bs2(LIS - 1, num) + dp.append([[0, 0], [num, count]]) + LIS += 1 + else: + j = bs1(num) + count = bs2(j - 1, num) + dp[j].append([num, dp[j][-1][1] + count]) + + return dp[-1][-1][1] +``` + +```java +public class Solution { + public int findNumberOfLIS(int[] nums) { + int n = nums.length; + List> dp = new ArrayList<>(); + List first = new ArrayList<>(); + first.add(new int[]{0, 0}); + first.add(new int[]{nums[0], 1}); + dp.add(first); + + int LIS = 1; + + for (int i = 1; i < n; i++) { + int num = nums[i]; + if (num > dp.get(dp.size() - 1).get(dp.get(dp.size() - 1).size() - 1)[0]) { + int count = bs2(dp, LIS - 1, num); + List newList = new ArrayList<>(); + newList.add(new int[]{0, 0}); + newList.add(new int[]{num, count}); + dp.add(newList); + LIS++; + } else { + int j = bs1(dp, num); + int count = bs2(dp, j - 1, num); + List list = dp.get(j); + int[] last = list.get(list.size() - 1); + list.add(new int[]{num, last[1] + count}); + } + } + + return dp.get(dp.size() - 1).get(dp.get(dp.size() - 1).size() - 1)[1]; + } + + private int bs1(List> dp, int num) { + int l = 0, r = dp.size() - 1, j = dp.size() - 1; + while (l <= r) { + int mid = (l + r) / 2; + if (dp.get(mid).get(dp.get(mid).size() - 1)[0] < num) { + l = mid + 1; + } else { + j = mid; + r = mid - 1; + } + } + return j; + } + + private int bs2(List> dp, int i, int num) { + if (i < 0) return 1; + int l = 1, r = dp.get(i).size() - 1, j = 0; + while (l <= r) { + int mid = (l + r) / 2; + if (dp.get(i).get(mid)[0] >= num) { + j = mid; + l = mid + 1; + } else { + r = mid - 1; + } + } + return dp.get(i).get(dp.get(i).size() - 1)[1] - dp.get(i).get(j)[1]; + } +} +``` + +```cpp +class Solution { +public: + int findNumberOfLIS(vector& nums) { + vector>> dp = {{{0, 0}, {nums[0], 1}}}; + int LIS = 1; + + for (int i = 1; i < nums.size(); i++) { + int num = nums[i]; + if (num > dp.back().back().first) { + int count = bs2(dp, LIS - 1, num); + dp.push_back({{0, 0}, {num, count}}); + LIS++; + } else { + int j = bs1(dp, num); + int count = bs2(dp, j - 1, num); + dp[j].push_back({num, dp[j].back().second + count}); + } + } + + return dp.back().back().second; + } + +private: + int bs1(vector>>& dp, int num) { + int l = 0, r = dp.size() - 1, j = dp.size() - 1; + while (l <= r) { + int mid = (l + r) / 2; + if (dp[mid].back().first < num) { + l = mid + 1; + } else { + j = mid; + r = mid - 1; + } + } + return j; + } + + int bs2(vector>>& dp, int i, int num) { + if (i < 0) return 1; + int l = 1, r = dp[i].size() - 1, j = 0; + while (l <= r) { + int mid = (l + r) / 2; + if (dp[i][mid].first >= num) { + j = mid; + l = mid + 1; + } else { + r = mid - 1; + } + } + return dp[i].back().second - dp[i][j].second; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + findNumberOfLIS(nums) { + const dp = [[[0, 0], [nums[0], 1]]]; + let LIS = 1; + + const bs1 = (num) => { + let l = 0, r = dp.length - 1, j = dp.length - 1; + while (l <= r) { + const mid = Math.floor((l + r) / 2); + if (dp[mid][dp[mid].length - 1][0] < num) { + l = mid + 1; + } else { + j = mid; + r = mid - 1; + } + } + return j; + }; + + const bs2 = (i, num) => { + if (i < 0) return 1; + let l = 1, r = dp[i].length - 1, j = 0; + while (l <= r) { + const mid = Math.floor((l + r) / 2); + if (dp[i][mid][0] >= num) { + j = mid; + l = mid + 1; + } else { + r = mid - 1; + } + } + return dp[i][dp[i].length - 1][1] - dp[i][j][1]; + }; + + for (let i = 1; i < nums.length; i++) { + const num = nums[i]; + if (num > dp[dp.length - 1][dp[dp.length - 1].length - 1][0]) { + const count = bs2(LIS - 1, num); + dp.push([[0, 0], [num, count]]); + LIS++; + } else { + const j = bs1(num); + const count = bs2(j - 1, num); + dp[j].push([num, dp[j][dp[j].length - 1][1] + count]); + } + } + + return dp[dp.length - 1][dp[dp.length - 1].length - 1][1]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n\log n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/ones-and-zeroes.md b/articles/ones-and-zeroes.md new file mode 100644 index 000000000..4d3e53b4d --- /dev/null +++ b/articles/ones-and-zeroes.md @@ -0,0 +1,531 @@ +## 1. Recursion + +::tabs-start + +```python +class Solution: + def findMaxForm(self, strs: List[str], m: int, n: int) -> int: + arr = [[0] * 2 for _ in range(len(strs))] + for i, s in enumerate(strs): + for c in s: + arr[i][ord(c) - ord('0')] += 1 + + def dfs(i, m, n): + if i == len(strs): + return 0 + + res = dfs(i + 1, m, n) + if m >= arr[i][0] and n >= arr[i][1]: + res = max(res, 1 + dfs(i + 1, m - arr[i][0], n - arr[i][1])) + return res + + return dfs(0, m, n) +``` + +```java +public class Solution { + public int findMaxForm(String[] strs, int m, int n) { + int[][] arr = new int[strs.length][2]; + for (int i = 0; i < strs.length; i++) { + for (char c : strs[i].toCharArray()) { + arr[i][c - '0']++; + } + } + + return dfs(0, m, n, arr); + } + + private int dfs(int i, int m, int n, int[][] arr) { + if (i == arr.length) { + return 0; + } + + int res = dfs(i + 1, m, n, arr); + if (m >= arr[i][0] && n >= arr[i][1]) { + res = Math.max(res, 1 + dfs(i + 1, m - arr[i][0], n - arr[i][1], arr)); + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int findMaxForm(vector& strs, int m, int n) { + vector> arr(strs.size(), vector(2)); + for (int i = 0; i < strs.size(); i++) { + for (char c : strs[i]) { + arr[i][c - '0']++; + } + } + return dfs(0, m, n, arr); + } + +private: + int dfs(int i, int m, int n, vector>& arr) { + if (i == arr.size()) { + return 0; + } + + int res = dfs(i + 1, m, n, arr); + if (m >= arr[i][0] && n >= arr[i][1]) { + res = max(res, 1 + dfs(i + 1, m - arr[i][0], n - arr[i][1], arr)); + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} strs + * @param {number} m + * @param {number} n + * @return {number} + */ + findMaxForm(strs, m, n) { + const arr = Array.from({ length: strs.length }, () => [0, 0]); + for (let i = 0; i < strs.length; i++) { + for (const c of strs[i]) { + arr[i][c - "0"]++; + } + } + + const dfs = (i, m, n) => { + if (i === strs.length) { + return 0; + } + + let res = dfs(i + 1, m, n); + if (m >= arr[i][0] && n >= arr[i][1]) { + res = Math.max(res, 1 + dfs(i + 1, m - arr[i][0], n - arr[i][1])); + } + return res; + }; + + return dfs(0, m, n); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(2 ^ N)$ +* Space complexity: $O(N)$ for recursion stack. + +> Where $N$ represents the number of binary strings, and $m$ and $n$ are the maximum allowable counts of zeros and ones, respectively. + +--- + +## 2. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def findMaxForm(self, strs: List[str], m: int, n: int) -> int: + arr = [[0] * 2 for _ in range(len(strs))] + for i, s in enumerate(strs): + for c in s: + arr[i][ord(c) - ord('0')] += 1 + + dp = {} + + def dfs(i, m, n): + if i == len(strs): + return 0 + if m == 0 and n == 0: + return 0 + if (i, m, n) in dp: + return dp[(i, m, n)] + + res = dfs(i + 1, m, n) + if m >= arr[i][0] and n >= arr[i][1]: + res = max(res, 1 + dfs(i + 1, m - arr[i][0], n - arr[i][1])) + dp[(i, m, n)] = res + return res + + return dfs(0, m, n) +``` + +```java +public class Solution { + private int[][][] dp; + private int[][] arr; + + public int findMaxForm(String[] strs, int m, int n) { + arr = new int[strs.length][2]; + for (int i = 0; i < strs.length; i++) { + for (char c : strs[i].toCharArray()) { + arr[i][c - '0']++; + } + } + + dp = new int[strs.length][m + 1][n + 1]; + for (int i = 0; i < strs.length; i++) { + for (int j = 0; j <= m; j++) { + for (int k = 0; k <= n; k++) { + dp[i][j][k] = -1; + } + } + } + + return dfs(0, m, n); + } + + private int dfs(int i, int m, int n) { + if (i == arr.length) { + return 0; + } + if (m == 0 && n == 0) { + return 0; + } + if (dp[i][m][n] != -1) { + return dp[i][m][n]; + } + + int res = dfs(i + 1, m, n); + if (m >= arr[i][0] && n >= arr[i][1]) { + res = Math.max(res, 1 + dfs(i + 1, m - arr[i][0], n - arr[i][1])); + } + dp[i][m][n] = res; + return res; + } +} +``` + +```cpp +class Solution { +private: + vector>> dp; + vector> arr; + +public: + int findMaxForm(vector& strs, int m, int n) { + arr = vector>(strs.size(), vector(2)); + for (int i = 0; i < strs.size(); i++) { + for (char c : strs[i]) { + arr[i][c - '0']++; + } + } + + dp = vector>>(strs.size(), vector>(m + 1, vector(n + 1, -1))); + return dfs(0, m, n); + } + +private: + int dfs(int i, int m, int n) { + if (i == arr.size()) { + return 0; + } + if (m == 0 && n == 0) { + return 0; + } + if (dp[i][m][n] != -1) { + return dp[i][m][n]; + } + + int res = dfs(i + 1, m, n); + if (m >= arr[i][0] && n >= arr[i][1]) { + res = max(res, 1 + dfs(i + 1, m - arr[i][0], n - arr[i][1])); + } + dp[i][m][n] = res; + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} strs + * @param {number} m + * @param {number} n + * @return {number} + */ + findMaxForm(strs, m, n) { + const arr = Array.from({ length: strs.length }, () => [0, 0]); + for (let i = 0; i < strs.length; i++) { + for (const c of strs[i]) { + arr[i][c - "0"]++; + } + } + + const dp = Array.from({ length: strs.length }, () => + Array.from({ length: m + 1 }, () => Array(n + 1).fill(-1)) + ); + + const dfs = (i, m, n) => { + if (i === strs.length) return 0; + if (m === 0 && n === 0) return 0; + if (dp[i][m][n] !== -1) return dp[i][m][n]; + + let res = dfs(i + 1, m, n); + if (m >= arr[i][0] && n >= arr[i][1]) { + res = Math.max(res, 1 + dfs(i + 1, m - arr[i][0], n - arr[i][1])); + } + dp[i][m][n] = res; + return res; + }; + + return dfs(0, m, n); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n * N)$ +* Space complexity: $O(m * n * N)$ + +> Where $N$ represents the number of binary strings, and $m$ and $n$ are the maximum allowable counts of zeros and ones, respectively. + +--- + +## 3. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def findMaxForm(self, strs: List[str], m: int, n: int) -> int: + arr = [[0] * 2 for _ in range(len(strs))] + for i, s in enumerate(strs): + for c in s: + arr[i][ord(c) - ord('0')] += 1 + + dp = [[[0] * (n + 1) for _ in range(m + 1)] for _ in range(len(strs) + 1)] + + for i in range(1, len(strs) + 1): + for j in range(m + 1): + for k in range(n + 1): + dp[i][j][k] = dp[i - 1][j][k] + if j >= arr[i - 1][0] and k >= arr[i - 1][1]: + dp[i][j][k] = max(dp[i][j][k], 1 + dp[i - 1][j - arr[i - 1][0]][k - arr[i - 1][1]]) + + return dp[len(strs)][m][n] +``` + +```java +public class Solution { + public int findMaxForm(String[] strs, int m, int n) { + int[][] arr = new int[strs.length][2]; + for (int i = 0; i < strs.length; i++) { + for (char c : strs[i].toCharArray()) { + arr[i][c - '0']++; + } + } + + int[][][] dp = new int[strs.length + 1][m + 1][n + 1]; + + for (int i = 1; i <= strs.length; i++) { + for (int j = 0; j <= m; j++) { + for (int k = 0; k <= n; k++) { + dp[i][j][k] = dp[i - 1][j][k]; + if (j >= arr[i - 1][0] && k >= arr[i - 1][1]) { + dp[i][j][k] = Math.max(dp[i][j][k], 1 + dp[i - 1][j - arr[i - 1][0]][k - arr[i - 1][1]]); + } + } + } + } + + return dp[strs.length][m][n]; + } +} +``` + +```cpp +class Solution { +public: + int findMaxForm(vector& strs, int m, int n) { + vector> arr(strs.size(), vector(2)); + for (int i = 0; i < strs.size(); i++) { + for (char c : strs[i]) { + arr[i][c - '0']++; + } + } + + vector>> dp(strs.size() + 1, vector>(m + 1, vector(n + 1, 0))); + + for (int i = 1; i <= strs.size(); i++) { + for (int j = 0; j <= m; j++) { + for (int k = 0; k <= n; k++) { + dp[i][j][k] = dp[i - 1][j][k]; + if (j >= arr[i - 1][0] && k >= arr[i - 1][1]) { + dp[i][j][k] = max(dp[i][j][k], 1 + dp[i - 1][j - arr[i - 1][0]][k - arr[i - 1][1]]); + } + } + } + } + + return dp[strs.size()][m][n]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} strs + * @param {number} m + * @param {number} n + * @return {number} + */ + findMaxForm(strs, m, n) { + const arr = strs.map(s => { + const zeros = s.split('').filter(c => c === '0').length; + const ones = s.length - zeros; + return [zeros, ones]; + }); + + const dp = Array.from({ length: strs.length + 1 }, () => + Array.from({ length: m + 1 }, () => Array(n + 1).fill(0)) + ); + + for (let i = 1; i <= strs.length; i++) { + for (let j = 0; j <= m; j++) { + for (let k = 0; k <= n; k++) { + dp[i][j][k] = dp[i - 1][j][k]; + if (j >= arr[i - 1][0] && k >= arr[i - 1][1]) { + dp[i][j][k] = Math.max(dp[i][j][k], 1 + dp[i - 1][j - arr[i - 1][0]][k - arr[i - 1][1]]); + } + } + } + } + + return dp[strs.length][m][n]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n * N)$ +* Space complexity: $O(m * n * N)$ + +> Where $N$ represents the number of binary strings, and $m$ and $n$ are the maximum allowable counts of zeros and ones, respectively. + +--- + +## 4. Dynamic Programming (Space Optimized) + +::tabs-start + +```python +class Solution: + def findMaxForm(self, strs: List[str], m: int, n: int) -> int: + arr = [[0, 0] for _ in range(len(strs))] + for i, s in enumerate(strs): + for c in s: + arr[i][ord(c) - ord('0')] += 1 + + dp = [[0] * (n + 1) for _ in range(m + 1)] + + for zeros, ones in arr: + for j in range(m, zeros - 1, -1): + for k in range(n, ones - 1, -1): + dp[j][k] = max(dp[j][k], 1 + dp[j - zeros][k - ones]) + + return dp[m][n] +``` + +```java +public class Solution { + public int findMaxForm(String[] strs, int m, int n) { + int[][] arr = new int[strs.length][2]; + for (int i = 0; i < strs.length; i++) { + for (char c : strs[i].toCharArray()) { + arr[i][c - '0']++; + } + } + + int[][] dp = new int[m + 1][n + 1]; + + for (int[] pair : arr) { + int zeros = pair[0], ones = pair[1]; + for (int j = m; j >= zeros; j--) { + for (int k = n; k >= ones; k--) { + dp[j][k] = Math.max(dp[j][k], 1 + dp[j - zeros][k - ones]); + } + } + } + + return dp[m][n]; + } +} +``` + +```cpp +class Solution { +public: + int findMaxForm(vector& strs, int m, int n) { + vector> arr(strs.size(), vector(2)); + for (int i = 0; i < strs.size(); i++) { + for (char c : strs[i]) { + arr[i][c - '0']++; + } + } + + vector> dp(m + 1, vector(n + 1, 0)); + + for (const auto& pair : arr) { + int zeros = pair[0], ones = pair[1]; + for (int j = m; j >= zeros; j--) { + for (int k = n; k >= ones; k--) { + dp[j][k] = max(dp[j][k], 1 + dp[j - zeros][k - ones]); + } + } + } + + return dp[m][n]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} strs + * @param {number} m + * @param {number} n + * @return {number} + */ + findMaxForm(strs, m, n) { + const arr = Array.from({ length: strs.length }, () => [0, 0]); + for (let i = 0; i < strs.length; i++) { + for (const c of strs[i]) { + arr[i][c - "0"]++; + } + } + + const dp = Array.from({ length: m + 1 }, () => Array(n + 1).fill(0)); + + for (const [zeros, ones] of arr) { + for (let j = m; j >= zeros; j--) { + for (let k = n; k >= ones; k--) { + dp[j][k] = Math.max(dp[j][k], 1 + dp[j - zeros][k - ones]); + } + } + } + + return dp[m][n]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n * N)$ +* Space complexity: $O(m * n + N)$ + +> Where $N$ represents the number of binary strings, and $m$ and $n$ are the maximum allowable counts of zeros and ones, respectively. \ No newline at end of file diff --git a/articles/solving-questions-with-brainpower.md b/articles/solving-questions-with-brainpower.md new file mode 100644 index 000000000..248ec63b3 --- /dev/null +++ b/articles/solving-questions-with-brainpower.md @@ -0,0 +1,233 @@ +## 1. Recursion + +::tabs-start + +```python +class Solution: + def mostPoints(self, questions: List[List[int]]) -> int: + def dfs(i): + if i >= len(questions): + return 0 + return max(dfs(i + 1), questions[i][0] + dfs(i + 1 + questions[i][1])) + return dfs(0) +``` + +```java +public class Solution { + public long mostPoints(int[][] questions) { + return dfs(0, questions); + } + + private long dfs(int i, int[][] questions) { + if (i >= questions.length) return 0; + return Math.max(dfs(i + 1, questions), questions[i][0] + dfs(i + 1 + questions[i][1], questions)); + } +} +``` + +```cpp +class Solution { +public: + long long mostPoints(vector>& questions) { + return dfs(0, questions); + } + +private: + long long dfs(int i, vector>& questions) { + if (i >= questions.size()) return 0; + return max(dfs(i + 1, questions), questions[i][0] + dfs(i + 1 + questions[i][1], questions)); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} questions + * @return {number} + */ + mostPoints(questions) { + const dfs = (i) => { + if (i >= questions.length) return 0; + return Math.max(dfs(i + 1), questions[i][0] + dfs(i + 1 + questions[i][1])); + }; + return dfs(0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(2 ^ n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def mostPoints(self, questions: List[List[int]]) -> int: + dp = {} + def dfs(i): + if i >= len(questions): + return 0 + if i in dp: + return dp[i] + dp[i] = max(dfs(i + 1), questions[i][0] + dfs(i + 1 + questions[i][1])) + return dp[i] + return dfs(0) +``` + +```java +public class Solution { + private long[] dp; + + public long mostPoints(int[][] questions) { + dp = new long[questions.length]; + return dfs(0, questions); + } + + private long dfs(int i, int[][] questions) { + if (i >= questions.length) return 0; + if (dp[i] != 0) return dp[i]; + dp[i] = Math.max(dfs(i + 1, questions), questions[i][0] + dfs(i + 1 + questions[i][1], questions)); + return dp[i]; + } +} + +``` + +```cpp +class Solution { + vector dp; + +public: + long long mostPoints(vector>& questions) { + dp.assign(questions.size(), 0); + return dfs(0, questions); + } + +private: + long long dfs(int i, vector>& questions) { + if (i >= questions.size()) return 0; + if (dp[i] != 0) return dp[i]; + dp[i] = max(dfs(i + 1, questions), questions[i][0] + dfs(i + 1 + questions[i][1], questions)); + return dp[i]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} questions + * @return {number} + */ + mostPoints(questions) { + const dp = new Array(questions.length).fill(-1); + + const dfs = (i) => { + if (i >= questions.length) return 0; + if (dp[i] !== -1) return dp[i]; + dp[i] = Math.max(dfs(i + 1), questions[i][0] + dfs(i + 1 + questions[i][1])); + return dp[i]; + }; + + return dfs(0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def mostPoints(self, questions: List[List[int]]) -> int: + dp = {} + + for i in range(len(questions) - 1, -1, -1): + dp[i] = max( + questions[i][0] + dp.get(i + 1 + questions[i][1], 0), + dp.get(i + 1, 0) + ) + return dp.get(0) +``` + +```java +public class Solution { + public long mostPoints(int[][] questions) { + int n = questions.length; + long[] dp = new long[n + 1]; + + for (int i = n - 1; i >= 0; i--) { + dp[i] = Math.max( + questions[i][0] + (i + 1 + questions[i][1] < n ? dp[i + 1 + questions[i][1]] : 0), + dp[i + 1] + ); + } + return dp[0]; + } +} +``` + +```cpp +class Solution { +public: + long long mostPoints(vector>& questions) { + int n = questions.size(); + vector dp(n + 1, 0); + + for (int i = n - 1; i >= 0; i--) { + dp[i] = max( + (long long)questions[i][0] + (i + 1 + questions[i][1] < n ? dp[i + 1 + questions[i][1]] : 0), + dp[i + 1] + ); + } + return dp[0]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} questions + * @return {number} + */ + mostPoints(questions) { + const n = questions.length; + const dp = new Array(n + 1).fill(0); + + for (let i = n - 1; i >= 0; i--) { + dp[i] = Math.max( + questions[i][0] + (i + 1 + questions[i][1] < n ? dp[i + 1 + questions[i][1]] : 0), + dp[i + 1] + ); + } + return dp[0]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/stickers-to-spell-word.md b/articles/stickers-to-spell-word.md new file mode 100644 index 000000000..2abbe91a7 --- /dev/null +++ b/articles/stickers-to-spell-word.md @@ -0,0 +1,580 @@ +## 1. Dynamic Programming (Top-Down) - I + +::tabs-start + +```python +class Solution: + def minStickers(self, stickers: List[str], target: str) -> int: + stickCount = [] + for s in stickers: + stickCount.append({}) + for c in s: + stickCount[-1][c] = 1 + stickCount[-1].get(c, 0) + + dp = {} + + def dfs(t, stick): + if t in dp: + return dp[t] + res = 1 if stick else 0 + remainT = "" + for c in t: + if c in stick and stick[c] > 0: + stick[c] -= 1 + else: + remainT += c + if remainT: + used = float("inf") + for s in stickCount: + if remainT[0] not in s: + continue + used = min(used, dfs(remainT, s.copy())) + dp[remainT] = used + res += used + return res + + res = dfs(target, {}) + return res if res != float("inf") else -1 +``` + +```java +public class Solution { + private List> stickCount; + private Map dp; + + public int minStickers(String[] stickers, String target) { + stickCount = new ArrayList<>(); + dp = new HashMap<>(); + + for (String s : stickers) { + Map countMap = new HashMap<>(); + for (char c : s.toCharArray()) { + countMap.put(c, countMap.getOrDefault(c, 0) + 1); + } + stickCount.add(countMap); + } + + int res = dfs(target, new HashMap<>()); + return res == Integer.MAX_VALUE ? -1 : res; + } + + private int dfs(String t, Map stick) { + if (t.isEmpty()) return 0; + if (dp.containsKey(t)) return dp.get(t); + + int res = stick.isEmpty() ? 0 : 1; + StringBuilder remainT = new StringBuilder(); + + for (char c : t.toCharArray()) { + if (stick.containsKey(c) && stick.get(c) > 0) { + stick.put(c, stick.get(c) - 1); + } else { + remainT.append(c); + } + } + + if (remainT.length() > 0) { + int used = Integer.MAX_VALUE; + for (Map s : stickCount) { + if (!s.containsKey(remainT.charAt(0))) continue; + int curr = dfs(remainT.toString(), new HashMap<>(s)); + if (curr != Integer.MAX_VALUE) { + used = Math.min(used, curr); + } + } + dp.put(remainT.toString(), used); + if (used != Integer.MAX_VALUE && res != Integer.MAX_VALUE) { + res += used; + } else { + res = Integer.MAX_VALUE; + } + } + + return res; + } +} +``` + +```cpp +class Solution { + vector> stickCount; + unordered_map dp; + +public: + int minStickers(vector& stickers, string target) { + stickCount.clear(); + dp.clear(); + + for (const string& s : stickers) { + unordered_map countMap; + for (char c : s) { + countMap[c]++; + } + stickCount.push_back(countMap); + } + + int res = dfs(target, unordered_map()); + return res == INT_MAX ? -1 : res; + } + +private: + int dfs(const string& t, unordered_map stick) { + if (t.empty()) return 0; + if (dp.count(t)) return dp[t]; + + int res = stick.empty() ? 0 : 1; + string remainT; + + for (char c : t) { + if (stick.count(c) && stick[c] > 0) { + stick[c]--; + } else { + remainT += c; + } + } + + if (!remainT.empty()) { + int used = INT_MAX; + for (const auto& s : stickCount) { + if (!s.count(remainT[0])) continue; + int curr = dfs(remainT, unordered_map(s)); + if (curr != INT_MAX) { + used = min(used, curr); + } + } + dp[remainT] = used; + if (used != INT_MAX && res != INT_MAX) { + res += used; + } else { + res = INT_MAX; + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} stickers + * @param {string} target + * @return {number} + */ + minStickers(stickers, target) { + const stickCount = []; + const dp = new Map(); + + for (const s of stickers) { + const countMap = new Map(); + for (const c of s) { + countMap.set(c, (countMap.get(c) || 0) + 1); + } + stickCount.push(countMap); + } + + const dfs = (t, stick) => { + if (t === '') return 0; + if (dp.has(t)) return dp.get(t); + + let res = stick.size === 0 ? 0 : 1; + let remainT = ''; + + for (const c of t) { + if (stick.has(c) && stick.get(c) > 0) { + stick.set(c, stick.get(c) - 1); + } else { + remainT += c; + } + } + + if (remainT.length > 0) { + let used = Infinity; + for (const s of stickCount) { + if (!s.has(remainT[0])) continue; + const newStick = new Map(s); + const curr = dfs(remainT, newStick); + used = Math.min(used, curr); + } + dp.set(remainT, used); + if (used !== Infinity && res !== Infinity) { + res += used; + } else { + res = Infinity; + } + } + + return res; + }; + + const res = dfs(target, new Map()); + return res === Infinity ? -1 : res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * k *2 ^ n)$ +* Space complexity: $O(m * k + 2 ^ n)$ + +> Where $n$ is the length of the target string, $m$ is the number of stickers and $k$ is the average length of each sticker. + +--- + +## 2. Dynamic Programming (Top-Down) - II + +::tabs-start + +```python +class Solution: + def minStickers(self, stickers: List[str], target: str) -> int: + tmp = [c for c in target] + target = ''.join(sorted(tmp)) + stickCount = [] + for s in stickers: + stickCount.append(Counter(s)) + + dp = {} + dp[""] = 0 + + def dfs(t): + if t in dp: + return dp[t] + + tarMp = Counter(t) + res = float("inf") + for s in stickCount: + if t[0] not in s: + continue + remainT = [] + for c in tarMp: + if tarMp[c] > s[c]: + remainT.extend([c] * (tarMp[c] - s[c])) + + remainT = ''.join(sorted(remainT)) + res = min(res, 1 + dfs(remainT)) + + dp[t] = res + return res + + ans = dfs(target) + return -1 if ans == float("inf") else ans +``` + +```java +public class Solution { + private Map dp = new HashMap<>(); + private List> stickCount = new ArrayList<>(); + + public int minStickers(String[] stickers, String target) { + dp.put("", 0); + for (String s : stickers) { + Map counter = new HashMap<>(); + for (char c : s.toCharArray()) { + counter.put(c, counter.getOrDefault(c, 0) + 1); + } + stickCount.add(counter); + } + char[] targetArray = target.toCharArray(); + Arrays.sort(targetArray); + target = new String(targetArray); + + int ans = dfs(target); + return ans == Integer.MAX_VALUE ? -1 : ans; + } + + private int dfs(String t) { + if (dp.containsKey(t)) { + return dp.get(t); + } + + Map tarMp = new HashMap<>(); + for (char c : t.toCharArray()) { + tarMp.put(c, tarMp.getOrDefault(c, 0) + 1); + } + + int res = Integer.MAX_VALUE; + for (Map s : stickCount) { + if (!s.containsKey(t.charAt(0))) { + continue; + } + + StringBuilder remainT = new StringBuilder(); + for (Map.Entry entry : tarMp.entrySet()) { + char c = entry.getKey(); + int need = entry.getValue() - s.getOrDefault(c, 0); + for (int i = 0; i < Math.max(0, need); i++) { + remainT.append(c); + } + } + char[] remainArray = remainT.toString().toCharArray(); + Arrays.sort(remainArray); + int cur = dfs(new String(remainArray)); + if (cur == Integer.MAX_VALUE) cur--; + res = Math.min(res, 1 + cur); + } + + dp.put(t, res); + return res; + } +} +``` + +```cpp +class Solution { +private: + unordered_map dp; + vector> stickCount; + +public: + int minStickers(vector& stickers, string target) { + dp[""] = 0; + for (const string& s : stickers) { + unordered_map counter; + for (char c : s) { + counter[c]++; + } + stickCount.push_back(counter); + } + + sort(target.begin(), target.end()); + int ans = dfs(target); + return ans == INT_MAX ? -1 : ans; + } + + int dfs(const string& t) { + if (dp.find(t) != dp.end()) { + return dp[t]; + } + + unordered_map tarMp; + for (char c : t) { + tarMp[c]++; + } + + int res = INT_MAX; + for (auto& s : stickCount) { + if (s.find(t[0]) == s.end()) { + continue; + } + + string remainT; + for (const auto& [c, count] : tarMp) { + int need = count - (s.count(c) ? s.at(c) : 0); + remainT.append(max(0, need), c); + } + + sort(remainT.begin(), remainT.end()); + int cur = dfs(remainT); + if (cur == INT_MAX) cur--; + res = min(res, 1 + cur); + } + + dp[t] = res; + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} stickers + * @param {string} target + * @return {number} + */ + minStickers(stickers, target) { + const dp = { "": 0 }; + const stickCount = stickers.map((s) => { + const counter = {}; + for (const c of s) { + counter[c] = (counter[c] || 0) + 1; + } + return counter; + }); + + const dfs = (t) => { + if (dp[t] !== undefined) return dp[t]; + + const tarMp = {}; + for (const c of t) { + tarMp[c] = (tarMp[c] || 0) + 1; + } + + let res = Infinity; + for (const s of stickCount) { + if (!s[t[0]]) continue; + + let remainT = []; + for (const [c, count] of Object.entries(tarMp)) { + let need = count - (s[c] || 0); + while (need > 0) { + remainT.push(c); + need--; + } + } + + remainT = remainT.sort().join(""); + res = Math.min(res, 1 + dfs(remainT)); + } + + dp[t] = res; + return res; + }; + + const ans = dfs(target.split("").sort().join("")); + return ans === Infinity ? -1 : ans; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * k *2 ^ n)$ +* Space complexity: $O(m * k + 2 ^ n)$ + +> Where $n$ is the length of the target string, $m$ is the number of stickers and $k$ is the average length of each sticker. + +--- + +## 3. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def minStickers(self, stickers: List[str], target: str) -> int: + n = len(target) + N = 1 << n + dp = [-1] * N + dp[0] = 0 + + for t in range(N): + if dp[t] == -1: + continue + for s in stickers: + nextT = t + for c in s: + for i in range(n): + if target[i] == c and not ((nextT >> i) & 1): + nextT |= 1 << i + break + if dp[nextT] == -1 or dp[nextT] > dp[t] + 1: + dp[nextT] = dp[t] + 1 + return dp[N - 1] +``` + +```java +public class Solution { + public int minStickers(String[] stickers, String target) { + int n = target.length(); + int N = 1 << n; + int[] dp = new int[N]; + Arrays.fill(dp, -1); + dp[0] = 0; + + for (int t = 0; t < N; t++) { + if (dp[t] == -1) continue; + for (String s : stickers) { + int nextT = t; + for (char c : s.toCharArray()) { + for (int i = 0; i < n; i++) { + if (target.charAt(i) == c && ((nextT >> i) & 1) == 0) { + nextT |= 1 << i; + break; + } + } + } + if (dp[nextT] == -1 || dp[nextT] > dp[t] + 1) { + dp[nextT] = dp[t] + 1; + } + } + } + + return dp[N - 1]; + } +} +``` + +```cpp +class Solution { +public: + int minStickers(vector& stickers, string target) { + int n = target.length(); + int N = 1 << n; + vector dp(N, -1); + dp[0] = 0; + + for (int t = 0; t < N; t++) { + if (dp[t] == -1) continue; + for (string& s : stickers) { + int nextT = t; + for (char c : s) { + for (int i = 0; i < n; i++) { + if (target[i] == c && ((nextT >> i) & 1) == 0) { + nextT |= 1 << i; + break; + } + } + } + if (dp[nextT] == -1 || dp[nextT] > dp[t] + 1) { + dp[nextT] = dp[t] + 1; + } + } + } + + return dp[N - 1]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} stickers + * @param {string} target + * @return {number} + */ + minStickers(stickers, target) { + const n = target.length; + const N = 1 << n; + const dp = Array(N).fill(-1); + dp[0] = 0; + + for (let t = 0; t < N; t++) { + if (dp[t] === -1) continue; + for (let s of stickers) { + let nextT = t; + for (let c of s) { + for (let i = 0; i < n; i++) { + if (target[i] === c && ((nextT >> i) & 1) === 0) { + nextT |= 1 << i; + break; + } + } + } + if (dp[nextT] === -1 || dp[nextT] > dp[t] + 1) { + dp[nextT] = dp[t] + 1; + } + } + } + + return dp[N - 1]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * m * k *2 ^ n)$ +* Space complexity: $O(2 ^ n)$ + +> Where $n$ is the length of the target string, $m$ is the number of stickers and $k$ is the average length of each sticker. \ No newline at end of file diff --git a/articles/stone-game.md b/articles/stone-game.md new file mode 100644 index 000000000..54e5043c1 --- /dev/null +++ b/articles/stone-game.md @@ -0,0 +1,525 @@ +## 1. Recursion + +::tabs-start + +```python +class Solution: + def stoneGame(self, piles: List[int]) -> bool: + def dfs(l, r): + if l > r: + return 0 + even = (r - l) % 2 == 0 + left = piles[l] if even else 0 + right = piles[r] if even else 0 + return max(dfs(l + 1, r) + left, dfs(l, r - 1) + right) + + total = sum(piles) + alice_score = dfs(0, len(piles) - 1) + return alice_score > total - alice_score +``` + +```java +public class Solution { + public boolean stoneGame(int[] piles) { + int total = 0; + for (int pile : piles) { + total += pile; + } + + int aliceScore = dfs(0, piles.length - 1, piles); + return aliceScore > total - aliceScore; + } + + private int dfs(int l, int r, int[] piles) { + if (l > r) { + return 0; + } + boolean even = (r - l) % 2 == 0; + int left = even ? piles[l] : 0; + int right = even ? piles[r] : 0; + return Math.max(dfs(l + 1, r, piles) + left, dfs(l, r - 1, piles) + right); + } +} +``` + +```cpp +class Solution { +public: + bool stoneGame(vector& piles) { + int total = accumulate(piles.begin(), piles.end(), 0); + int aliceScore = dfs(0, piles.size() - 1, piles); + return aliceScore > total - aliceScore; + } + +private: + int dfs(int l, int r, const vector& piles) { + if (l > r) { + return 0; + } + bool even = (r - l) % 2 == 0; + int left = even ? piles[l] : 0; + int right = even ? piles[r] : 0; + return max(dfs(l + 1, r, piles) + left, dfs(l, r - 1, piles) + right); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} piles + * @return {boolean} + */ + stoneGame(piles) { + const total = piles.reduce((a, b) => a + b, 0); + + const dfs = (l, r) => { + if (l > r) { + return 0; + } + const even = (r - l) % 2 === 0; + const left = even ? piles[l] : 0; + const right = even ? piles[r] : 0; + return Math.max(dfs(l + 1, r) + left, dfs(l, r - 1) + right); + }; + + const aliceScore = dfs(0, piles.length - 1); + return aliceScore > total - aliceScore; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(2 ^ n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def stoneGame(self, piles: List[int]) -> bool: + dp = {} + + def dfs(l, r): + if l > r: + return 0 + if (l, r) in dp: + return dp[(l, r)] + even = (r - l) % 2 == 0 + left = piles[l] if even else 0 + right = piles[r] if even else 0 + dp[(l, r)] = max(dfs(l + 1, r) + left, dfs(l, r - 1) + right) + return dp[(l, r)] + + total = sum(piles) + alice_score = dfs(0, len(piles) - 1) + return alice_score > total - alice_score +``` + +```java +public class Solution { + private int[][] dp; + + public boolean stoneGame(int[] piles) { + int n = piles.length; + dp = new int[n][n]; + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { + dp[i][j] = -1; + } + } + + int total = 0; + for (int pile : piles) { + total += pile; + } + + int aliceScore = dfs(0, n - 1, piles); + return aliceScore > total - aliceScore; + } + + private int dfs(int l, int r, int[] piles) { + if (l > r) { + return 0; + } + if (dp[l][r] != -1) { + return dp[l][r]; + } + boolean even = (r - l) % 2 == 0; + int left = even ? piles[l] : 0; + int right = even ? piles[r] : 0; + dp[l][r] = Math.max(dfs(l + 1, r, piles) + left, dfs(l, r - 1, piles) + right); + return dp[l][r]; + } +} +``` + +```cpp +class Solution { +private: + vector> dp; + +public: + bool stoneGame(vector& piles) { + int n = piles.size(); + dp = vector>(n, vector(n, -1)); + int total = accumulate(piles.begin(), piles.end(), 0); + int aliceScore = dfs(0, n - 1, piles); + return aliceScore > total - aliceScore; + } + +private: + int dfs(int l, int r, const vector& piles) { + if (l > r) { + return 0; + } + if (dp[l][r] != -1) { + return dp[l][r]; + } + bool even = (r - l) % 2 == 0; + int left = even ? piles[l] : 0; + int right = even ? piles[r] : 0; + dp[l][r] = max(dfs(l + 1, r, piles) + left, dfs(l, r - 1, piles) + right); + return dp[l][r]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} piles + * @return {boolean} + */ + stoneGame(piles) { + const n = piles.length; + const dp = Array.from({ length: n }, () => Array(n).fill(-1)); + + const dfs = (l, r) => { + if (l > r) { + return 0; + } + if (dp[l][r] !== -1) { + return dp[l][r]; + } + const even = (r - l) % 2 === 0; + const left = even ? piles[l] : 0; + const right = even ? piles[r] : 0; + dp[l][r] = Math.max(dfs(l + 1, r) + left, dfs(l, r - 1) + right); + return dp[l][r]; + }; + + const total = piles.reduce((a, b) => a + b, 0); + const aliceScore = dfs(0, n - 1); + return aliceScore > total - aliceScore; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n ^ 2)$ + +--- + +## 3. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def stoneGame(self, piles: List[int]) -> bool: + n = len(piles) + dp = [[0] * n for _ in range(n)] + + for l in range(n - 1, -1, -1): + for r in range(l, n): + even = (r - l) % 2 == 0 + left = piles[l] if even else 0 + right = piles[r] if even else 0 + if l == r: + dp[l][r] = left + else: + dp[l][r] = max(dp[l + 1][r] + left, dp[l][r - 1] + right) + + total = sum(piles) + alice_score = dp[0][n - 1] + return alice_score > total - alice_score +``` + +```java +public class Solution { + public boolean stoneGame(int[] piles) { + int n = piles.length; + int[][] dp = new int[n][n]; + + for (int l = n - 1; l >= 0; l--) { + for (int r = l; r < n; r++) { + boolean even = (r - l) % 2 == 0; + int left = even ? piles[l] : 0; + int right = even ? piles[r] : 0; + if (l == r) { + dp[l][r] = left; + } else { + dp[l][r] = Math.max(dp[l + 1][r] + left, dp[l][r - 1] + right); + } + } + } + + int total = 0; + for (int pile : piles) { + total += pile; + } + + int aliceScore = dp[0][n - 1]; + return aliceScore > total - aliceScore; + } +} +``` + +```cpp +class Solution { +public: + bool stoneGame(vector& piles) { + int n = piles.size(); + vector> dp(n, vector(n, 0)); + + for (int l = n - 1; l >= 0; l--) { + for (int r = l; r < n; r++) { + bool even = (r - l) % 2 == 0; + int left = even ? piles[l] : 0; + int right = even ? piles[r] : 0; + if (l == r) { + dp[l][r] = left; + } else { + dp[l][r] = max(dp[l + 1][r] + left, dp[l][r - 1] + right); + } + } + } + + int total = accumulate(piles.begin(), piles.end(), 0); + int aliceScore = dp[0][n - 1]; + return aliceScore > total - aliceScore; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} piles + * @return {boolean} + */ + stoneGame(piles) { + const n = piles.length; + const dp = Array.from({ length: n }, () => Array(n).fill(0)); + + for (let l = n - 1; l >= 0; l--) { + for (let r = l; r < n; r++) { + const even = (r - l) % 2 === 0; + const left = even ? piles[l] : 0; + const right = even ? piles[r] : 0; + if (l === r) { + dp[l][r] = left; + } else { + dp[l][r] = Math.max(dp[l + 1][r] + left, dp[l][r - 1] + right); + } + } + } + + const total = piles.reduce((a, b) => a + b, 0); + const aliceScore = dp[0][n - 1]; + return aliceScore > total - aliceScore; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n ^ 2)$ + +--- + +## 4. Dynamic Programming (Space Optimized) + +::tabs-start + +```python +class Solution: + def stoneGame(self, piles: List[int]) -> bool: + n = len(piles) + dp = [0] * n + + for l in reversed(range(n)): + for r in range(l, n): + even = ((r - l) % 2 == 0) + left = piles[l] if even else 0 + right = piles[r] if even else 0 + + if l == r: + dp[r] = left + else: + dp[r] = max(dp[r] + left, dp[r - 1] + right) + + total = sum(piles) + alice_score = dp[n - 1] + return alice_score > (total - alice_score) +``` + +```java +public class Solution { + public boolean stoneGame(int[] piles) { + int n = piles.length; + int[] dp = new int[n]; + + for (int l = n - 1; l >= 0; l--) { + for (int r = l; r < n; r++) { + boolean even = (r - l) % 2 == 0; + int left = even ? piles[l] : 0; + int right = even ? piles[r] : 0; + + if (l == r) { + dp[r] = left; + } else { + dp[r] = Math.max(dp[r] + left, dp[r - 1] + right); + } + } + } + + int total = 0; + for (int pile : piles) { + total += pile; + } + + int aliceScore = dp[n - 1]; + return aliceScore > (total - aliceScore); + } +} +``` + +```cpp +class Solution { +public: + bool stoneGame(vector& piles) { + int n = piles.size(); + vector dp(n, 0); + + for (int l = n - 1; l >= 0; l--) { + for (int r = l; r < n; r++) { + bool even = (r - l) % 2 == 0; + int left = even ? piles[l] : 0; + int right = even ? piles[r] : 0; + + if (l == r) { + dp[r] = left; + } else { + dp[r] = max(dp[r] + left, dp[r - 1] + right); + } + } + } + + int total = accumulate(piles.begin(), piles.end(), 0); + int aliceScore = dp[n - 1]; + return aliceScore > (total - aliceScore); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} piles + * @return {boolean} + */ + stoneGame(piles) { + const n = piles.length; + const dp = new Array(n).fill(0); + + for (let l = n - 1; l >= 0; l--) { + for (let r = l; r < n; r++) { + const even = (r - l) % 2 === 0; + const left = even ? piles[l] : 0; + const right = even ? piles[r] : 0; + + if (l === r) { + dp[r] = left; + } else { + dp[r] = Math.max(dp[r] + left, dp[r - 1] + right); + } + } + } + + const total = piles.reduce((a, b) => a + b, 0); + const aliceScore = dp[n - 1]; + return aliceScore > (total - aliceScore); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n)$ + +--- + +## 5. Return TRUE + +::tabs-start + +```python +class Solution: + def stoneGame(self, piles: List[int]) -> bool: + return True +``` + +```java +public class Solution { + public boolean stoneGame(int[] piles) { + return true; + } +} +``` + +```cpp +class Solution { +public: + bool stoneGame(vector& piles) { + return true; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} piles + * @return {boolean} + */ + stoneGame(piles) { + return true; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(1)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/uncrossed-lines.md b/articles/uncrossed-lines.md new file mode 100644 index 000000000..a08442f64 --- /dev/null +++ b/articles/uncrossed-lines.md @@ -0,0 +1,560 @@ +## 1. Recursion + +::tabs-start + +```python +class Solution: + def maxUncrossedLines(self, nums1: List[int], nums2: List[int]) -> int: + def dfs(i, j): + if i == len(nums1) or j == len(nums2): + return 0 + + if nums1[i] == nums2[j]: + return 1 + dfs(i + 1, j + 1) + return max(dfs(i, j + 1), dfs(i + 1, j)) + + return dfs(0, 0) +``` + +```java +public class Solution { + public int maxUncrossedLines(int[] nums1, int[] nums2) { + int n1 = nums1.length, n2 = nums2.length; + + return dfs(nums1, nums2, 0, 0); + } + + private int dfs(int[] nums1, int[] nums2, int i, int j) { + if (i == nums1.length || j == nums2.length) return 0; + + if (nums1[i] == nums2[j]) { + return 1 + dfs(nums1, nums2, i + 1, j + 1); + } + return Math.max(dfs(nums1, nums2, i, j + 1), dfs(nums1, nums2, i + 1, j)); + } +} +``` + +```cpp +class Solution { +public: + int dfs(vector& nums1, vector& nums2, int i, int j) { + if (i == nums1.size() || j == nums2.size()) return 0; + + if (nums1[i] == nums2[j]) { + return 1 + dfs(nums1, nums2, i + 1, j + 1); + } + return max(dfs(nums1, nums2, i, j + 1), dfs(nums1, nums2, i + 1, j)); + } + + int maxUncrossedLines(vector& nums1, vector& nums2) { + return dfs(nums1, nums2, 0, 0); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums1 + * @param {number[]} nums2 + * @return {number} + */ + maxUncrossedLines(nums1, nums2) { + const dfs = (i, j) => { + if (i === nums1.length || j === nums2.length) { + return 0; + } + if (nums1[i] === nums2[j]) { + return 1 + dfs(i + 1, j + 1); + } + return Math.max(dfs(i, j + 1), dfs(i + 1, j)); + }; + + return dfs(0, 0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(2 ^ {n + m})$ +* Space complexity: $O(n + m)$ for recursion stack. + +> Where $n$ and $m$ are the sizes of the arrays $nums1$ and $nums2$ respectively. + +--- + +## 2. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def maxUncrossedLines(self, nums1: List[int], nums2: List[int]) -> int: + dp = {} + + def dfs(i, j): + if i == len(nums1) or j == len(nums2): + return 0 + + if (i, j) in dp: + return dp[(i, j)] + + if nums1[i] == nums2[j]: + dp[(i, j)] = 1 + dfs(i + 1, j + 1) + else: + dp[(i, j)] = max(dfs(i, j + 1), dfs(i + 1, j)) + + return dp[(i, j)] + + return dfs(0, 0) +``` + +```java +public class Solution { + public int maxUncrossedLines(int[] nums1, int[] nums2) { + int n = nums1.length, m = nums2.length; + int[][] dp = new int[n][m]; + + for (int[] row : dp) { + Arrays.fill(row, -1); + } + + return dfs(0, 0, nums1, nums2, dp); + } + + private int dfs(int i, int j, int[] nums1, int[] nums2, int[][] dp) { + if (i == nums1.length || j == nums2.length) { + return 0; + } + + if (dp[i][j] != -1) { + return dp[i][j]; + } + + if (nums1[i] == nums2[j]) { + dp[i][j] = 1 + dfs(i + 1, j + 1, nums1, nums2, dp); + } else { + dp[i][j] = Math.max(dfs(i, j + 1, nums1, nums2, dp), dfs(i + 1, j, nums1, nums2, dp)); + } + + return dp[i][j]; + } +} +``` + +```cpp +class Solution { +public: + int maxUncrossedLines(vector& nums1, vector& nums2) { + int n = nums1.size(), m = nums2.size(); + vector> dp(n, vector(m, -1)); + + return dfs(0, 0, nums1, nums2, dp); + } + +private: + int dfs(int i, int j, vector& nums1, vector& nums2, vector>& dp) { + if (i == nums1.size() || j == nums2.size()) { + return 0; + } + + if (dp[i][j] != -1) { + return dp[i][j]; + } + + if (nums1[i] == nums2[j]) { + dp[i][j] = 1 + dfs(i + 1, j + 1, nums1, nums2, dp); + } else { + dp[i][j] = max(dfs(i, j + 1, nums1, nums2, dp), dfs(i + 1, j, nums1, nums2, dp)); + } + + return dp[i][j]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums1 + * @param {number[]} nums2 + * @return {number} + */ + maxUncrossedLines(nums1, nums2) { + const n = nums1.length, m = nums2.length; + const dp = Array.from({ length: n }, () => Array(m).fill(-1)); + + const dfs = (i, j) => { + if (i === n || j === m) { + return 0; + } + + if (dp[i][j] !== -1) { + return dp[i][j]; + } + + if (nums1[i] === nums2[j]) { + dp[i][j] = 1 + dfs(i + 1, j + 1); + } else { + dp[i][j] = Math.max(dfs(i, j + 1), dfs(i + 1, j)); + } + + return dp[i][j]; + }; + + return dfs(0, 0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * m)$ +* Space complexity: $O(n * m)$ + +> Where $n$ and $m$ are the sizes of the arrays $nums1$ and $nums2$ respectively. + +--- + +## 3. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def maxUncrossedLines(self, nums1: List[int], nums2: List[int]) -> int: + n, m = len(nums1), len(nums2) + dp = [[0] * (m + 1) for _ in range(n + 1)] + + for i in range(n): + for j in range(m): + if nums1[i] == nums2[j]: + dp[i + 1][j + 1] = 1 + dp[i][j] + else: + dp[i + 1][j + 1] = max(dp[i][j + 1], dp[i + 1][j]) + + return dp[n][m] +``` + +```java +public class Solution { + public int maxUncrossedLines(int[] nums1, int[] nums2) { + int n = nums1.length, m = nums2.length; + int[][] dp = new int[n + 1][m + 1]; + + for (int i = 0; i < n; i++) { + for (int j = 0; j < m; j++) { + if (nums1[i] == nums2[j]) { + dp[i + 1][j + 1] = 1 + dp[i][j]; + } else { + dp[i + 1][j + 1] = Math.max(dp[i][j + 1], dp[i + 1][j]); + } + } + } + + return dp[n][m]; + } +} +``` + +```cpp +class Solution { +public: + int maxUncrossedLines(vector& nums1, vector& nums2) { + int n = nums1.size(), m = nums2.size(); + vector> dp(n + 1, vector(m + 1, 0)); + + for (int i = 0; i < n; i++) { + for (int j = 0; j < m; j++) { + if (nums1[i] == nums2[j]) { + dp[i + 1][j + 1] = 1 + dp[i][j]; + } else { + dp[i + 1][j + 1] = max(dp[i][j + 1], dp[i + 1][j]); + } + } + } + + return dp[n][m]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums1 + * @param {number[]} nums2 + * @return {number} + */ + maxUncrossedLines(nums1, nums2) { + const n = nums1.length, m = nums2.length; + const dp = Array.from({ length: n + 1 }, () => Array(m + 1).fill(0)); + + for (let i = 0; i < n; i++) { + for (let j = 0; j < m; j++) { + if (nums1[i] === nums2[j]) { + dp[i + 1][j + 1] = 1 + dp[i][j]; + } else { + dp[i + 1][j + 1] = Math.max(dp[i][j + 1], dp[i + 1][j]); + } + } + } + + return dp[n][m]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * m)$ +* Space complexity: $O(n * m)$ + +> Where $n$ and $m$ are the sizes of the arrays $nums1$ and $nums2$ respectively. + +--- + +## 4. Dynamic Programming (Space Optimized) + +::tabs-start + +```python +class Solution: + def maxUncrossedLines(self, nums1: List[int], nums2: List[int]) -> int: + prev = [0] * (len(nums2) + 1) + + for i in range(len(nums1)): + dp = [0] * (len(nums2) + 1) + for j in range(len(nums2)): + if nums1[i] == nums2[j]: + dp[j + 1] = 1 + prev[j] + else: + dp[j + 1] = max(dp[j], prev[j + 1]) + prev = dp + + return prev[len(nums2)] +``` + +```java +public class Solution { + public int maxUncrossedLines(int[] nums1, int[] nums2) { + int[] prev = new int[nums2.length + 1]; + + for (int i = 0; i < nums1.length; i++) { + int[] dp = new int[nums2.length + 1]; + for (int j = 0; j < nums2.length; j++) { + if (nums1[i] == nums2[j]) { + dp[j + 1] = 1 + prev[j]; + } else { + dp[j + 1] = Math.max(dp[j], prev[j + 1]); + } + } + prev = dp; + } + + return prev[nums2.length]; + } +} +``` + +```cpp +class Solution { +public: + int maxUncrossedLines(vector& nums1, vector& nums2) { + vector prev(nums2.size() + 1, 0); + + for (int i = 0; i < nums1.size(); i++) { + vector dp(nums2.size() + 1, 0); + for (int j = 0; j < nums2.size(); j++) { + if (nums1[i] == nums2[j]) { + dp[j + 1] = 1 + prev[j]; + } else { + dp[j + 1] = max(dp[j], prev[j + 1]); + } + } + prev = dp; + } + + return prev[nums2.size()]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums1 + * @param {number[]} nums2 + * @return {number} + */ + maxUncrossedLines(nums1, nums2) { + let prev = new Array(nums2.length + 1).fill(0); + + for (let i = 0; i < nums1.length; i++) { + const dp = new Array(nums2.length + 1).fill(0); + for (let j = 0; j < nums2.length; j++) { + if (nums1[i] === nums2[j]) { + dp[j + 1] = 1 + prev[j]; + } else { + dp[j + 1] = Math.max(dp[j], prev[j + 1]); + } + } + prev = dp; + } + + return prev[nums2.length]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * m)$ +* Space complexity: $O(m)$ + +> Where $n$ and $m$ are the sizes of the arrays $nums1$ and $nums2$ respectively. + +--- + +## 5. Dynamic Programming (Optimal) + +::tabs-start + +```python +class Solution: + def maxUncrossedLines(self, nums1: List[int], nums2: List[int]) -> int: + n, m = len(nums1), len(nums2) + if m > n: + n, m = m, n + nums1, nums2 = nums2, nums1 + + dp = [0] * (m + 1) + + for i in range(n): + prev = 0 + for j in range(m): + temp = dp[j + 1] + if nums1[i] == nums2[j]: + dp[j + 1] = 1 + prev + else: + dp[j + 1] = max(dp[j + 1], dp[j]) + prev = temp + + return dp[m] +``` + +```java +public class Solution { + public int maxUncrossedLines(int[] nums1, int[] nums2) { + int n = nums1.length, m = nums2.length; + if (m > n) { + int[] tempArr = nums1; + nums1 = nums2; + nums2 = tempArr; + int temp = n; + n = m; + m = temp; + } + + int[] dp = new int[m + 1]; + + for (int i = 0; i < n; i++) { + int prev = 0; + for (int j = 0; j < m; j++) { + int temp = dp[j + 1]; + if (nums1[i] == nums2[j]) { + dp[j + 1] = 1 + prev; + } else { + dp[j + 1] = Math.max(dp[j + 1], dp[j]); + } + prev = temp; + } + } + + return dp[m]; + } +} +``` + +```cpp +class Solution { +public: + int maxUncrossedLines(vector& nums1, vector& nums2) { + int n = nums1.size(), m = nums2.size(); + if (m > n) { + swap(nums1, nums2); + swap(n, m); + } + + vector dp(m + 1, 0); + + for (int i = 0; i < n; i++) { + int prev = 0; + for (int j = 0; j < m; j++) { + int temp = dp[j + 1]; + if (nums1[i] == nums2[j]) { + dp[j + 1] = 1 + prev; + } else { + dp[j + 1] = max(dp[j + 1], dp[j]); + } + prev = temp; + } + } + + return dp[m]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums1 + * @param {number[]} nums2 + * @return {number} + */ + maxUncrossedLines(nums1, nums2) { + let n = nums1.length, m = nums2.length; + if (m > n) { + [nums1, nums2] = [nums2, nums1]; + [n, m] = [m, n]; + } + + const dp = Array(m + 1).fill(0); + + for (let i = 0; i < n; i++) { + let prev = 0; + for (let j = 0; j < m; j++) { + const temp = dp[j + 1]; + if (nums1[i] === nums2[j]) { + dp[j + 1] = 1 + prev; + } else { + dp[j + 1] = Math.max(dp[j + 1], dp[j]); + } + prev = temp; + } + } + + return dp[m]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * m)$ +* Space complexity: $O(min(n, m))$ + +> Where $n$ and $m$ are the sizes of the arrays $nums1$ and $nums2$ respectively. \ No newline at end of file diff --git a/articles/unique-paths-ii.md b/articles/unique-paths-ii.md new file mode 100644 index 000000000..35178ff44 --- /dev/null +++ b/articles/unique-paths-ii.md @@ -0,0 +1,481 @@ +## 1. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def uniquePathsWithObstacles(self, grid: List[List[int]]) -> int: + M, N = len(grid), len(grid[0]) + dp = {(M - 1, N - 1): 1} + + def dfs(r, c): + if r == M or c == N or grid[r][c]: + return 0 + if (r, c) in dp: + return dp[(r, c)] + dp[(r, c)] = dfs(r + 1, c) + dfs(r, c + 1) + return dp[(r, c)] + + return dfs(0, 0) +``` + +```java +public class Solution { + private int[][] dp; + + public int uniquePathsWithObstacles(int[][] grid) { + int M = grid.length, N = grid[0].length; + dp = new int[M][N]; + for (int i = 0; i < M; i++) { + for (int j = 0; j < N; j++) { + dp[i][j] = -1; + } + } + return dfs(0, 0, grid, M, N); + } + + private int dfs(int r, int c, int[][] grid, int M, int N) { + if (r == M || c == N || grid[r][c] == 1) { + return 0; + } + if (r == M - 1 && c == N - 1) { + return 1; + } + if (dp[r][c] != -1) { + return dp[r][c]; + } + dp[r][c] = dfs(r + 1, c, grid, M, N) + dfs(r, c + 1, grid, M, N); + return dp[r][c]; + } +} +``` + +```cpp +class Solution { +private: + vector> dp; + +public: + int uniquePathsWithObstacles(vector>& grid) { + int M = grid.size(), N = grid[0].size(); + dp.resize(M, vector(N, -1)); + return dfs(0, 0, grid, M, N); + } + +private: + int dfs(int r, int c, vector>& grid, int M, int N) { + if (r == M || c == N || grid[r][c] == 1) { + return 0; + } + if (r == M - 1 && c == N - 1) { + return 1; + } + if (dp[r][c] != -1) { + return dp[r][c]; + } + dp[r][c] = dfs(r + 1, c, grid, M, N) + dfs(r, c + 1, grid, M, N); + return dp[r][c]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} grid + * @return {number} + */ + uniquePathsWithObstacles(grid) { + const M = grid.length, N = grid[0].length; + const dp = Array.from({ length: M }, () => Array(N).fill(-1)); + + const dfs = (r, c) => { + if (r === M || c === N || grid[r][c] === 1) { + return 0; + } + if (r === M - 1 && c === N - 1) { + return 1; + } + if (dp[r][c] !== -1) { + return dp[r][c]; + } + dp[r][c] = dfs(r + 1, c) + dfs(r, c + 1); + return dp[r][c]; + }; + + return dfs(0, 0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n)$ +* Space complexity: $O(m * n)$ + +> Where $m$ is the number of rows and $n$ is the number of columns. + +--- + +## 2. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def uniquePathsWithObstacles(self, grid: List[List[int]]) -> int: + M, N = len(grid), len(grid[0]) + if grid[0][0] == 1 or grid[M - 1][N - 1] == 1: + return 0 + dp = [[0] * (N + 1) for _ in range(M + 1)] + + + dp[M - 1][N - 1] = 1 + + for r in range(M - 1, -1, -1): + for c in range(N - 1, -1, -1): + if grid[r][c] == 1: + dp[r][c] = 0 + else: + dp[r][c] += dp[r + 1][c] + dp[r][c] += dp[r][c + 1] + + return dp[0][0] +``` + +```java +public class Solution { + public int uniquePathsWithObstacles(int[][] grid) { + int M = grid.length, N = grid[0].length; + if (grid[0][0] == 1 || grid[M - 1][N - 1] == 1) { + return 0; + } + + int[][] dp = new int[M + 1][N + 1]; + dp[M - 1][N - 1] = 1; + + for (int r = M - 1; r >= 0; r--) { + for (int c = N - 1; c >= 0; c--) { + if (grid[r][c] == 1) { + dp[r][c] = 0; + } else { + dp[r][c] += dp[r + 1][c]; + dp[r][c] += dp[r][c + 1]; + } + } + } + + return dp[0][0]; + } +} +``` + +```cpp +class Solution { +public: + int uniquePathsWithObstacles(vector>& grid) { + int M = grid.size(), N = grid[0].size(); + if (grid[0][0] == 1 || grid[M - 1][N - 1] == 1) { + return 0; + } + + vector> dp(M + 1, vector(N + 1, 0)); + dp[M - 1][N - 1] = 1; + + for (int r = M - 1; r >= 0; r--) { + for (int c = N - 1; c >= 0; c--) { + if (grid[r][c] == 1) { + dp[r][c] = 0; + } else { + dp[r][c] += dp[r + 1][c]; + dp[r][c] += dp[r][c + 1]; + } + } + } + + return dp[0][0]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} grid + * @return {number} + */ + uniquePathsWithObstacles(grid) { + const M = grid.length, N = grid[0].length; + if (grid[0][0] === 1 || grid[M - 1][N - 1] === 1) { + return 0; + } + + const dp = Array.from({ length: M + 1 }, () => Array(N + 1).fill(0)); + dp[M - 1][N - 1] = 1; + + for (let r = M - 1; r >= 0; r--) { + for (let c = N - 1; c >= 0; c--) { + if (grid[r][c] === 1) { + dp[r][c] = 0; + } else { + dp[r][c] += dp[r + 1][c]; + dp[r][c] += dp[r][c + 1]; + } + } + } + + return dp[0][0]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n)$ +* Space complexity: $O(m * n)$ + +> Where $m$ is the number of rows and $n$ is the number of columns. + +--- + +## 3. Dynamic Programming (Space Optimized) + +::tabs-start + +```python +class Solution: + def uniquePathsWithObstacles(self, grid: List[List[int]]) -> int: + M, N = len(grid), len(grid[0]) + dp = [0] * (N + 1) + dp[N - 1] = 1 + + for r in range(M - 1, -1, -1): + for c in range(N - 1, -1, -1): + if grid[r][c]: + dp[c] = 0 + else: + dp[c] += dp[c + 1] + + return dp[0] +``` + +```java +public class Solution { + public int uniquePathsWithObstacles(int[][] grid) { + int M = grid.length, N = grid[0].length; + int[] dp = new int[N + 1]; + dp[N - 1] = 1; + + for (int r = M - 1; r >= 0; r--) { + for (int c = N - 1; c >= 0; c--) { + if (grid[r][c] == 1) { + dp[c] = 0; + } else { + dp[c] += dp[c + 1]; + } + } + } + + return dp[0]; + } +} +``` + +```cpp +class Solution { +public: + int uniquePathsWithObstacles(vector>& grid) { + int M = grid.size(), N = grid[0].size(); + vector dp(N + 1, 0); + dp[N - 1] = 1; + + for (int r = M - 1; r >= 0; r--) { + for (int c = N - 1; c >= 0; c--) { + if (grid[r][c] == 1) { + dp[c] = 0; + } else { + dp[c] += dp[c + 1]; + } + } + } + + return dp[0]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} grid + * @return {number} + */ + uniquePathsWithObstacles(grid) { + const M = grid.length, N = grid[0].length; + const dp = new Array(N + 1).fill(0); + dp[N - 1] = 1; + + for (let r = M - 1; r >= 0; r--) { + for (let c = N - 1; c >= 0; c--) { + if (grid[r][c] === 1) { + dp[c] = 0; + } else { + dp[c] += dp[c + 1]; + } + } + } + + return dp[0]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n)$ +* Space complexity: $O(n)$ + +> Where $m$ is the number of rows and $n$ is the number of columns. + +--- + +## 4. Dynamic Programming (In-Place) + +::tabs-start + +```python +class Solution: + def uniquePathsWithObstacles(self, grid: List[List[int]]) -> int: + M, N = len(grid), len(grid[0]) + if grid[0][0] == 1 or grid[M - 1][N - 1] == 1: + return 0 + + grid[M - 1][N - 1] = 1 + + for r in range(M - 1, -1, -1): + for c in range(N - 1, -1, -1): + if r == M - 1 and c == N - 1: + continue + + if grid[r][c] == 1: + grid[r][c] = 0 + else: + down = grid[r + 1][c] if r + 1 < M else 0 + right = grid[r][c + 1] if c + 1 < N else 0 + grid[r][c] = down + right + + return grid[0][0] +``` + +```java +public class Solution { + public int uniquePathsWithObstacles(int[][] grid) { + int M = grid.length, N = grid[0].length; + if (grid[0][0] == 1 || grid[M - 1][N - 1] == 1) { + return 0; + } + + grid[M - 1][N - 1] = 1; + + for (int r = M - 1; r >= 0; r--) { + for (int c = N - 1; c >= 0; c--) { + if (r == M - 1 && c == N - 1) { + continue; + } + + if (grid[r][c] == 1) { + grid[r][c] = 0; + } else { + int down = (r + 1 < M) ? grid[r + 1][c] : 0; + int right = (c + 1 < N) ? grid[r][c + 1] : 0; + grid[r][c] = down + right; + } + } + } + + return grid[0][0]; + } +} +``` + +```cpp +class Solution { +public: + int uniquePathsWithObstacles(vector>& grid) { + int M = grid.size(), N = grid[0].size(); + if (grid[0][0] == 1 || grid[M - 1][N - 1] == 1) { + return 0; + } + + grid[M - 1][N - 1] = 1; + + for (int r = M - 1; r >= 0; r--) { + for (int c = N - 1; c >= 0; c--) { + if (r == M - 1 && c == N - 1) { + continue; + } + + if (grid[r][c] == 1) { + grid[r][c] = 0; + } else { + uint down = (r + 1 < M) ? grid[r + 1][c] : 0; + uint right = (c + 1 < N) ? grid[r][c + 1] : 0; + grid[r][c] = down + right; + } + } + } + + return grid[0][0]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} grid + * @return {number} + */ + uniquePathsWithObstacles(grid) { + const M = grid.length, N = grid[0].length; + if (grid[0][0] === 1 || grid[M - 1][N - 1] === 1) { + return 0; + } + + grid[M - 1][N - 1] = 1; + + for (let r = M - 1; r >= 0; r--) { + for (let c = N - 1; c >= 0; c--) { + if (r === M - 1 && c === N - 1) { + continue; + } + + if (grid[r][c] === 1) { + grid[r][c] = 0; + } else { + const down = (r + 1 < M) ? grid[r + 1][c] : 0; + const right = (c + 1 < N) ? grid[r][c + 1] : 0; + grid[r][c] = down + right; + } + } + } + + return grid[0][0]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n)$ +* Space complexity: $O(1)$ extra space. + +> Where $m$ is the number of rows and $n$ is the number of columns. \ No newline at end of file From 6af881219189b5166229f63701a884ba7c2a4374 Mon Sep 17 00:00:00 2001 From: Sri Hari <94112314+Srihari2222@users.noreply.github.com> Date: Sat, 28 Dec 2024 02:39:57 +0530 Subject: [PATCH 21/45] Sri Hari: Batch-4/Neetcode-All/Added-articles (#3775) * Batch-4/Neetcode-All/Added-articles * Batch-4/Neetcode-All/Added-articles * Batch-4/Neetcode-All/Added-articles --- articles/add-to-array-form-of-integer.md | 217 +++++ articles/count-vowels-permutation.md | 864 ++++++++++++++++++ .../flip-string-to-monotone-increasing.md | 523 +++++++++++ articles/longest-ideal-subsequence.md | 422 +++++++++ .../maximum-value-of-k-coins-from-piles.md | 451 +++++++++ articles/minimum-cost-to-cut-a-stick.md | 485 ++++++++++ articles/minimum-falling-path-sum.md | 449 +++++++++ .../number-of-dice-rolls-with-target-sum.md | 573 ++++++++++++ articles/number-of-music-playlists.md | 340 +++++++ ...form-a-target-string-given-a-dictionary.md | 568 ++++++++++++ ...-rearrange-sticks-with-k-sticks-visible.md | 375 ++++++++ ...stay-in-the-same-place-after-some-steps.md | 503 ++++++++++ articles/out-of-boundary-paths.md | 525 +++++++++++ articles/painting-the-walls.md | 430 +++++++++ articles/profitable-schemes.md | 529 +++++++++++ articles/stone-game-ii.md | 540 +++++++++++ articles/string-compression-ii.md | 480 ++++++++++ articles/target-sum.md | 18 +- 18 files changed, 8283 insertions(+), 9 deletions(-) create mode 100644 articles/add-to-array-form-of-integer.md create mode 100644 articles/count-vowels-permutation.md create mode 100644 articles/flip-string-to-monotone-increasing.md create mode 100644 articles/longest-ideal-subsequence.md create mode 100644 articles/maximum-value-of-k-coins-from-piles.md create mode 100644 articles/minimum-cost-to-cut-a-stick.md create mode 100644 articles/minimum-falling-path-sum.md create mode 100644 articles/number-of-dice-rolls-with-target-sum.md create mode 100644 articles/number-of-music-playlists.md create mode 100644 articles/number-of-ways-to-form-a-target-string-given-a-dictionary.md create mode 100644 articles/number-of-ways-to-rearrange-sticks-with-k-sticks-visible.md create mode 100644 articles/number-of-ways-to-stay-in-the-same-place-after-some-steps.md create mode 100644 articles/out-of-boundary-paths.md create mode 100644 articles/painting-the-walls.md create mode 100644 articles/profitable-schemes.md create mode 100644 articles/stone-game-ii.md create mode 100644 articles/string-compression-ii.md diff --git a/articles/add-to-array-form-of-integer.md b/articles/add-to-array-form-of-integer.md new file mode 100644 index 000000000..ecab6ca84 --- /dev/null +++ b/articles/add-to-array-form-of-integer.md @@ -0,0 +1,217 @@ +## 1. Reverse and Add + +::tabs-start + +```python +class Solution: + def addToArrayForm(self, num: List[int], k: int) -> List[int]: + num.reverse() + i = 0 + while k: + digit = k % 10 + if i < len(num): + num[i] += digit + else: + num.append(digit) + carry = num[i] // 10 + num[i] %= 10 + k //= 10 + k += carry + i += 1 + num.reverse() + return num +``` + +```java +public class Solution { + public List addToArrayForm(int[] num, int k) { + List result = new ArrayList<>(); + for (int i = num.length - 1; i >= 0; i--) { + k += num[i]; + result.add(k % 10); + k /= 10; + } + while (k > 0) { + result.add(k % 10); + k /= 10; + } + Collections.reverse(result); + return result; + } +} +``` + +```cpp +class Solution { +public: + vector addToArrayForm(vector& num, int k) { + reverse(num.begin(), num.end()); + int i = 0; + while (k) { + int digit = k % 10; + if (i < num.size()) { + num[i] += digit; + } else { + num.push_back(digit); + } + int carry = num[i] / 10; + num[i] %= 10; + k /= 10; + k += carry; + i++; + } + reverse(num.begin(), num.end()); + return num; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} num + * @param {number} k + * @return {number[]} + */ + addToArrayForm(num, k) { + num.reverse(); + let i = 0; + while (k > 0) { + const digit = k % 10; + if (i < num.length) { + num[i] += digit; + } else { + num.push(digit); + } + const carry = Math.floor(num[i] / 10); + num[i] %= 10; + k = Math.floor(k / 10) + carry; + i++; + } + num.reverse(); + return num; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(max(n, m))$ +* Space complexity: $O(n)$. + +> Where $n$ is the size of the array $num$ and $m$ is the number of digits in $k$. + +--- + +## 2. Without Reverse() + +::tabs-start + +```python +class Solution: + def addToArrayForm(self, num: List[int], k: int) -> List[int]: + from collections import deque + result = deque() + i = len(num) - 1 + carry = 0 + + while i >= 0 or k > 0 or carry > 0: + digit = k % 10 + sum_val = carry + (num[i] if i >= 0 else 0) + digit + + result.appendleft(sum_val % 10) + carry = sum_val // 10 + + k //= 10 + i -= 1 + + return list(result) +``` + +```java +public class Solution { + public List addToArrayForm(int[] num, int k) { + LinkedList result = new LinkedList<>(); + int carry = 0, i = num.length - 1; + + while (i >= 0 || k > 0 || carry > 0) { + int digit = k % 10; + int sum = carry + (i >= 0 ? num[i] : 0) + digit; + + result.addFirst(sum % 10); + carry = sum / 10; + + k /= 10; + i--; + } + + return result; + } +} +``` + +```cpp +class Solution { +public: + vector addToArrayForm(vector& num, int k) { + list result; + int carry = 0, i = num.size() - 1; + + while (i >= 0 || k > 0 || carry > 0) { + int digit = k % 10; + int sum = carry + (i >= 0 ? num[i] : 0) + digit; + + result.push_front(sum % 10); + carry = sum / 10; + + k /= 10; + i--; + } + + return vector(result.begin(), result.end()); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} num + * @param {number} k + * @return {number[]} + */ + addToArrayForm(num, k) { + let res = new Deque(); + let carry = 0, i = num.length - 1; + + while (i >= 0 || k > 0 || carry > 0) { + const digit = k % 10; + const sum = carry + (i >= 0 ? num[i] : 0) + digit; + + res.pushFront(sum % 10); + carry = Math.floor(sum / 10); + + k = Math.floor(k / 10); + i--; + } + + const resultArray = []; + while (!res.isEmpty()) { + resultArray.push(res.popFront()); + } + + return resultArray; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(max(n, m))$ +* Space complexity: $O(n)$ + +> Where $n$ is the size of the array $num$ and $m$ is the number of digits in $k$. \ No newline at end of file diff --git a/articles/count-vowels-permutation.md b/articles/count-vowels-permutation.md new file mode 100644 index 000000000..c2f561f56 --- /dev/null +++ b/articles/count-vowels-permutation.md @@ -0,0 +1,864 @@ +## 1. Recursion + +::tabs-start + +```python +class Solution: + def countVowelPermutation(self, n: int) -> int: + MOD = 10**9 + 7 + follows = { + 'a': ['e'], + 'e': ['a', 'i'], + 'i': ['a', 'e', 'o', 'u'], + 'o': ['i', 'u'], + 'u': ['a'] + } + + def dfs(i, v): + if i == n: + return 1 + + total = 0 + for nxt in follows[v]: + total = (total + dfs(i + 1, nxt)) % MOD + return total + + res = 0 + for vowel in ['a', 'e', 'i', 'o', 'u']: + res = (res + dfs(1, vowel)) % MOD + + return res +``` + +```java +public class Solution { + private final int MOD = 1_000_000_007; + private final Map> follows = Map.of( + 'a', List.of('e'), + 'e', List.of('a', 'i'), + 'i', List.of('a', 'e', 'o', 'u'), + 'o', List.of('i', 'u'), + 'u', List.of('a') + ); + + public int countVowelPermutation(int n) { + int res = 0; + for (char vowel : "aeiou".toCharArray()) { + res = (res + dfs(1, vowel, n)) % MOD; + } + return res; + } + + private int dfs(int i, char v, int n) { + if (i == n) { + return 1; + } + + int total = 0; + for (char next : follows.get(v)) { + total = (total + dfs(i + 1, next, n)) % MOD; + } + return total; + } +} +``` + +```cpp +class Solution { + const int MOD = 1e9 + 7; + unordered_map> follows = { + {'a', {'e'}}, + {'e', {'a', 'i'}}, + {'i', {'a', 'e', 'o', 'u'}}, + {'o', {'i', 'u'}}, + {'u', {'a'}} + }; + +public: + int countVowelPermutation(int n) { + + int res = 0; + for (char vowel : string("aeiou")) { + res = (res + dfs(1, vowel, n)) % MOD; + } + return res; + } + +private: + int dfs(int i, char v, int n) { + if (i == n) { + return 1; + } + + int total = 0; + for (char& next : follows[v]) { + total = (total + dfs(i + 1, next, n)) % MOD; + } + return total; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {number} + */ + countVowelPermutation(n) { + const MOD = 1e9 + 7; + + const follows = { + a: ['e'], + e: ['a', 'i'], + i: ['a', 'e', 'o', 'u'], + o: ['i', 'u'], + u: ['a'] + }; + + const dfs = (i, v) => { + if (i === n) { + return 1; + } + + let total = 0; + for (const next of follows[v]) { + total = (total + dfs(i + 1, next)) % MOD; + } + return total; + }; + + let res = 0; + for (const vowel of ['a', 'e', 'i', 'o', 'u']) { + res = (res + dfs(1, vowel)) % MOD; + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(4 ^ n)$ +* Space complexity: $O(n)$ for recursion stack. + +--- + +## 2. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def countVowelPermutation(self, n: int) -> int: + MOD = 10**9 + 7 + cache = {} + follows = { + 'a': ['e'], + 'e': ['a', 'i'], + 'i': ['a', 'e', 'o', 'u'], + 'o': ['i', 'u'], + 'u': ['a'] + } + + def dfs(i, v): + if i == n: + return 1 + if (i, v) in cache: + return cache[(i, v)] + + total = 0 + for nxt in follows[v]: + total = (total + dfs(i + 1, nxt)) % MOD + cache[(i, v)] = total + return total + + res = 0 + for vowel in ['a', 'e', 'i', 'o', 'u']: + res = (res + dfs(1, vowel)) % MOD + + return res +``` + +```java +public class Solution { + private final int MOD = 1_000_000_007; + private final Map> follows = Map.of( + 0, List.of(1), // 'a' -> 'e' + 1, List.of(0, 2), // 'e' -> 'a', 'i' + 2, List.of(0, 1, 3, 4), // 'i' -> 'a', 'e', 'o', 'u' + 3, List.of(2, 4), // 'o' -> 'i', 'u' + 4, List.of(0) // 'u' -> 'a' + ); + private int[][] dp; + + public int countVowelPermutation(int n) { + dp = new int[n][5]; + for (int i = 0; i < n; i++) { + Arrays.fill(dp[i], -1); + } + + int res = 0; + for (int vowel = 0; vowel < 5; vowel++) { + res = (res + dfs(1, vowel, n)) % MOD; + } + return res; + } + + private int dfs(int i, int v, int n) { + if (i == n) { + return 1; + } + if (dp[i][v] != -1) return dp[i][v]; + + int total = 0; + for (int next : follows.get(v)) { + total = (total + dfs(i + 1, next, n)) % MOD; + } + return dp[i][v] = total; + } +} +``` + +```cpp +class Solution { +private: + static const int MOD = 1e9 + 7; + vector> dp; + vector> follows = { + {1}, // 'a' -> 'e' + {0, 2}, // 'e' -> 'a', 'i' + {0, 1, 3, 4}, // 'i' -> 'a', 'e', 'o', 'u' + {2, 4}, // 'o' -> 'i', 'u' + {0} // 'u' -> 'a' + }; + + int dfs(int i, int v, int n) { + if (i == n) return 1; + if (dp[i][v] != -1) return dp[i][v]; + + int total = 0; + for (int next : follows[v]) { + total = (total + dfs(i + 1, next, n)) % MOD; + } + return dp[i][v] = total; + } + +public: + int countVowelPermutation(int n) { + dp.assign(n, vector(5, -1)); + + int res = 0; + for (int vowel = 0; vowel < 5; vowel++) { + res = (res + dfs(1, vowel, n)) % MOD; + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {number} + */ + countVowelPermutation(n) { + const MOD = 1e9 + 7; + const dp = Array.from({ length: n }, () => Array(5).fill(-1)); + + const follows = { + 0: [1], // 'a' -> 'e' + 1: [0, 2], // 'e' -> 'a', 'i' + 2: [0, 1, 3, 4], // 'i' -> 'a', 'e', 'o', 'u' + 3: [2, 4], // 'o' -> 'i', 'u' + 4: [0], // 'u' -> 'a' + }; + + const dfs = (i, v) => { + if (i === n) return 1; + if (dp[i][v] !== -1) return dp[i][v]; + + let total = 0; + for (const next of follows[v]) { + total = (total + dfs(i + 1, next)) % MOD; + } + dp[i][v] = total; + return total; + }; + + let res = 0; + for (let vowel = 0; vowel < 5; vowel++) { + res = (res + dfs(1, vowel)) % MOD; + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def countVowelPermutation(self, n: int) -> int: + MOD = 10**9 + 7 + a, e, i, o, u = 0, 1, 2, 3, 4 + dp = [[0] * 5 for _ in range(n + 1)] + dp[1] = [1, 1, 1, 1, 1] + + for j in range(2, n + 1): + dp[j][a] = (dp[j - 1][e] + dp[j - 1][i] + dp[j - 1][u]) % MOD + dp[j][e] = (dp[j - 1][a] + dp[j - 1][i]) % MOD + dp[j][i] = (dp[j - 1][e] + dp[j - 1][o]) % MOD + dp[j][o] = dp[j - 1][i] % MOD + dp[j][u] = (dp[j - 1][i] + dp[j - 1][o]) % MOD + + return sum(dp[n]) % MOD +``` + +```java +public class Solution { + private static final int MOD = 1_000_000_007; + + public int countVowelPermutation(int n) { + int[][] dp = new int[n + 1][5]; + int[][] follows = { + {1}, // 'a' -> 'e' + {0, 2}, // 'e' -> 'a', 'i' + {0, 1, 3, 4}, // 'i' -> 'a', 'e', 'o', 'u' + {2, 4}, // 'o' -> 'i', 'u' + {0} // 'u' -> 'a' + }; + + for (int v = 0; v < 5; v++) { + dp[1][v] = 1; + } + + for (int i = 2; i <= n; i++) { + for (int v = 0; v < 5; v++) { + for (int nextV : follows[v]) { + dp[i][v] = (dp[i][v] + dp[i - 1][nextV]) % MOD; + } + } + } + + int result = 0; + for (int v = 0; v < 5; v++) { + result = (result + dp[n][v]) % MOD; + } + return result; + } +} +``` + +```cpp +class Solution { +private: + static const int MOD = 1e9 + 7; + +public: + int countVowelPermutation(int n) { + vector> dp(n + 1, vector(5, 0)); + vector> follows = { + {1}, // 'a' -> 'e' + {0, 2}, // 'e' -> 'a', 'i' + {0, 1, 3, 4}, // 'i' -> 'a', 'e', 'o', 'u' + {2, 4}, // 'o' -> 'i', 'u' + {0} // 'u' -> 'a' + }; + + for (int v = 0; v < 5; v++) { + dp[1][v] = 1; + } + + for (int i = 2; i <= n; i++) { + for (int v = 0; v < 5; v++) { + for (int nextV : follows[v]) { + dp[i][v] = (dp[i][v] + dp[i - 1][nextV]) % MOD; + } + } + } + + int result = 0; + for (int v = 0; v < 5; v++) { + result = (result + dp[n][v]) % MOD; + } + return result; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {number} + */ + countVowelPermutation(n) { + const MOD = 1e9 + 7; + const dp = Array.from({ length: n + 1 }, () => Array(5).fill(0)); + const follows = [ + [1], // 'a' -> 'e' + [0, 2], // 'e' -> 'a', 'i' + [0, 1, 3, 4], // 'i' -> 'a', 'e', 'o', 'u' + [2, 4], // 'o' -> 'i', 'u' + [0] // 'u' -> 'a' + ]; + + for (let v = 0; v < 5; v++) { + dp[1][v] = 1; + } + + for (let i = 2; i <= n; i++) { + for (let v = 0; v < 5; v++) { + for (const nextV of follows[v]) { + dp[i][v] = (dp[i][v] + dp[i - 1][nextV]) % MOD; + } + } + } + + return dp[n].reduce((sum, val) => (sum + val) % MOD, 0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 4. Dynamic Programming (Space Optimized) + +::tabs-start + +```python +class Solution: + def countVowelPermutation(self, n: int) -> int: + MOD = 10**9 + 7 + follows = [ + [1], # 'a' -> 'e' + [0, 2], # 'e' -> 'a', 'i' + [0, 1, 3, 4], # 'i' -> 'a', 'e', 'o', 'u' + [2, 4], # 'o' -> 'i', 'u' + [0] # 'u' -> 'a' + ] + dp = [1] * 5 + + for _ in range(2, n + 1): + next_dp = [0] * 5 + for v in range(5): + for nextV in follows[v]: + next_dp[v] = (next_dp[v] + dp[nextV]) % MOD + dp = next_dp + + return sum(dp) % MOD +``` + +```java +public class Solution { + private static final int MOD = 1_000_000_007; + + public int countVowelPermutation(int n) { + int[][] follows = { + {1}, // 'a' -> 'e' + {0, 2}, // 'e' -> 'a', 'i' + {0, 1, 3, 4}, // 'i' -> 'a', 'e', 'o', 'u' + {2, 4}, // 'o' -> 'i', 'u' + {0} // 'u' -> 'a' + }; + + int[] dp = {1, 1, 1, 1, 1}; + + for (int i = 2; i <= n; i++) { + int[] nextDp = new int[5]; + for (int v = 0; v < 5; v++) { + for (int nextV : follows[v]) { + nextDp[v] = (nextDp[v] + dp[nextV]) % MOD; + } + } + dp = nextDp; + } + + int result = 0; + for (int count : dp) { + result = (result + count) % MOD; + } + return result; + } +} +``` + +```cpp +class Solution { +private: + static const int MOD = 1e9 + 7; + +public: + int countVowelPermutation(int n) { + vector> follows = { + {1}, // 'a' -> 'e' + {0, 2}, // 'e' -> 'a', 'i' + {0, 1, 3, 4}, // 'i' -> 'a', 'e', 'o', 'u' + {2, 4}, // 'o' -> 'i', 'u' + {0} // 'u' -> 'a' + }; + + vector dp(5, 1); + + for (int i = 2; i <= n; i++) { + vector nextDp(5, 0); + for (int v = 0; v < 5; v++) { + for (int nextV : follows[v]) { + nextDp[v] = (nextDp[v] + dp[nextV]) % MOD; + } + } + dp = nextDp; + } + + int result = 0; + for (int count : dp) { + result = (result + count) % MOD; + } + return result; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {number} + */ + countVowelPermutation(n) { + const MOD = 1e9 + 7; + const follows = [ + [1], // 'a' -> 'e' + [0, 2], // 'e' -> 'a', 'i' + [0, 1, 3, 4], // 'i' -> 'a', 'e', 'o', 'u' + [2, 4], // 'o' -> 'i', 'u' + [0] // 'u' -> 'a' + ]; + + let dp = [1, 1, 1, 1, 1]; + + for (let i = 2; i <= n; i++) { + const nextDp = [0, 0, 0, 0, 0]; + for (let v = 0; v < 5; v++) { + for (const nextV of follows[v]) { + nextDp[v] = (nextDp[v] + dp[nextV]) % MOD; + } + } + dp = nextDp; + } + + return dp.reduce((sum, count) => (sum + count) % MOD, 0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ since we used array of size $5$. + +--- + +## 5. Matrix Exponentiation + +::tabs-start + +```python +class Solution: + MOD = 10**9 + 7 + + class M: + def __init__(self, n): + self.a = [[0] * n for _ in range(n)] + + def __mul__(self, other): + n = len(self.a) + product = Solution.M(n) + for i in range(n): + for j in range(n): + for k in range(n): + product.a[i][k] = (product.a[i][k] + self.a[i][j] * other.a[j][k]) % Solution.MOD + return product + + def matrixExpo(self, base, exp): + n = len(base.a) + result = Solution.M(n) + for i in range(n): + result.a[i][i] = 1 + while exp > 0: + if exp % 2 == 1: + result = result * base + base = base * base + exp //= 2 + return result + + def countVowelPermutation(self, n: int) -> int: + follows = [ + [0, 1, 0, 0, 0], # 'a' -> 'e' + [1, 0, 1, 0, 0], # 'e' -> 'a', 'i' + [1, 1, 0, 1, 1], # 'i' -> 'a', 'e', 'o', 'u' + [0, 0, 1, 0, 1], # 'o' -> 'i', 'u' + [1, 0, 0, 0, 0] # 'u' -> 'a' + ] + + base = Solution.M(5) + base.a = follows + + result = self.matrixExpo(base, n - 1) + + return sum(sum(row) for row in result.a) % self.MOD +``` + +```java +public class Solution { + private static final int MOD = 1_000_000_007; + + static class M { + int[][] a; + + M(int n) { + a = new int[n][n]; + } + + M multiply(M other) { + int n = a.length; + M product = new M(n); + + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { + for (int k = 0; k < n; k++) { + product.a[i][k] = (int) ((product.a[i][k] + 1L * a[i][j] * other.a[j][k]) % MOD); + } + } + } + return product; + } + } + + private M matrixExpo(M base, int exp) { + int n = base.a.length; + M result = new M(n); + for (int i = 0; i < n; i++) { + result.a[i][i] = 1; + } + while (exp > 0) { + if (exp % 2 == 1) { + result = result.multiply(base); + } + base = base.multiply(base); + exp /= 2; + } + return result; + } + + public int countVowelPermutation(int n) { + int[][] follows = { + {0, 1, 0, 0, 0}, // 'a' -> 'e' + {1, 0, 1, 0, 0}, // 'e' -> 'a', 'i' + {1, 1, 0, 1, 1}, // 'i' -> 'a', 'e', 'o', 'u' + {0, 0, 1, 0, 1}, // 'o' -> 'i', 'u' + {1, 0, 0, 0, 0} // 'u' -> 'a' + }; + + M base = new M(5); + base.a = follows; + + M result = matrixExpo(base, n - 1); + + int ans = 0; + for (int i = 0; i < 5; i++) { + for (int j = 0; j < 5; j++) { + ans = (ans + result.a[i][j]) % MOD; + } + } + return ans; + } +} +``` + +```cpp +class Solution { + static const int MOD = 1e9 + 7; + + struct M { + vector> a; + + M(int n) { + a.resize(n, vector(n, 0)); + } + + M operator*(const M& other) const { + int n = a.size(); + M product(n); + + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { + for (int k = 0; k < n; k++) { + product.a[i][k] = (product.a[i][k] + a[i][j] * 1LL * other.a[j][k]) % MOD; + } + } + } + return product; + } + }; + + M matrixExpo(M base, int exp) { + int n = base.a.size(); + M result(n); + + for (int i = 0; i < n; i++) { + result.a[i][i] = 1; + } + + while (exp > 0) { + if (exp % 2 == 1) { + result = result * base; + } + base = base * base; + exp /= 2; + } + + return result; + } + +public: + int countVowelPermutation(int n) { + vector> follows = { + {0, 1, 0, 0, 0}, // 'a' -> 'e' + {1, 0, 1, 0, 0}, // 'e' -> 'a', 'i' + {1, 1, 0, 1, 1}, // 'i' -> 'a', 'e', 'o', 'u' + {0, 0, 1, 0, 1}, // 'o' -> 'i', 'u' + {1, 0, 0, 0, 0} // 'u' -> 'a' + }; + + M base(5); + base.a = follows; + + M result = matrixExpo(base, n - 1); + + int ans = 0; + for (int i = 0; i < 5; i++) { + for (int j = 0; j < 5; j++) { + ans = (ans + result.a[i][j]) % MOD; + } + } + + return ans; + } +}; +``` + +```javascript +class M { + /** + * @constructor + * @param {number} n + */ + constructor(n) { + this.MOD = 1e9 + 7; + this.a = Array.from({ length: n }, () => Array(n).fill(0)); + } + + /** + * @param {M} + * @return {M} + */ + multiply(other) { + const n = this.a.length; + const product = new M(n); + for (let i = 0; i < n; i++) { + for (let j = 0; j < n; j++) { + let sum = 0n; + for (let k = 0; k < n; k++) { + sum += BigInt(this.a[i][k]) * BigInt(other.a[k][j]); + } + product.a[i][j] = Number(sum % BigInt(this.MOD)); + } + } + return product; + } +} + +class Solution { + + /** + * @param {M} + * @param {number} exp + * @return {M} + */ + matrixExpo(base, exp) { + const n = base.a.length; + let result = new M(n); + for (let i = 0; i < n; i++) { + result.a[i][i] = 1; + } + while (exp > 0) { + if (exp & 1) { + result = result.multiply(base); + } + base = base.multiply(base); + exp >>= 1; + } + return result; + } + + /** + * @param {number} n + * @return {number} + */ + countVowelPermutation(n) { + const MOD = 1e9 + 7; + const follows = [ + [0, 1, 0, 0, 0], // 'a' -> 'e' + [1, 0, 1, 0, 0], // 'e' -> 'a', 'i' + [1, 1, 0, 1, 1], // 'i' -> 'a', 'e', 'o', 'u' + [0, 0, 1, 0, 1], // 'o' -> 'i', 'u' + [1, 0, 0, 0, 0] // 'u' -> 'a' + ]; + const base = new M(5); + base.a = follows; + const result = this.matrixExpo(base, n - 1); + let ans = 0; + for (let i = 0; i < 5; i++) { + for (let j = 0; j < 5; j++) { + ans = (ans + result.a[i][j]) % MOD; + } + } + return ans; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m ^ 3 \log n)$ +* Space complexity: $O(m ^ 2)$ + +> Where $m$ is the size of the matrix used in matrix exponentiation $(5 X 5)$ and $n$ is the length of the permutation. \ No newline at end of file diff --git a/articles/flip-string-to-monotone-increasing.md b/articles/flip-string-to-monotone-increasing.md new file mode 100644 index 000000000..74c357f57 --- /dev/null +++ b/articles/flip-string-to-monotone-increasing.md @@ -0,0 +1,523 @@ +## 1. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def minFlipsMonoIncr(self, s: str) -> int: + dp = {} + + def dfs(i, mono): + if (i, mono) in dp: + return dp[(i, mono)] + if i == len(s): + return 0 + + if mono and s[i] == "0": + dp[(i, mono)] = min(1 + dfs(i + 1, False), dfs(i + 1, mono)) + elif mono and s[i] == "1": + dp[(i, mono)] = min(1 + dfs(i + 1, mono), dfs(i + 1, False)) + elif not mono and s[i] == "1": + dp[(i, mono)] = dfs(i + 1, mono) + else: + dp[(i, mono)] = 1 + dfs(i + 1, mono) + + return dp[(i, mono)] + + return dfs(0, True) +``` + +```java +public class Solution { + private int[][] dp; + + public int minFlipsMonoIncr(String s) { + int n = s.length(); + dp = new int[n][2]; + for (int i = 0; i < n; i++) { + dp[i][0] = dp[i][1] = -1; + } + return dfs(0, 1, s); + } + + private int dfs(int i, int mono, String s) { + if (i == s.length()) return 0; + if (dp[i][mono] != -1) return dp[i][mono]; + + if (mono == 1 && s.charAt(i) == '0') { + dp[i][mono] = Math.min(1 + dfs(i + 1, 0, s), dfs(i + 1, mono, s)); + } else if (mono == 1 && s.charAt(i) == '1') { + dp[i][mono] = Math.min(1 + dfs(i + 1, mono, s), dfs(i + 1, 0, s)); + } else if (mono == 0 && s.charAt(i) == '1') { + dp[i][mono] = dfs(i + 1, mono, s); + } else { + dp[i][mono] = 1 + dfs(i + 1, mono, s); + } + return dp[i][mono]; + } +} +``` + +```cpp +class Solution { +public: + int minFlipsMonoIncr(string s) { + int n = s.length(); + vector> dp(n, vector(2, -1)); + return dfs(0, 1, s, dp); + } + +private: + int dfs(int i, int mono, const string& s, vector>& dp) { + if (i == s.length()) return 0; + if (dp[i][mono] != -1) return dp[i][mono]; + + if (mono == 1 && s[i] == '0') { + dp[i][mono] = min(1 + dfs(i + 1, 0, s, dp), dfs(i + 1, mono, s, dp)); + } else if (mono == 1 && s[i] == '1') { + dp[i][mono] = min(1 + dfs(i + 1, mono, s, dp), dfs(i + 1, 0, s, dp)); + } else if (mono == 0 && s[i] == '1') { + dp[i][mono] = dfs(i + 1, mono, s, dp); + } else { + dp[i][mono] = 1 + dfs(i + 1, mono, s, dp); + } + return dp[i][mono]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {number} + */ + minFlipsMonoIncr(s) { + const n = s.length; + const dp = Array.from({ length: n }, () => Array(2).fill(-1)); + + const dfs = (i, mono) => { + if (i === n) return 0; + if (dp[i][mono] !== -1) return dp[i][mono]; + + if (mono === 1 && s[i] === '0') { + dp[i][mono] = Math.min(1 + dfs(i + 1, 0), dfs(i + 1, mono)); + } else if (mono === 1 && s[i] === '1') { + dp[i][mono] = Math.min(1 + dfs(i + 1, mono), dfs(i + 1, 0)); + } else if (mono === 0 && s[i] === '1') { + dp[i][mono] = dfs(i + 1, mono); + } else { + dp[i][mono] = 1 + dfs(i + 1, mono); + } + return dp[i][mono]; + }; + + return dfs(0, 1); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def minFlipsMonoIncr(self, s: str) -> int: + n = len(s) + dp = [[0] * 2 for _ in range(n + 1)] + + for i in range(n - 1, -1, -1): + if s[i] == '0': + dp[i][1] = min(1 + dp[i + 1][0], dp[i + 1][1]) + dp[i][0] = 1 + dp[i + 1][0] + else: # s[i] == '1' + dp[i][1] = min(1 + dp[i + 1][1], dp[i + 1][0]) + dp[i][0] = dp[i + 1][0] + + return dp[0][1] +``` + +```java +public class Solution { + public int minFlipsMonoIncr(String s) { + int n = s.length(); + int[][] dp = new int[n + 1][2]; + + for (int i = n - 1; i >= 0; i--) { + if (s.charAt(i) == '0') { + dp[i][1] = Math.min(1 + dp[i + 1][0], dp[i + 1][1]); + dp[i][0] = 1 + dp[i + 1][0]; + } else { // s.charAt(i) == '1' + dp[i][1] = Math.min(1 + dp[i + 1][1], dp[i + 1][0]); + dp[i][0] = dp[i + 1][0]; + } + } + + return dp[0][1]; + } +} +``` + +```cpp +class Solution { +public: + int minFlipsMonoIncr(string s) { + int n = s.size(); + vector> dp(n + 1, vector(2, 0)); + + for (int i = n - 1; i >= 0; i--) { + if (s[i] == '0') { + dp[i][1] = min(1 + dp[i + 1][0], dp[i + 1][1]); + dp[i][0] = 1 + dp[i + 1][0]; + } else { // s[i] == '1' + dp[i][1] = min(1 + dp[i + 1][1], dp[i + 1][0]); + dp[i][0] = dp[i + 1][0]; + } + } + + return dp[0][1]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {number} + */ + minFlipsMonoIncr(s) { + const n = s.length; + const dp = Array.from({ length: n + 1 }, () => [0, 0]); + + for (let i = n - 1; i >= 0; i--) { + if (s[i] === '0') { + dp[i][1] = Math.min(1 + dp[i + 1][0], dp[i + 1][1]); + dp[i][0] = 1 + dp[i + 1][0]; + } else { // s[i] === '1' + dp[i][1] = Math.min(1 + dp[i + 1][1], dp[i + 1][0]); + dp[i][0] = dp[i + 1][0]; + } + } + + return dp[0][1]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Dynamic Programming (Space Optimized) + +::tabs-start + +```python +class Solution: + def minFlipsMonoIncr(self, s: str) -> int: + n = len(s) + dp = [0, 0] + + for i in range(n - 1, -1, -1): + if s[i] == '0': + new_dp_1 = min(1 + dp[0], dp[1]) + new_dp_0 = dp[0] + 1 + else: # s[i] == '1' + new_dp_1 = min(dp[1] + 1, dp[0]) + new_dp_0 = dp[0] + + dp[1] = new_dp_1 + dp[0] = new_dp_0 + + return dp[1] +``` + +```java +public class Solution { + public int minFlipsMonoIncr(String s) { + int[] dp = new int[2]; + + for (int i = s.length() - 1; i >= 0; i--) { + int newDp1, newDp0; + if (s.charAt(i) == '0') { + newDp1 = Math.min(1 + dp[0], dp[1]); + newDp0 = 1 + dp[0]; + } else { // s[i] == '1' + newDp1 = Math.min(1 + dp[1], dp[0]); + newDp0 = dp[0]; + } + + dp[1] = newDp1; + dp[0] = newDp0; + } + + return dp[1]; + } +} +``` + +```cpp +class Solution { +public: + int minFlipsMonoIncr(string s) { + vector dp(2, 0); + + for (int i = s.length() - 1; i >= 0; i--) { + int newDp1, newDp0; + if (s[i] == '0') { + newDp1 = min(1 + dp[0], dp[1]); + newDp0 = dp[0] + 1; + } else { // s[i] == '1' + newDp1 = min(1 + dp[1], dp[0]); + newDp0 = dp[0]; + } + + dp[1] = newDp1; + dp[0] = newDp0; + } + + return dp[1]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {number} + */ + minFlipsMonoIncr(s) { + let dp = [0, 0]; + + for (let i = s.length - 1; i >= 0; i--) { + let newDp1, newDp0; + if (s[i] === '0') { + newDp1 = Math.min(1 + dp[0], dp[1]); + newDp0 = dp[0] + 1; + } else { // s[i] === '1' + newDp1 = Math.min(1 + dp[1], dp[0]); + newDp0 = dp[0]; + } + + dp[1] = newDp1; + dp[0] = newDp0; + } + + return dp[1]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. + +--- + +## 4. Prefix & Suffix Arrays + +::tabs-start + +```python +class Solution: + def minFlipsMonoIncr(self, s: str) -> int: + n = len(s) + left_ones = [0] * (n + 1) + right_zeros = [0] * (n + 1) + + for i in range(n): + left_ones[i + 1] = left_ones[i] + (1 if s[i] == '1' else 0) + + for i in range(n - 1, -1, -1): + right_zeros[i] = right_zeros[i + 1] + (1 if s[i] == '0' else 0) + + res = float('inf') + for i in range(n + 1): + res = min(res, left_ones[i] + right_zeros[i]) + + return res +``` + +```java +public class Solution { + public int minFlipsMonoIncr(String s) { + int n = s.length(); + int[] leftOnes = new int[n + 1]; + int[] rightZeros = new int[n + 1]; + + for (int i = 0; i < n; i++) { + leftOnes[i + 1] = leftOnes[i] + (s.charAt(i) == '1' ? 1 : 0); + } + + for (int i = n - 1; i >= 0; i--) { + rightZeros[i] = rightZeros[i + 1] + (s.charAt(i) == '0' ? 1 : 0); + } + + int res = Integer.MAX_VALUE; + for (int i = 0; i <= n; i++) { + res = Math.min(res, leftOnes[i] + rightZeros[i]); + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int minFlipsMonoIncr(string s) { + int n = s.size(); + vector leftOnes(n + 1, 0), rightZeros(n + 1, 0); + + for (int i = 0; i < n; i++) { + leftOnes[i + 1] = leftOnes[i] + (s[i] == '1' ? 1 : 0); + } + + for (int i = n - 1; i >= 0; i--) { + rightZeros[i] = rightZeros[i + 1] + (s[i] == '0' ? 1 : 0); + } + + int res = INT_MAX; + for (int i = 0; i <= n; i++) { + res = min(res, leftOnes[i] + rightZeros[i]); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {number} + */ + minFlipsMonoIncr(s) { + const n = s.length; + const leftOnes = Array(n + 1).fill(0); + const rightZeros = Array(n + 1).fill(0); + + for (let i = 0; i < n; i++) { + leftOnes[i + 1] = leftOnes[i] + (s[i] === '1' ? 1 : 0); + } + + for (let i = n - 1; i >= 0; i--) { + rightZeros[i] = rightZeros[i + 1] + (s[i] === '0' ? 1 : 0); + } + + let res = Infinity; + for (let i = 0; i <= n; i++) { + res = Math.min(res, leftOnes[i] + rightZeros[i]); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 5. Dynamic Programming (Optimal) + +::tabs-start + +```python +class Solution: + def minFlipsMonoIncr(self, s: str) -> int: + res = cntOne = 0 + for c in s: + if c == '1': + cntOne += 1 + else: + res = min(res + 1, cntOne) + return res +``` + +```java +public class Solution { + public int minFlipsMonoIncr(String s) { + int res = 0, cntOne = 0; + for (int i = 0; i < s.length(); i++) { + if (s.charAt(i) == '1') { + cntOne++; + } else { + res = Math.min(res + 1, cntOne); + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int minFlipsMonoIncr(string s) { + int res = 0, cntOne = 0; + for (char& c : s) { + if (c == '1') { + cntOne++; + } else { + res = min(res + 1, cntOne); + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {number} + */ + minFlipsMonoIncr(s) { + let res = 0, cntOne = 0; + for (let c of s) { + if (c === '1') { + cntOne++; + } else { + res = Math.min(res + 1, cntOne); + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. \ No newline at end of file diff --git a/articles/longest-ideal-subsequence.md b/articles/longest-ideal-subsequence.md new file mode 100644 index 000000000..2162b48ec --- /dev/null +++ b/articles/longest-ideal-subsequence.md @@ -0,0 +1,422 @@ +## 1. Recursion + +::tabs-start + +```python +class Solution: + def longestIdealString(self, s: str, k: int) -> int: + def dfs(i, prev): + if i == len(s): + return 0 + skip = dfs(i + 1, prev) + include = 0 + if prev == "" or abs(ord(s[i]) - ord(prev)) <= k: + include = 1 + dfs(i + 1, s[i]) + return max(skip, include) + + return dfs(0, "") +``` + +```java +public class Solution { + public int longestIdealString(String s, int k) { + return dfs(0, -1, s, k); + } + + private int dfs(int i, int prev, String s, int k) { + if (i == s.length()) { + return 0; + } + int skip = dfs(i + 1, prev, s, k); + int include = 0; + if (prev == -1 || Math.abs(s.charAt(i) - prev) <= k) { + include = 1 + dfs(i + 1, s.charAt(i), s, k); + } + return Math.max(skip, include); + } +} +``` + +```cpp +class Solution { +public: + int longestIdealString(string s, int k) { + return dfs(0, -1, s, k); + } + +private: + int dfs(int i, int prev, const string &s, int k) { + if (i == s.size()) { + return 0; + } + int skip = dfs(i + 1, prev, s, k); + int include = 0; + if (prev == -1 || abs(s[i] - prev) <= k) { + include = 1 + dfs(i + 1, s[i], s, k); + } + return max(skip, include); + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @param {number} k + * @return {number} + */ + longestIdealString(s, k) { + const dfs = (i, prev) => { + if (i === s.length) { + return 0; + } + const skip = dfs(i + 1, prev); + let include = 0; + if (prev === -1 || Math.abs(s.charCodeAt(i) - prev) <= k) { + include = 1 + dfs(i + 1, s.charCodeAt(i)); + } + return Math.max(skip, include); + }; + + return dfs(0, -1); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(2 ^ n)$ +* Space complexity: $O(n)$ for recursion stack. + +--- + +## 2. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def longestIdealString(self, s: str, k: int) -> int: + cache = [[-1] * 27 for _ in range(len(s))] + + def dfs(i, prev): + if i == len(s): + return 0 + if cache[i][prev + 1] != -1: + return cache[i][prev + 1] + + skip = dfs(i + 1, prev) + include = 0 + c = ord(s[i]) - ord('a') + if prev == -1 or abs(c - prev) <= k: + include = 1 + dfs(i + 1, c) + cache[i][prev + 1] = max(skip, include) + return cache[i][prev + 1] + + return dfs(0, -1) +``` + +```java +public class Solution { + private int[][] dp; + + public int longestIdealString(String s, int k) { + dp = new int[s.length()][27]; + for (int i = 0; i < s.length(); i++) { + for (int j = 0; j < 27; j++) { + dp[i][j] = -1; + } + } + return dfs(0, -1, s, k); + } + + private int dfs(int i, int prev, String s, int k) { + if (i == s.length()) { + return 0; + } + if (dp[i][prev + 1] != -1) { + return dp[i][prev + 1]; + } + int skip = dfs(i + 1, prev, s, k); + int include = 0; + if (prev == -1 || Math.abs(s.charAt(i) - ('a' + prev)) <= k) { + include = 1 + dfs(i + 1, s.charAt(i) - 'a', s, k); + } + dp[i][prev + 1] = Math.max(skip, include); + return Math.max(skip, include); + } +} +``` + +```cpp +class Solution { +private: + vector> dp; + +public: + int longestIdealString(string s, int k) { + dp = vector>(s.size(), vector(27, -1)); + return dfs(0, -1, s, k); + } + +private: + int dfs(int i, int prev, const string &s, int k) { + if (i == s.size()) { + return 0; + } + if (dp[i][prev + 1] != -1) { + return dp[i][prev + 1]; + } + int skip = dfs(i + 1, prev, s, k); + int include = 0; + if (prev == -1 || abs(s[i] - ('a' + prev)) <= k) { + include = 1 + dfs(i + 1, s[i] - 'a', s, k); + } + dp[i][prev + 1] = max(skip, include); + return max(skip, include); + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @param {number} k + * @return {number} + */ + longestIdealString(s, k) { + const dp = Array.from({ length: s.length }, () => Array(27).fill(-1)); + + const dfs = (i, prev) => { + if (i === s.length) { + return 0; + } + if (dp[i][prev + 1] !== -1) { + return dp[i][prev + 1]; + } + const skip = dfs(i + 1, prev); + let include = 0; + if (prev === -1 || Math.abs(s.charCodeAt(i) - ('a'.charCodeAt(0) + prev)) <= k) { + include = 1 + dfs(i + 1, s.charCodeAt(i) - 'a'.charCodeAt(0)); + } + dp[i][prev + 1] = Math.max(skip, include); + return Math.max(skip, include); + }; + + return dfs(0, -1); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def longestIdealString(self, s: str, k: int) -> int: + dp = [[0] * 26 for _ in range(len(s) + 1)] + + for i in range(1, len(s) + 1): + curr = ord(s[i - 1]) - ord('a') + for prev in range(26): + dp[i][prev] = max(dp[i][prev], dp[i - 1][prev]) + if abs(curr - prev) <= k: + dp[i][curr] = max(dp[i][curr], 1 + dp[i - 1][prev]) + + return max(dp[len(s)]) +``` + +```java +public class Solution { + public int longestIdealString(String s, int k) { + int n = s.length(); + int[][] dp = new int[n + 1][26]; + + for (int i = 1; i <= n; i++) { + int curr = s.charAt(i - 1) - 'a'; + for (int prev = 0; prev < 26; prev++) { + dp[i][prev] = Math.max(dp[i][prev], dp[i - 1][prev]); + if (Math.abs(curr - prev) <= k) { + dp[i][curr] = Math.max(dp[i][curr], 1 + dp[i - 1][prev]); + } + } + } + + int max = 0; + for (int val : dp[n]) { + max = Math.max(max, val); + } + return max; + } +} +``` + +```cpp +class Solution { +public: + int longestIdealString(string s, int k) { + int n = s.size(); + vector> dp(n + 1, vector(26, 0)); + + for (int i = 1; i <= n; i++) { + int curr = s[i - 1] - 'a'; + for (int prev = 0; prev < 26; prev++) { + dp[i][prev] = max(dp[i][prev], dp[i - 1][prev]); + if (abs(curr - prev) <= k) { + dp[i][curr] = max(dp[i][curr], 1 + dp[i - 1][prev]); + } + } + } + + return *max_element(dp[n].begin(), dp[n].end()); + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @param {number} k + * @return {number} + */ + longestIdealString(s, k) { + const n = s.length; + const dp = Array.from({ length: n + 1 }, () => Array(26).fill(0)); + + for (let i = 1; i <= n; i++) { + const curr = s.charCodeAt(i - 1) - 'a'.charCodeAt(0); + for (let prev = 0; prev < 26; prev++) { + dp[i][prev] = Math.max(dp[i][prev], dp[i - 1][prev]); + if (Math.abs(curr - prev) <= k) { + dp[i][curr] = Math.max(dp[i][curr], 1 + dp[i - 1][prev]); + } + } + } + return Math.max(...dp[n]); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 4. Dynamic Programming (Space Optimized) + +::tabs-start + +```python +class Solution: + def longestIdealString(self, s: str, k: int) -> int: + dp = [0] * 26 + + for c in s: + curr = ord(c) - ord('a') + longest = 1 + for prev in range(26): + if abs(curr - prev) <= k: + longest = max(longest, 1 + dp[prev]) + dp[curr] = max(dp[curr], longest) + + return max(dp) +``` + +```java +public class Solution { + public int longestIdealString(String s, int k) { + int[] dp = new int[26]; + + for (char c : s.toCharArray()) { + int curr = c - 'a'; // 0-25 + int longest = 1; + for (int prev = 0; prev < 26; prev++) { + if (Math.abs(curr - prev) <= k) { + longest = Math.max(longest, 1 + dp[prev]); + } + } + dp[curr] = Math.max(dp[curr], longest); + } + + int max = 0; + for (int val : dp) { + max = Math.max(max, val); + } + return max; + } +} +``` + +```cpp +class Solution { +public: + int longestIdealString(string s, int k) { + vector dp(26, 0); + + for (char c : s) { + int curr = c - 'a'; + int longest = 1; + for (int prev = 0; prev < 26; prev++) { + if (abs(curr - prev) <= k) { + longest = max(longest, 1 + dp[prev]); + } + } + dp[curr] = max(dp[curr], longest); + } + + return *max_element(dp.begin(), dp.end()); + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @param {number} k + * @return {number} + */ + longestIdealString(s, k) { + const dp = Array(26).fill(0); + + for (const c of s) { + const curr = c.charCodeAt(0) - 'a'.charCodeAt(0); + let longest = 1; + for (let prev = 0; prev < 26; prev++) { + if (Math.abs(curr - prev) <= k) { + longest = Math.max(longest, 1 + dp[prev]); + } + } + dp[curr] = Math.max(dp[curr], longest); + } + + return Math.max(...dp); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ since we have at most 26 different characters. \ No newline at end of file diff --git a/articles/maximum-value-of-k-coins-from-piles.md b/articles/maximum-value-of-k-coins-from-piles.md new file mode 100644 index 000000000..bf108e73c --- /dev/null +++ b/articles/maximum-value-of-k-coins-from-piles.md @@ -0,0 +1,451 @@ +## 1. Recursion + +::tabs-start + +```python +class Solution: + def maxValueOfCoins(self, piles: List[List[int]], k: int) -> int: + n = len(piles) + + def dfs(i, coins): + if i == n: + return 0 + + res = dfs(i + 1, coins) # skip current pile + curPile = 0 + for j in range(min(coins, len(piles[i]))): + curPile += piles[i][j] + res = max(res, curPile + dfs(i + 1, coins - (j + 1))) + return res + + return dfs(0, k) +``` + +```java +public class Solution { + public int maxValueOfCoins(List> piles, int k) { + int n = piles.size(); + return dfs(0, k, piles); + } + + private int dfs(int i, int coins, List> piles) { + if (i == piles.size()) { + return 0; + } + + int res = dfs(i + 1, coins, piles); // skip current pile + int curPile = 0; + for (int j = 0; j < Math.min(coins, piles.get(i).size()); j++) { + curPile += piles.get(i).get(j); + res = Math.max(res, curPile + dfs(i + 1, coins - (j + 1), piles)); + } + return res; + } +} +``` + +```cpp +class Solution { +private: + int dfs(int i, int coins, const vector>& piles) { + if (i == piles.size()) { + return 0; + } + + int res = dfs(i + 1, coins, piles); // skip current pile + int curPile = 0; + for (int j = 0; j < min(coins, (int)piles[i].size()); j++) { + curPile += piles[i][j]; + res = max(res, curPile + dfs(i + 1, coins - (j + 1), piles)); + } + return res; + } + +public: + int maxValueOfCoins(vector>& piles, int k) { + int n = piles.size(); + return dfs(0, k, piles); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} piles + * @param {number} k + * @return {number} + */ + maxValueOfCoins = (piles, k) => { + const n = piles.length; + + const dfs = (i, coins) => { + if (i === n) return 0; + + let res = dfs(i + 1, coins); // skip current pile + let curPile = 0; + for (let j = 0; j < Math.min(coins, piles[i].length); j++) { + curPile += piles[i][j]; + res = Math.max(res, curPile + dfs(i + 1, coins - (j + 1))); + } + return res; + }; + + return dfs(0, k); + }; +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(k ^ n)$ +* Space complexity: $O(n)$ for the recursion stack. + +> Where $n$ is the number of piles and $k$ is the number of coins to choose. + +--- + +## 2. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def maxValueOfCoins(self, piles: List[List[int]], k: int) -> int: + n = len(piles) + dp = [[-1] * (k + 1) for _ in range(n)] + + def dfs(i, coins): + if i == n: + return 0 + if dp[i][coins] != -1: + return dp[i][coins] + + dp[i][coins] = dfs(i + 1, coins) # skip current pile + curPile = 0 + for j in range(min(coins, len(piles[i]))): + curPile += piles[i][j] + dp[i][coins] = max(dp[i][coins], curPile + dfs(i + 1, coins - (j + 1))) + return dp[i][coins] + + return dfs(0, k) +``` + +```java +public class Solution { + private int[][] dp; + + public int maxValueOfCoins(List> piles, int k) { + int n = piles.size(); + dp = new int[n][k + 1]; + for (int[] row : dp) { + Arrays.fill(row, -1); + } + return dfs(0, k, piles); + } + + private int dfs(int i, int coins, List> piles) { + if (i == piles.size()) { + return 0; + } + if (dp[i][coins] != -1) { + return dp[i][coins]; + } + + dp[i][coins] = dfs(i + 1, coins, piles); // skip current pile + int curPile = 0; + for (int j = 0; j < Math.min(coins, piles.get(i).size()); j++) { + curPile += piles.get(i).get(j); + dp[i][coins] = Math.max(dp[i][coins], curPile + dfs(i + 1, coins - (j + 1), piles)); + } + return dp[i][coins]; + } +} +``` + +```cpp +class Solution { +private: + vector> dp; + + int dfs(int i, int coins, const vector>& piles) { + if (i == piles.size()) { + return 0; + } + if (dp[i][coins] != -1) { + return dp[i][coins]; + } + + dp[i][coins] = dfs(i + 1, coins, piles); // skip current pile + int curPile = 0; + for (int j = 0; j < min(coins, (int)piles[i].size()); j++) { + curPile += piles[i][j]; + dp[i][coins] = max(dp[i][coins], curPile + dfs(i + 1, coins - (j + 1), piles)); + } + return dp[i][coins]; + } + +public: + int maxValueOfCoins(vector>& piles, int k) { + int n = piles.size(); + dp = vector>(n, vector(k + 1, -1)); + return dfs(0, k, piles); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} piles + * @param {number} k + * @return {number} + */ + maxValueOfCoins = (piles, k) => { + const n = piles.length; + const dp = Array.from({ length: n }, () => Array(k + 1).fill(-1)); + + const dfs = (i, coins) => { + if (i === n) return 0; + if (dp[i][coins] !== -1) return dp[i][coins]; + + dp[i][coins] = dfs(i + 1, coins); // skip current pile + let curPile = 0; + for (let j = 0; j < Math.min(coins, piles[i].length); j++) { + curPile += piles[i][j]; + dp[i][coins] = Math.max(dp[i][coins], curPile + dfs(i + 1, coins - (j + 1))); + } + return dp[i][coins]; + }; + + return dfs(0, k); + }; +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * k)$ +* Space complexity: $O(n * k)$ + +> Where $n$ is the number of piles, $k$ is the number of coins to choose, and $m$ is the total number of coins among all the piles. + +--- + +## 3. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def maxValueOfCoins(self, piles: List[List[int]], k: int) -> int: + n = len(piles) + dp = [[0] * (k + 1) for _ in range(n + 1)] + + for i in range(n - 1, -1, -1): + for coins in range(k + 1): + dp[i][coins] = dp[i + 1][coins] + + curPile = 0 + for j in range(min(coins, len(piles[i]))): + curPile += piles[i][j] + dp[i][coins] = max( + dp[i][coins], + curPile + dp[i + 1][coins - (j + 1)] + ) + + return dp[0][k] +``` + +```java +public class Solution { + public int maxValueOfCoins(List> piles, int k) { + int n = piles.size(); + int[][] dp = new int[n + 1][k + 1]; + + for (int i = n - 1; i >= 0; i--) { + for (int coins = 0; coins <= k; coins++) { + dp[i][coins] = dp[i + 1][coins]; + + int curPile = 0; + for (int j = 0; j < Math.min(coins, piles.get(i).size()); j++) { + curPile += piles.get(i).get(j); + dp[i][coins] = Math.max( + dp[i][coins], + curPile + dp[i + 1][coins - (j + 1)] + ); + } + } + } + + return dp[0][k]; + } +} +``` + +```cpp +class Solution { +public: + int maxValueOfCoins(vector>& piles, int k) { + int n = piles.size(); + vector> dp(n + 1, vector(k + 1, 0)); + + for (int i = n - 1; i >= 0; i--) { + for (int coins = 0; coins <= k; coins++) { + dp[i][coins] = dp[i + 1][coins]; + + int curPile = 0; + for (int j = 0; j < min(coins, (int)piles[i].size()); j++) { + curPile += piles[i][j]; + dp[i][coins] = max( + dp[i][coins], + curPile + dp[i + 1][coins - (j + 1)] + ); + } + } + } + + return dp[0][k]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} piles + * @param {number} k + * @return {number} + */ + maxValueOfCoins = (piles, k) => { + const n = piles.length; + const dp = Array.from({ length: n + 1 }, () => Array(k + 1).fill(0)); + + for (let i = n - 1; i >= 0; i--) { + for (let coins = 0; coins <= k; coins++) { + dp[i][coins] = dp[i + 1][coins]; + + let curPile = 0; + for (let j = 0; j < Math.min(coins, piles[i].length); j++) { + curPile += piles[i][j]; + dp[i][coins] = Math.max( + dp[i][coins], + curPile + dp[i + 1][coins - (j + 1)] + ); + } + } + } + + return dp[0][k]; + }; +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * k)$ +* Space complexity: $O(n * k)$ + +> Where $n$ is the number of piles, $k$ is the number of coins to choose, and $m$ is the total number of coins among all the piles. + +--- + +## 4. Dynamic Programming (Space Optimized) + +::tabs-start + +```python +class Solution: + def maxValueOfCoins(self, piles: List[List[int]], k: int) -> int: + dp = [0] * (k + 1) + + for pile in piles: + for coins in range(k, 0, -1): + curPile = 0 + for j in range(min(coins, len(pile))): + curPile += pile[j] + dp[coins] = max(dp[coins], dp[coins - (j + 1)] + curPile) + + return dp[k] +``` + +```java +public class Solution { + public int maxValueOfCoins(List> piles, int k) { + int[] dp = new int[k + 1]; + + for (List pile : piles) { + for (int coins = k; coins >= 1; coins--) { + int curPile = 0; + for (int j = 0; j < Math.min(coins, pile.size()); j++) { + curPile += pile.get(j); + dp[coins] = Math.max(dp[coins], dp[coins - (j + 1)] + curPile); + } + } + } + + return dp[k]; + } +} +``` + +```cpp +class Solution { +public: + int maxValueOfCoins(vector>& piles, int k) { + vector dp(k + 1, 0); + + for (const auto& pile : piles) { + for (int coins = k; coins >= 1; coins--) { + int curPile = 0; + for (int j = 0; j < min(coins, (int)pile.size()); j++) { + curPile += pile[j]; + dp[coins] = max(dp[coins], dp[coins - (j + 1)] + curPile); + } + } + } + + return dp[k]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} piles + * @param {number} k + * @return {number} + */ + maxValueOfCoins = (piles, k) => { + let dp = Array(k + 1).fill(0); + + for (const pile of piles) { + for (let coins = k; coins >= 1; coins--) { + let curPile = 0; + for (let j = 0; j < Math.min(coins, pile.length); j++) { + curPile += pile[j]; + dp[coins] = Math.max(dp[coins], dp[coins - (j + 1)] + curPile); + } + } + } + + return dp[k]; + }; +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * k)$ +* Space complexity: $O(n * k)$ + +> Where $n$ is the number of piles, $k$ is the number of coins to choose, and $m$ is the total number of coins among all the piles. \ No newline at end of file diff --git a/articles/minimum-cost-to-cut-a-stick.md b/articles/minimum-cost-to-cut-a-stick.md new file mode 100644 index 000000000..0ef0f2484 --- /dev/null +++ b/articles/minimum-cost-to-cut-a-stick.md @@ -0,0 +1,485 @@ +## 1. Recursion + +::tabs-start + +```python +class Solution: + def minCost(self, n: int, cuts: List[int]) -> int: + def dfs(l, r): + if r - l == 1: + return 0 + res = float("inf") + for c in cuts: + if l < c < r: + res = min(res, (r - l) + dfs(l, c) + dfs(c, r)) + res = 0 if res == float("inf") else res + return res + + return dfs(0, n) +``` + +```java +public class Solution { + public int minCost(int n, int[] cuts) { + return dfs(0, n, cuts); + } + + private int dfs(int l, int r, int[] cuts) { + if (r - l == 1) { + return 0; + } + int res = Integer.MAX_VALUE; + for (int c : cuts) { + if (l < c && c < r) { + res = Math.min(res, (r - l) + dfs(l, c, cuts) + dfs(c, r, cuts)); + } + } + return res == Integer.MAX_VALUE ? 0 : res; + } +} +``` + +```cpp +class Solution { +public: + int minCost(int n, vector& cuts) { + return dfs(0, n, cuts); + } + +private: + int dfs(int l, int r, vector& cuts) { + if (r - l == 1) { + return 0; + } + int res = INT_MAX; + for (int c : cuts) { + if (l < c && c < r) { + res = min(res, (r - l) + dfs(l, c, cuts) + dfs(c, r, cuts)); + } + } + return res == INT_MAX ? 0 : res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number[]} cuts + * @return {number} + */ + minCost(n, cuts) { + const dfs = (l, r) => { + if (r - l === 1) { + return 0; + } + let res = Infinity; + for (const c of cuts) { + if (l < c && c < r) { + res = Math.min(res, (r - l) + dfs(l, c) + dfs(c, r)); + } + } + return res === Infinity ? 0 : res; + }; + + return dfs(0, n); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m ^ N)$ +* Space complexity: $O(N)$ for recursion stack. + +> Where $m$ is the size of the $cuts$ array, $n$ is the length of the stick, and $N = min(n, m)$. + +--- + +## 2. Dynamic Programming (Top-Down) - I + +::tabs-start + +```python +class Solution: + def minCost(self, n: int, cuts: List[int]) -> int: + dp = {} + + def dfs(l, r): + if r - l == 1: + return 0 + if (l, r) in dp: + return dp[(l, r)] + + res = float("inf") + for c in cuts: + if l < c < r: + res = min(res, (r - l) + dfs(l, c) + dfs(c, r)) + res = 0 if res == float("inf") else res + dp[(l, r)] = res + return res + + return dfs(0, n) +``` + +```java +public class Solution { + private Map dp; + + public int minCost(int n, int[] cuts) { + dp = new HashMap<>(); + return dfs(0, n, cuts); + } + + private int dfs(int l, int r, int[] cuts) { + if (r - l == 1) { + return 0; + } + String key = l + "," + r; + if (dp.containsKey(key)) { + return dp.get(key); + } + + int res = Integer.MAX_VALUE; + for (int c : cuts) { + if (l < c && c < r) { + res = Math.min(res, (r - l) + dfs(l, c, cuts) + dfs(c, r, cuts)); + } + } + res = res == Integer.MAX_VALUE ? 0 : res; + dp.put(key, res); + return res; + } +} +``` + +```cpp +class Solution { +public: + int minCost(int n, vector& cuts) { + return dfs(0, n, cuts); + } + +private: + unordered_map dp; + + int dfs(int l, int r, vector& cuts) { + if (r - l == 1) { + return 0; + } + string key = to_string(l) + "," + to_string(r); + if (dp.find(key) != dp.end()) { + return dp[key]; + } + + int res = INT_MAX; + for (int c : cuts) { + if (l < c && c < r) { + res = min(res, (r - l) + dfs(l, c, cuts) + dfs(c, r, cuts)); + } + } + res = res == INT_MAX ? 0 : res; + dp[key] = res; + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number[]} cuts + * @return {number} + */ + minCost(n, cuts) { + const dp = new Map(); + + const dfs = (l, r) => { + if (r - l === 1) { + return 0; + } + const key = `${l},${r}`; + if (dp.has(key)) { + return dp.get(key); + } + + let res = Infinity; + for (const c of cuts) { + if (l < c && c < r) { + res = Math.min(res, (r - l) + dfs(l, c) + dfs(c, r)); + } + } + res = res === Infinity ? 0 : res; + dp.set(key, res); + return res; + }; + + return dfs(0, n); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * N ^ 2)$ +* Space complexity: $O(N ^ 2)$ + +> Where $m$ is the size of the $cuts$ array, $n$ is the length of the stick, and $N = min(n, m)$. + +--- + +## 3. Dynamic Programming (Top-Down) - II + +::tabs-start + +```python +class Solution: + def minCost(self, n: int, cuts: List[int]) -> int: + m = len(cuts) + cuts.sort() + dp = [[-1] * (m + 1) for _ in range(m + 1)] + + def dfs(l, r, i, j): + if i > j: + return 0 + if dp[i][j] != -1: + return dp[i][j] + + res = float("inf") + for mid in range(i, j + 1): + cur = (r - l) + dfs(l, cuts[mid], i, mid - 1) + dfs(cuts[mid], r, mid + 1, j) + res = min(res, cur) + + dp[i][j] = res + return res + + return dfs(0, n, 0, m - 1) +``` + +```java +public class Solution { + private int[][] dp; + + public int minCost(int n, int[] cuts) { + int m = cuts.length; + Arrays.sort(cuts); + dp = new int[m + 1][m + 1]; + + for (int[] row : dp) { + Arrays.fill(row, -1); + } + + return dfs(0, n, 0, m - 1, cuts); + } + + private int dfs(int l, int r, int i, int j, int[] cuts) { + if (i > j) return 0; + if (dp[i][j] != -1) return dp[i][j]; + + int res = Integer.MAX_VALUE; + for (int mid = i; mid <= j; mid++) { + int cur = (r - l) + dfs(l, cuts[mid], i, mid - 1, cuts) + dfs(cuts[mid], r, mid + 1, j, cuts); + res = Math.min(res, cur); + } + + dp[i][j] = res; + return res; + } +} +``` + +```cpp +class Solution { +private: + vector> dp; + +public: + int minCost(int n, vector& cuts) { + int m = cuts.size(); + sort(cuts.begin(), cuts.end()); + dp = vector>(m + 1, vector(m + 1, -1)); + return dfs(0, n, 0, m - 1, cuts); + } + +private: + int dfs(int l, int r, int i, int j, vector& cuts) { + if (i > j) return 0; + if (dp[i][j] != -1) return dp[i][j]; + + int res = INT_MAX; + for (int mid = i; mid <= j; mid++) { + int cur = (r - l) + dfs(l, cuts[mid], i, mid - 1, cuts) + dfs(cuts[mid], r, mid + 1, j, cuts); + res = min(res, cur); + } + + dp[i][j] = res; + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number[]} cuts + * @return {number} + */ + minCost(n, cuts) { + const m = cuts.length; + cuts.sort((a, b) => a - b); + const dp = Array.from({ length: m + 1 }, () => Array(m + 1).fill(-1)); + + const dfs = (l, r, i, j) => { + if (i > j) return 0; + if (dp[i][j] !== -1) return dp[i][j]; + + let res = Infinity; + for (let mid = i; mid <= j; mid++) { + const cur = (r - l) + dfs(l, cuts[mid], i, mid - 1) + dfs(cuts[mid], r, mid + 1, j); + res = Math.min(res, cur); + } + + dp[i][j] = res; + return res; + }; + + return dfs(0, n, 0, m - 1); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m\log m + m ^ 3)$ +* Space complexity: $O(m ^ 2)$ + +> Where $m$ is the size of the $cuts$ array and $n$ is the length of the stick. + +--- + +## 4. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def minCost(self, n: int, cuts: List[int]) -> int: + m = len(cuts) + cuts = [0] + sorted(cuts) + [n] + dp = [[0] * (m + 2) for _ in range(m + 2)] + + for length in range(2, m + 2): + for i in range(m + 2 - length): + j = i + length + dp[i][j] = float("inf") + for mid in range(i + 1, j): + dp[i][j] = min( + dp[i][j], + cuts[j] - cuts[i] + dp[i][mid] + dp[mid][j] + ) + + return dp[0][m + 1] +``` + +```java +public class Solution { + public int minCost(int n, int[] cuts) { + int m = cuts.length; + int[] newCuts = new int[m + 2]; + System.arraycopy(cuts, 0, newCuts, 1, m); + newCuts[0] = 0; + newCuts[m + 1] = n; + Arrays.sort(newCuts); + + int[][] dp = new int[m + 2][m + 2]; + + for (int length = 2; length <= m + 1; length++) { + for (int i = 0; i + length <= m + 1; i++) { + int j = i + length; + dp[i][j] = Integer.MAX_VALUE; + for (int mid = i + 1; mid < j; mid++) { + dp[i][j] = Math.min(dp[i][j], + newCuts[j] - newCuts[i] + dp[i][mid] + dp[mid][j]); + } + } + } + + return dp[0][m + 1]; + } +} +``` + +```cpp +class Solution { +public: + int minCost(int n, vector& cuts) { + int m = cuts.size(); + cuts.push_back(0); + cuts.push_back(n); + sort(cuts.begin(), cuts.end()); + + vector> dp(m + 2, vector(m + 2, 0)); + + for (int length = 2; length <= m + 1; length++) { + for (int i = 0; i + length <= m + 1; i++) { + int j = i + length; + dp[i][j] = INT_MAX; + for (int mid = i + 1; mid < j; mid++) { + dp[i][j] = min(dp[i][j], + cuts[j] - cuts[i] + dp[i][mid] + dp[mid][j]); + } + } + } + + return dp[0][m + 1]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number[]} cuts + * @return {number} + */ + minCost(n, cuts) { + const m = cuts.length; + cuts = [0, ...cuts.sort((a, b) => a - b), n]; + const dp = Array.from({ length: m + 2 }, () => Array(m + 2).fill(0)); + + for (let length = 2; length <= m + 1; length++) { + for (let i = 0; i + length <= m + 1; i++) { + const j = i + length; + dp[i][j] = Infinity; + for (let mid = i + 1; mid < j; mid++) { + dp[i][j] = Math.min( + dp[i][j], + cuts[j] - cuts[i] + dp[i][mid] + dp[mid][j] + ); + } + } + } + + return dp[0][m + 1]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m\log m + m ^ 3)$ +* Space complexity: $O(m ^ 2)$ + +> Where $m$ is the size of the $cuts$ array and $n$ is the length of the stick. \ No newline at end of file diff --git a/articles/minimum-falling-path-sum.md b/articles/minimum-falling-path-sum.md new file mode 100644 index 000000000..c12290d15 --- /dev/null +++ b/articles/minimum-falling-path-sum.md @@ -0,0 +1,449 @@ +## 1. Recursion + +::tabs-start + +```python +class Solution: + def minFallingPathSum(self, matrix: List[List[int]]) -> int: + N = len(matrix) + + def dfs(r, c): + if r == N: + return 0 + if c < 0 or c >= N: + return float("inf") + return matrix[r][c] + min( + dfs(r + 1, c - 1), + dfs(r + 1, c), + dfs(r + 1, c + 1) + ) + + res = float("inf") + for c in range(N): + res = min(res, dfs(0, c)) + return res +``` + +```java +public class Solution { + private int dfs(int r, int c, int[][] matrix, int N) { + if (r == N) return 0; + if (c < 0 || c >= N) return Integer.MAX_VALUE; + return matrix[r][c] + Math.min( + Math.min(dfs(r + 1, c - 1, matrix, N), dfs(r + 1, c, matrix, N)), + dfs(r + 1, c + 1, matrix, N) + ); + } + + public int minFallingPathSum(int[][] matrix) { + int N = matrix.length; + int res = Integer.MAX_VALUE; + for (int c = 0; c < N; c++) { + res = Math.min(res, dfs(0, c, matrix, N)); + } + return res; + } +} +``` + +```cpp +class Solution { +private: + int dfs(int r, int c, vector>& matrix, int N) { + if (r == N) return 0; + if (c < 0 || c >= N) return INT_MAX; + return matrix[r][c] + min({ + dfs(r + 1, c - 1, matrix, N), + dfs(r + 1, c, matrix, N), + dfs(r + 1, c + 1, matrix, N) + }); + } + +public: + int minFallingPathSum(vector>& matrix) { + int N = matrix.size(); + int res = INT_MAX; + for (int c = 0; c < N; c++) { + res = min(res, dfs(0, c, matrix, N)); + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} matrix + * @return {number} + */ + minFallingPathSum(matrix) { + const N = matrix.length; + + const dfs = (r, c) => { + if (r === N) return 0; + if (c < 0 || c >= N) return Infinity; + return matrix[r][c] + Math.min( + dfs(r + 1, c - 1), + dfs(r + 1, c), + dfs(r + 1, c + 1) + ); + }; + + let res = Infinity; + for (let c = 0; c < N; c++) { + res = Math.min(res, dfs(0, c)); + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(3 ^ n)$ +* Space complexity: $O(n)$ for recursion stack. + +--- + +## 2. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def minFallingPathSum(self, matrix: List[List[int]]) -> int: + N = len(matrix) + cache = {} + + def dfs(r, c): + if r == N: + return 0 + if c < 0 or c >= N: + return float("inf") + if (r, c) in cache: + return cache[(r, c)] + cache[(r, c)] = matrix[r][c] + min( + dfs(r + 1, c - 1), + dfs(r + 1, c), + dfs(r + 1, c + 1) + ) + return cache[(r, c)] + + res = float("inf") + for c in range(N): + res = min(res, dfs(0, c)) + return res +``` + +```java +public class Solution { + private int[][] cache; + + private int dfs(int r, int c, int[][] matrix, int N) { + if (r == N) return 0; + if (c < 0 || c >= N) return Integer.MAX_VALUE; + if (cache[r][c] != Integer.MIN_VALUE) return cache[r][c]; + + cache[r][c] = matrix[r][c] + Math.min( + Math.min(dfs(r + 1, c - 1, matrix, N), dfs(r + 1, c, matrix, N)), + dfs(r + 1, c + 1, matrix, N) + ); + return cache[r][c]; + } + + public int minFallingPathSum(int[][] matrix) { + int N = matrix.length; + cache = new int[N][N]; + for (int[] row : cache) { + Arrays.fill(row, Integer.MIN_VALUE); + } + + int res = Integer.MAX_VALUE; + for (int c = 0; c < N; c++) { + res = Math.min(res, dfs(0, c, matrix, N)); + } + return res; + } +} +``` + +```cpp +class Solution { +private: + vector> cache; + + int dfs(int r, int c, vector>& matrix, int N) { + if (r == N) return 0; + if (c < 0 || c >= N) return INT_MAX; + if (cache[r][c] != INT_MIN) return cache[r][c]; + + cache[r][c] = matrix[r][c] + min({ + dfs(r + 1, c - 1, matrix, N), + dfs(r + 1, c, matrix, N), + dfs(r + 1, c + 1, matrix, N) + }); + return cache[r][c]; + } + +public: + int minFallingPathSum(vector>& matrix) { + int N = matrix.size(); + cache = vector>(N, vector(N, INT_MIN)); + + int res = INT_MAX; + for (int c = 0; c < N; c++) { + res = min(res, dfs(0, c, matrix, N)); + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} matrix + * @return {number} + */ + minFallingPathSum(matrix) { + const N = matrix.length; + const cache = Array.from({ length: N }, () => Array(N).fill(null)); + + const dfs = (r, c) => { + if (r === N) return 0; + if (c < 0 || c >= N) return Infinity; + if (cache[r][c] !== null) return cache[r][c]; + + cache[r][c] = matrix[r][c] + Math.min( + dfs(r + 1, c - 1), + dfs(r + 1, c), + dfs(r + 1, c + 1) + ); + return cache[r][c]; + }; + + let res = Infinity; + for (let c = 0; c < N; c++) { + res = Math.min(res, dfs(0, c)); + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * n)$ +* Space complexity: $O(n * n)$ + +--- + +## 3. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def minFallingPathSum(self, matrix: List[List[int]]) -> int: + N = len(matrix) + dp = matrix[0][:] + + for r in range(1, N): + leftUp = float('inf') + for c in range(N): + midUp = dp[c] + rightUp = dp[c + 1] if c < N - 1 else float('inf') + dp[c] = matrix[r][c] + min(midUp, leftUp, rightUp) + leftUp = midUp + + return min(dp) +``` + +```java +public class Solution { + public int minFallingPathSum(int[][] matrix) { + int N = matrix.length; + int[] dp = new int[N]; + for (int c = 0; c < N; c++) { + dp[c] = matrix[0][c]; + } + + for (int r = 1; r < N; r++) { + int leftUp = Integer.MAX_VALUE; + for (int c = 0; c < N; c++) { + int midUp = dp[c]; + int rightUp = (c < N - 1) ? dp[c + 1] : Integer.MAX_VALUE; + dp[c] = matrix[r][c] + Math.min(midUp, Math.min(leftUp, rightUp)); + leftUp = midUp; + } + } + + int ans = Integer.MAX_VALUE; + for (int val : dp) { + ans = Math.min(ans, val); + } + return ans; + } +} +``` + +```cpp +class Solution { +public: + int minFallingPathSum(vector>& matrix) { + int N = matrix.size(); + vector dp(N); + + for (int c = 0; c < N; c++) { + dp[c] = matrix[0][c]; + } + + for (int r = 1; r < N; r++) { + int leftUp = INT_MAX; + for (int c = 0; c < N; c++) { + int midUp = dp[c]; + int rightUp = (c < N - 1) ? dp[c + 1] : INT_MAX; + dp[c] = matrix[r][c] + min(midUp, min(leftUp, rightUp)); + leftUp = midUp; + } + } + + int ans = INT_MAX; + for (int val : dp) { + ans = min(ans, val); + } + return ans; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} matrix + * @return {number} + */ + minFallingPathSum(matrix) { + const N = matrix.length; + const dp = matrix[0].slice(); + + for (let r = 1; r < N; r++) { + let leftUp = Infinity; + for (let c = 0; c < N; c++) { + const midUp = dp[c]; + const rightUp = (c < N - 1) ? dp[c + 1] : Infinity; + dp[c] = matrix[r][c] + Math.min(midUp, leftUp, rightUp); + leftUp = midUp; + } + } + + return Math.min(...dp); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n)$ + +--- + +## 4. Dynamic Programming (In-Place) + +::tabs-start + +```python +class Solution: + def minFallingPathSum(self, matrix: List[List[int]]) -> int: + N = len(matrix) + + for r in range(1, N): + for c in range(N): + mid = matrix[r - 1][c] + left = matrix[r - 1][c - 1] if c > 0 else float("inf") + right = matrix[r - 1][c + 1] if c < N - 1 else float("inf") + matrix[r][c] = matrix[r][c] + min(mid, left, right) + + return min(matrix[-1]) +``` + +```java +public class Solution { + public int minFallingPathSum(int[][] matrix) { + int N = matrix.length; + + for (int r = 1; r < N; r++) { + for (int c = 0; c < N; c++) { + int mid = matrix[r - 1][c]; + int left = (c > 0) ? matrix[r - 1][c - 1] : Integer.MAX_VALUE; + int right = (c < N - 1) ? matrix[r - 1][c + 1] : Integer.MAX_VALUE; + matrix[r][c] = matrix[r][c] + Math.min(mid, Math.min(left, right)); + } + } + + int res = Integer.MAX_VALUE; + for (int c = 0; c < N; c++) { + res = Math.min(res, matrix[N - 1][c]); + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int minFallingPathSum(vector>& matrix) { + int N = matrix.size(); + + for (int r = 1; r < N; r++) { + for (int c = 0; c < N; c++) { + int mid = matrix[r - 1][c]; + int left = (c > 0) ? matrix[r - 1][c - 1] : INT_MAX; + int right = (c < N - 1) ? matrix[r - 1][c + 1] : INT_MAX; + matrix[r][c] = matrix[r][c] + min({mid, left, right}); + } + } + + return *min_element(matrix[N - 1].begin(), matrix[N - 1].end()); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} matrix + * @return {number} + */ + minFallingPathSum(matrix) { + const N = matrix.length; + + for (let r = 1; r < N; r++) { + for (let c = 0; c < N; c++) { + const mid = matrix[r - 1][c]; + const left = c > 0 ? matrix[r - 1][c - 1] : Infinity; + const right = c < N - 1 ? matrix[r - 1][c + 1] : Infinity; + matrix[r][c] = matrix[r][c] + Math.min(mid, left, right); + } + } + + return Math.min(...matrix[N - 1]); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(1)$ extra space. \ No newline at end of file diff --git a/articles/number-of-dice-rolls-with-target-sum.md b/articles/number-of-dice-rolls-with-target-sum.md new file mode 100644 index 000000000..442065a4a --- /dev/null +++ b/articles/number-of-dice-rolls-with-target-sum.md @@ -0,0 +1,573 @@ +## 1. Recursion + +::tabs-start + +```python +class Solution: + def numRollsToTarget(self, n: int, k: int, target: int) -> int: + MOD = 10**9 + 7 + + def count(n, target): + if n == 0: + return 1 if target == 0 else 0 + if target < 0: + return 0 + + res = 0 + for val in range(1, k + 1): + res = (res + count(n - 1, target - val)) % MOD + return res + + return count(n, target) +``` + +```java +public class Solution { + private static final int MOD = 1_000_000_007; + + public int numRollsToTarget(int n, int k, int target) { + return count(n, target, k); + } + + private int count(int n, int target, int k) { + if (n == 0) { + return target == 0 ? 1 : 0; + } + if (target < 0) { + return 0; + } + + int res = 0; + for (int val = 1; val <= k; val++) { + res = (res + count(n - 1, target - val, k)) % MOD; + } + return res; + } +} +``` + +```cpp +class Solution { +private: + const int MOD = 1e9 + 7; + +public: + int numRollsToTarget(int n, int k, int target) { + return count(n, target, k); + } + +private: + int count(int n, int target, int k) { + if (n == 0) { + return target == 0 ? 1 : 0; + } + if (target < 0) { + return 0; + } + + int res = 0; + for (int val = 1; val <= k; val++) { + res = (res + count(n - 1, target - val, k)) % MOD; + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number} k + * @param {number} target + * @return {number} + */ + numRollsToTarget(n, k, target) { + const MOD = 1e9 + 7; + + const count = (n, target) => { + if (n === 0) { + return target === 0 ? 1 : 0; + } + if (target < 0) { + return 0; + } + + let res = 0; + for (let val = 1; val <= k; val++) { + res = (res + count(n - 1, target - val)) % MOD; + } + return res; + }; + + return count(n, target); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(k ^ n)$ +* Space complexity: $O(n)$ + +> Where $n$ is the number of dices, $k$ is the number of faces each dice have, and $t$ is the target value. + +--- + +## 2. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def numRollsToTarget(self, n: int, k: int, target: int) -> int: + MOD = 10**9 + 7 + cache = {} + + def count(n, target): + if n == 0: + return 1 if target == 0 else 0 + if (n, target) in cache: + return cache[(n, target)] + + res = 0 + for val in range(1, k + 1): + if target - val >= 0: + res = (res + count(n - 1, target - val)) % MOD + cache[(n, target)] = res + return res + + return count(n, target) +``` + +```java +public class Solution { + private static final int MOD = 1_000_000_007; + private int[][] dp; + + public int numRollsToTarget(int n, int k, int target) { + dp = new int[n + 1][target + 1]; + for (int[] row : dp) { + Arrays.fill(row, -1); + } + return count(n, target, k); + } + + private int count(int n, int target, int k) { + if (n == 0) { + return target == 0 ? 1 : 0; + } + if (target < 0) { + return 0; + } + if (dp[n][target] != -1) { + return dp[n][target]; + } + + int res = 0; + for (int val = 1; val <= k; val++) { + if (target - val >= 0) { + res = (res + count(n - 1, target - val, k)) % MOD; + } + } + dp[n][target] = res; + return res; + } +} +``` + +```cpp +class Solution { +private: + const int MOD = 1e9 + 7; + vector> dp; + +public: + int numRollsToTarget(int n, int k, int target) { + dp = vector>(n + 1, vector(target + 1, -1)); + return count(n, target, k); + } + +private: + int count(int n, int target, int k) { + if (n == 0) { + return target == 0 ? 1 : 0; + } + if (target < 0) { + return 0; + } + if (dp[n][target] != -1) { + return dp[n][target]; + } + + int res = 0; + for (int val = 1; val <= k; val++) { + if (target - val >= 0) { + res = (res + count(n - 1, target - val, k)) % MOD; + } + } + dp[n][target] = res; + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number} k + * @param {number} target + * @return {number} + */ + numRollsToTarget(n, k, target) { + const MOD = 1e9 + 7; + const dp = Array.from({ length: n + 1 }, () => Array(target + 1).fill(-1)); + + const count = (n, target) => { + if (n === 0) { + return target === 0 ? 1 : 0; + } + if (dp[n][target] !== -1) { + return dp[n][target]; + } + + let res = 0; + for (let val = 1; val <= k; val++) { + if (target - val >= 0) { + res = (res + count(n - 1, target - val)) % MOD; + } + } + dp[n][target] = res; + return res; + }; + + return count(n, target); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * t * k)$ +* Space complexity: $O(n * t)$ + +> Where $n$ is the number of dices, $k$ is the number of faces each dice have, and $t$ is the target value. + +--- + +## 3. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def numRollsToTarget(self, n: int, k: int, target: int) -> int: + MOD = 10**9 + 7 + dp = [[0] * (target + 1) for _ in range(n + 1)] + dp[0][0] = 1 + + for i in range(1, n + 1): + for val in range(1, k + 1): + for t in range(val, target + 1): + dp[i][t] = (dp[i][t] + dp[i - 1][t - val]) % MOD + + return dp[n][target] +``` + +```java +public class Solution { + public int numRollsToTarget(int n, int k, int target) { + int MOD = 1_000_000_007; + int[][] dp = new int[n + 1][target + 1]; + dp[0][0] = 1; + + for (int i = 1; i <= n; i++) { + for (int val = 1; val <= k; val++) { + for (int t = val; t <= target; t++) { + dp[i][t] = (dp[i][t] + dp[i - 1][t - val]) % MOD; + } + } + } + + return dp[n][target]; + } +} +``` + +```cpp +class Solution { +public: + int numRollsToTarget(int n, int k, int target) { + const int MOD = 1e9 + 7; + vector> dp(n + 1, vector(target + 1, 0)); + dp[0][0] = 1; + + for (int i = 1; i <= n; i++) { + for (int val = 1; val <= k; val++) { + for (int t = val; t <= target; t++) { + dp[i][t] = (dp[i][t] + dp[i - 1][t - val]) % MOD; + } + } + } + + return dp[n][target]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number} k + * @param {number} target + * @return {number} + */ + numRollsToTarget(n, k, target) { + const MOD = 1e9 + 7; + const dp = Array.from({ length: n + 1 }, () => Array(target + 1).fill(0)); + dp[0][0] = 1; + + for (let i = 1; i <= n; i++) { + for (let val = 1; val <= k; val++) { + for (let t = val; t <= target; t++) { + dp[i][t] = (dp[i][t] + dp[i - 1][t - val]) % MOD; + } + } + } + + return dp[n][target]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * t * k)$ +* Space complexity: $O(n * t)$ + +> Where $n$ is the number of dices, $k$ is the number of faces each dice have, and $t$ is the target value. + +--- + +## 4. Dynamic Programming (Space Optimized) + +::tabs-start + +```python +class Solution: + def numRollsToTarget(self, n: int, k: int, target: int) -> int: + dp = [0] * (target + 1) + dp[0] = 1 + MOD = 10**9 + 7 + + for dice in range(n): + next_dp = [0] * (target + 1) + for val in range(1, k + 1): + for total in range(val, target + 1): + next_dp[total] = (next_dp[total] + dp[total - val]) % MOD + dp = next_dp + + return dp[target] +``` + +```java +public class Solution { + public int numRollsToTarget(int n, int k, int target) { + int[] dp = new int[target + 1]; + dp[0] = 1; + int MOD = 1_000_000_007; + + for (int dice = 0; dice < n; dice++) { + int[] nextDp = new int[target + 1]; + for (int val = 1; val <= k; val++) { + for (int total = val; total <= target; total++) { + nextDp[total] = (nextDp[total] + dp[total - val]) % MOD; + } + } + dp = nextDp; + } + + return dp[target]; + } +} +``` + +```cpp +class Solution { +public: + int numRollsToTarget(int n, int k, int target) { + const int MOD = 1e9 + 7; + vector dp(target + 1, 0); + dp[0] = 1; + + for (int dice = 0; dice < n; dice++) { + vector nextDp(target + 1, 0); + for (int val = 1; val <= k; val++) { + for (int total = val; total <= target; total++) { + nextDp[total] = (nextDp[total] + dp[total - val]) % MOD; + } + } + dp = nextDp; + } + + return dp[target]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number} k + * @param {number} target + * @return {number} + */ + numRollsToTarget(n, k, target) { + const MOD = 1e9 + 7; + let dp = Array(target + 1).fill(0); + dp[0] = 1; + + for (let dice = 0; dice < n; dice++) { + const nextDp = Array(target + 1).fill(0); + for (let val = 1; val <= k; val++) { + for (let total = val; total <= target; total++) { + nextDp[total] = (nextDp[total] + dp[total - val]) % MOD; + } + } + dp = nextDp; + } + + return dp[target]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * t * k)$ +* Space complexity: $O(t)$ + +> Where $n$ is the number of dices, $k$ is the number of faces each dice have, and $t$ is the target value. + +--- + +## 5. Dynamic Programming (Optimal) + +::tabs-start + +```python +class Solution: + def numRollsToTarget(self, n: int, k: int, target: int) -> int: + MOD = 10**9 + 7 + dp = [0] * (target + 1) + dp[0] = 1 + + for dice in range(n): + for t in range(target, -1, -1): + ways = dp[t] + dp[t] = 0 + if ways: + for val in range(1, min(k + 1, target - t + 1)): + dp[t + val] = (dp[t + val] + ways) % MOD + + return dp[target] +``` + +```java +public class Solution { + public int numRollsToTarget(int n, int k, int target) { + int MOD = 1_000_000_007; + int[] dp = new int[target + 1]; + dp[0] = 1; + + for (int dice = 0; dice < n; dice++) { + for (int t = target; t >= 0; t--) { + int ways = dp[t]; + dp[t] = 0; + if (ways > 0) { + for (int val = 1; val <= Math.min(k, target - t); val++) { + dp[t + val] = (dp[t + val] + ways) % MOD; + } + } + } + } + + return dp[target]; + } +} +``` + +```cpp +class Solution { +public: + int numRollsToTarget(int n, int k, int target) { + const int MOD = 1e9 + 7; + vector dp(target + 1, 0); + dp[0] = 1; + + for (int dice = 0; dice < n; dice++) { + for (int t = target; t >= 0; t--) { + int ways = dp[t]; + dp[t] = 0; + if (ways > 0) { + for (int val = 1; val <= min(k, target - t); val++) { + dp[t + val] = (dp[t + val] + ways) % MOD; + } + } + } + } + + return dp[target]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number} k + * @param {number} target + * @return {number} + */ + numRollsToTarget(n, k, target) { + const MOD = 1e9 + 7; + const dp = Array(target + 1).fill(0); + dp[0] = 1; + + for (let dice = 0; dice < n; dice++) { + for (let t = target; t >= 0; t--) { + const ways = dp[t]; + dp[t] = 0; + if (ways > 0) { + for (let val = 1; val <= Math.min(k, target - t); val++) { + dp[t + val] = (dp[t + val] + ways) % MOD; + } + } + } + } + + return dp[target]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * t * k)$ +* Space complexity: $O(t)$ + +> Where $n$ is the number of dices, $k$ is the number of faces each dice have, and $t$ is the target value. \ No newline at end of file diff --git a/articles/number-of-music-playlists.md b/articles/number-of-music-playlists.md new file mode 100644 index 000000000..284e35bae --- /dev/null +++ b/articles/number-of-music-playlists.md @@ -0,0 +1,340 @@ +## 1. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def numMusicPlaylists(self, n: int, goal: int, k: int) -> int: + mod = 10**9 + 7 + dp = {} + + def count(cur_goal, old_songs): + if cur_goal == 0 and old_songs == n: + return 1 + if cur_goal == 0 or old_songs > n: + return 0 + if (cur_goal, old_songs) in dp: + return dp[(cur_goal, old_songs)] + + res = (n - old_songs) * count(cur_goal - 1, old_songs + 1) + if old_songs > k: + res += (old_songs - k) * count(cur_goal - 1, old_songs) + dp[(cur_goal, old_songs)] = res % mod + return dp[(cur_goal, old_songs)] + + return count(goal, 0) +``` + +```java +public class Solution { + private static final int MOD = 1_000_000_007; + private int[][] dp; + + public int numMusicPlaylists(int n, int goal, int k) { + dp = new int[goal + 1][n + 1]; + for (int[] row : dp) { + Arrays.fill(row, -1); + } + return count(goal, 0, n, k); + } + + private int count(int curGoal, int oldSongs, int n, int k) { + if (curGoal == 0 && oldSongs == n) return 1; + if (curGoal == 0 || oldSongs > n) return 0; + if (dp[curGoal][oldSongs] != -1) return dp[curGoal][oldSongs]; + + long res = (long) (n - oldSongs) * count(curGoal - 1, oldSongs + 1, n, k) % MOD; + if (oldSongs > k) { + res = (res + (long) (oldSongs - k) * count(curGoal - 1, oldSongs, n, k)) % MOD; + } + dp[curGoal][oldSongs] = (int) res; + return dp[curGoal][oldSongs]; + } +} +``` + +```cpp +class Solution { +private: + static const int MOD = 1e9 + 7; + vector> dp; + + int count(int curGoal, int oldSongs, int n, int k) { + if (curGoal == 0 && oldSongs == n) return 1; + if (curGoal == 0 || oldSongs > n) return 0; + if (dp[curGoal][oldSongs] != -1) return dp[curGoal][oldSongs]; + + long long res = (long long)(n - oldSongs) * count(curGoal - 1, oldSongs + 1, n, k) % MOD; + if (oldSongs > k) { + res = (res + (long long)(oldSongs - k) * count(curGoal - 1, oldSongs, n, k)) % MOD; + } + dp[curGoal][oldSongs] = res; + return dp[curGoal][oldSongs]; + } + +public: + int numMusicPlaylists(int n, int goal, int k) { + dp.assign(goal + 1, vector(n + 1, -1)); + return count(goal, 0, n, k); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number} goal + * @param {number} k + * @return {number} + */ + numMusicPlaylists(n, goal, k) { + const MOD = 1e9 + 7; + const dp = Array.from({ length: goal + 1 }, () => Array(n + 1).fill(-1)); + + const count = (curGoal, oldSongs) => { + if (curGoal === 0 && oldSongs === n) return 1; + if (curGoal === 0 || oldSongs > n) return 0; + if (dp[curGoal][oldSongs] !== -1) return dp[curGoal][oldSongs]; + + let res = ((n - oldSongs) * count(curGoal - 1, oldSongs + 1)) % MOD; + if (oldSongs > k) { + res = (res + ((oldSongs - k) * count(curGoal - 1, oldSongs)) % MOD) % MOD; + } + dp[curGoal][oldSongs] = res; + return res; + }; + + return count(goal, 0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(g * n)$ +* Space complexity: $O(g * n)$ + +> Where $g$ is the number of songs to listen and $n$ is the number of different songs. + +--- + +## 2. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def numMusicPlaylists(self, n: int, goal: int, k: int) -> int: + mod = 10**9 + 7 + dp = [[0] * (n + 1) for _ in range(goal + 1)] + dp[0][0] = 1 + + for cur_goal in range(1, goal + 1): + for old_songs in range(1, n + 1): + res = (dp[cur_goal - 1][old_songs - 1] * (n - old_songs + 1)) % mod + if old_songs > k: + res = (res + dp[cur_goal - 1][old_songs] * (old_songs - k)) % mod + dp[cur_goal][old_songs] = res + + return dp[goal][n] +``` + +```java +public class Solution { + private static final int MOD = 1_000_000_007; + + public int numMusicPlaylists(int n, int goal, int k) { + int[][] dp = new int[goal + 1][n + 1]; + dp[0][0] = 1; + + for (int curGoal = 1; curGoal <= goal; curGoal++) { + for (int oldSongs = 1; oldSongs <= n; oldSongs++) { + int res = (int) (((long) dp[curGoal - 1][oldSongs - 1] * (n - oldSongs + 1)) % MOD); + if (oldSongs > k) { + res = (res + (int) (((long) dp[curGoal - 1][oldSongs] * (oldSongs - k)) % MOD)) % MOD; + } + dp[curGoal][oldSongs] = res; + } + } + + return dp[goal][n]; + } +} +``` + +```cpp +class Solution { +private: + static const int MOD = 1e9 + 7; + +public: + int numMusicPlaylists(int n, int goal, int k) { + vector> dp(goal + 1, vector(n + 1, 0)); + dp[0][0] = 1; + + for (int curGoal = 1; curGoal <= goal; curGoal++) { + for (int oldSongs = 1; oldSongs <= n; oldSongs++) { + int res = (long long) dp[curGoal - 1][oldSongs - 1] * (n - oldSongs + 1) % MOD; + if (oldSongs > k) { + res = (res + (long long) dp[curGoal - 1][oldSongs] * (oldSongs - k) % MOD) % MOD; + } + dp[curGoal][oldSongs] = res; + } + } + + return dp[goal][n]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number} goal + * @param {number} k + * @return {number} + */ + numMusicPlaylists(n, goal, k) { + const MOD = 1e9 + 7; + const dp = Array.from({ length: goal + 1 }, () => Array(n + 1).fill(0)); + dp[0][0] = 1; + + for (let curGoal = 1; curGoal <= goal; curGoal++) { + for (let oldSongs = 1; oldSongs <= n; oldSongs++) { + let res = (dp[curGoal - 1][oldSongs - 1] * (n - oldSongs + 1)) % MOD; + if (oldSongs > k) { + res = (res + dp[curGoal - 1][oldSongs] * (oldSongs - k)) % MOD; + } + dp[curGoal][oldSongs] = res; + } + } + + return dp[goal][n]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(g * n)$ +* Space complexity: $O(g * n)$ + +> Where $g$ is the number of songs to listen and $n$ is the number of different songs. + +--- + +## 3. Dynamic Programming (Space Optimized) + +::tabs-start + +```python +class Solution: + def numMusicPlaylists(self, n: int, goal: int, k: int) -> int: + mod = 10**9 + 7 + dp = [0] * (n + 1) + + for cur_goal in range(1, goal + 1): + prev = 1 if cur_goal == 1 else 0 + for old_songs in range(1, n + 1): + res = (prev * (n - old_songs + 1)) % mod + if old_songs > k: + res = (res + dp[old_songs] * (old_songs - k)) % mod + prev = dp[old_songs] + dp[old_songs] = res + + return dp[n] +``` + +```java +public class Solution { + private static final int MOD = 1_000_000_007; + + public int numMusicPlaylists(int n, int goal, int k) { + int[] dp = new int[n + 1]; + + for (int curGoal = 1; curGoal <= goal; curGoal++) { + int prev = curGoal == 1 ? 1 : 0; + for (int oldSongs = 1; oldSongs <= n; oldSongs++) { + int res = (int) (((long) prev * (n - oldSongs + 1)) % MOD); + if (oldSongs > k) { + res = (res + (int) (((long) dp[oldSongs] * (oldSongs - k)) % MOD)) % MOD; + } + prev = dp[oldSongs]; + dp[oldSongs] = res; + } + } + + return dp[n]; + } +} +``` + +```cpp +class Solution { +private: + static const int MOD = 1e9 + 7; + +public: + int numMusicPlaylists(int n, int goal, int k) { + vector dp(n + 1); + + for (int curGoal = 1; curGoal <= goal; curGoal++) { + int prev = curGoal == 1 ? 1: 0; + for (int oldSongs = 1; oldSongs <= n; oldSongs++) { + int res = (long long) prev * (n - oldSongs + 1) % MOD; + if (oldSongs > k) { + res = (res + (long long) dp[oldSongs] * (oldSongs - k) % MOD) % MOD; + } + prev = dp[oldSongs]; + dp[oldSongs] = res; + } + } + + return dp[n]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number} goal + * @param {number} k + * @return {number} + */ + numMusicPlaylists(n, goal, k) { + const MOD = 1e9 + 7; + const dp = new Array(n + 1).fill(0); + + for (let curGoal = 1; curGoal <= goal; curGoal++) { + let prev = curGoal === 1 ? 1 : 0; + for (let oldSongs = 1; oldSongs <= n; oldSongs++) { + let res = (prev * (n - oldSongs + 1)) % MOD; + if (oldSongs > k) { + res = (res + dp[oldSongs] * (oldSongs - k)) % MOD; + } + prev = dp[oldSongs]; + dp[oldSongs] = res; + } + } + + return dp[n]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(g * n)$ +* Space complexity: $O(n)$ + +> Where $g$ is the number of songs to listen and $n$ is the number of different songs. \ No newline at end of file diff --git a/articles/number-of-ways-to-form-a-target-string-given-a-dictionary.md b/articles/number-of-ways-to-form-a-target-string-given-a-dictionary.md new file mode 100644 index 000000000..878690a7c --- /dev/null +++ b/articles/number-of-ways-to-form-a-target-string-given-a-dictionary.md @@ -0,0 +1,568 @@ +## 1. Recursion + +::tabs-start + +```python +class Solution: + def numWays(self, words: List[str], target: str) -> int: + mod = 10**9 + 7 + n, m = len(target), len(words[0]) + + def dfs(i, k): + if i == n: + return 1 + if k == m: + return 0 + + res = dfs(i, k + 1) + for w in words: + if w[k] != target[i]: + continue + res = (res + dfs(i + 1, k + 1)) % mod + return res + + return dfs(0, 0) +``` + +```java +public class Solution { + private static final int MOD = 1_000_000_007; + + public int numWays(String[] words, String target) { + return dfs(words, target, 0, 0); + } + + private int dfs(String[] words, String target, int i, int k) { + if (i == target.length()) return 1; + if (k == words[0].length()) return 0; + + int res = dfs(words, target, i, k + 1); + for (String w : words) { + if (w.charAt(k) != target.charAt(i)) { + continue; + } + res = (int) (res + 0L + dfs(words, target, i + 1, k + 1)) % MOD; + } + + return res; + } +} +``` + +```cpp +class Solution { +private: + static const int MOD = 1e9 + 7; + + int dfs(vector& words, string target, int i, int k) { + if (i == target.length()) return 1; + if (k == words[0].length()) return 0; + + int res = dfs(words, target, i, k + 1); + for (string& w : words) { + if (w[k] != target[i]) { + continue; + } + res = (int) (res + 0LL + dfs(words, target, i + 1, k + 1)) % MOD; + } + + return res; + } + +public: + int numWays(vector& words, string target) { + return dfs(words, target, 0, 0); + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} words + * @param {string} target + * @return {number} + */ + numWays(words, target) { + const n = target.length; + const m = words[0].length; + const MOD = 1e9 + 7; + + const dfs = (i, k) => { + if (i === n) return 1; + if (k === m) return 0; + + let res = dfs(i, k + 1); + for (const w of words) { + if (w[k] != target[i]) { + continue; + } + res = (res + dfs(i + 1, k + 1)) % MOD; + } + + return res; + }; + + return dfs(0, 0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(N ^ m)$ +* Space complexity: $O(m)$ + +> Where $N$ is the number of words, $m$ is the length of each word, and $n$ is the length of the $target$ string. + +--- + +## 2. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def numWays(self, words: List[str], target: str) -> int: + mod = 10**9 + 7 + cnt = defaultdict(int) # (index, char) --> count among all words + for w in words: + for i, c in enumerate(w): + cnt[(i, c)] += 1 + + dp = {} + + def dfs(i, k): + if i == len(target): + return 1 + if k == len(words[0]): + return 0 + if (i, k) in dp: + return dp[(i, k)] + + c = target[i] + dp[(i, k)] = dfs(i, k + 1) # skip k position + dp[(i, k)] += cnt[(k, c)] * dfs(i + 1, k + 1) + dp[(i, k)] %= mod + return dp[(i, k)] + + return dfs(0, 0) +``` + +```java +public class Solution { + private static final int MOD = 1_000_000_007; + private int[][] dp; + private int[][] cnt; + + public int numWays(String[] words, String target) { + int n = target.length(), m = words[0].length(); + cnt = new int[m][26]; + + for (String word : words) { + for (int i = 0; i < word.length(); i++) { + cnt[i][word.charAt(i) - 'a']++; + } + } + + dp = new int[n + 1][m + 1]; + for (int[] row : dp) { + Arrays.fill(row, -1); + } + + return dfs(0, 0, target, n, m); + } + + private int dfs(int i, int k, String target, int n, int m) { + if (i == n) return 1; + if (k == m) return 0; + if (dp[i][k] != -1) return dp[i][k]; + + int c = target.charAt(i) - 'a'; + dp[i][k] = dfs(i, k + 1, target, n, m); // Skip k position + dp[i][k] = (int) ((dp[i][k] + (long) cnt[k][c] * dfs(i + 1, k + 1, target, n, m)) % MOD); + return dp[i][k]; + } +} +``` + +```cpp +class Solution { +private: + static const int MOD = 1e9 + 7; + vector> dp; + vector> cnt; + +public: + int numWays(vector& words, string target) { + int n = target.size(), m = words[0].size(); + cnt = vector>(m, vector(26, 0)); + + for (const string& word : words) { + for (int i = 0; i < word.size(); i++) { + cnt[i][word[i] - 'a']++; + } + } + + dp = vector>(n + 1, vector(m + 1, -1)); + return dfs(0, 0, target, n, m); + } + +private: + int dfs(int i, int k, const string& target, int n, int m) { + if (i == n) return 1; + if (k == m) return 0; + if (dp[i][k] != -1) return dp[i][k]; + + int c = target[i] - 'a'; + dp[i][k] = dfs(i, k + 1, target, n, m); // Skip k position + dp[i][k] = (dp[i][k] + (long long) cnt[k][c] * dfs(i + 1, k + 1, target, n, m)) % MOD; + return dp[i][k]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} words + * @param {string} target + * @return {number} + */ + numWays(words, target) { + const MOD = 1e9 + 7; + const n = target.length, m = words[0].length; + const cnt = Array.from({ length: m }, () => Array(26).fill(0)); + + for (const word of words) { + for (let i = 0; i < word.length; i++) { + cnt[i][word.charCodeAt(i) - 97]++; + } + } + + const dp = Array.from({ length: n + 1 }, () => Array(m + 1).fill(-1)); + + const dfs = (i, k) => { + if (i === n) return 1; + if (k === m) return 0; + if (dp[i][k] !== -1) return dp[i][k]; + + const c = target.charCodeAt(i) - 97; + dp[i][k] = dfs(i, k + 1); // Skip k position + dp[i][k] = (dp[i][k] + cnt[k][c] * dfs(i + 1, k + 1)) % MOD; + return dp[i][k]; + }; + + return dfs(0, 0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * (n + N))$ +* Space complexity: $O(n * m)$ + +> Where $N$ is the number of words, $m$ is the length of each word, and $n$ is the length of the $target$ string. + +--- + +## 3. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def numWays(self, words: List[str], target: str) -> int: + MOD = 10**9 + 7 + n, m = len(target), len(words[0]) + + cnt = [[0] * 26 for _ in range(m)] + for word in words: + for i, c in enumerate(word): + cnt[i][ord(c) - ord('a')] += 1 + + dp = [[0] * (m + 1) for _ in range(n + 1)] + dp[n][m] = 1 + + for i in range(n, -1, -1): + for k in range(m - 1, -1, -1): + dp[i][k] = dp[i][k + 1] + if i < n: + c = ord(target[i]) - ord('a') + dp[i][k] = (dp[i][k] + cnt[k][c] * dp[i + 1][k + 1]) % MOD + + return dp[0][0] +``` + +```java +public class Solution { + private static final int MOD = 1_000_000_007; + + public int numWays(String[] words, String target) { + int n = target.length(), m = words[0].length(); + + int[][] cnt = new int[m][26]; + for (String word : words) { + for (int i = 0; i < word.length(); i++) { + cnt[i][word.charAt(i) - 'a']++; + } + } + + int[][] dp = new int[n + 1][m + 1]; + dp[n][m] = 1; + + for (int i = n; i >= 0; i--) { + for (int k = m - 1; k >= 0; k--) { + dp[i][k] = dp[i][k + 1]; + if (i < n) { + int c = target.charAt(i) - 'a'; + dp[i][k] = (int) ((dp[i][k] + (long) cnt[k][c] * dp[i + 1][k + 1]) % MOD); + } + } + } + + return dp[0][0]; + } +} +``` + +```cpp +class Solution { +private: + static const int MOD = 1e9 + 7; + +public: + int numWays(vector& words, string target) { + int n = target.size(), m = words[0].size(); + + vector> cnt(m, vector(26, 0)); + for (const string& word : words) { + for (int i = 0; i < word.size(); i++) { + cnt[i][word[i] - 'a']++; + } + } + + vector> dp(n + 1, vector(m + 1, 0)); + dp[n][m] = 1; + + for (int i = n; i >= 0; i--) { + for (int k = m - 1; k >= 0; k--) { + dp[i][k] = dp[i][k + 1]; + if (i < n) { + int c = target[i] - 'a'; + dp[i][k] = (dp[i][k] + (long long) cnt[k][c] * dp[i + 1][k + 1]) % MOD; + } + } + } + + return dp[0][0]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} words + * @param {string} target + * @return {number} + */ + numWays(words, target) { + const MOD = 1e9 + 7; + const n = target.length, m = words[0].length; + + const cnt = Array.from({ length: m }, () => Array(26).fill(0)); + for (const word of words) { + for (let i = 0; i < word.length; i++) { + cnt[i][word.charCodeAt(i) - 'a'.charCodeAt(0)]++; + } + } + + const dp = Array.from({ length: n + 1 }, () => Array(m + 1).fill(0)); + dp[n][m] = 1; + + for (let i = n; i >= 0; i--) { + for (let k = m - 1; k >= 0; k--) { + dp[i][k] = dp[i][k + 1]; + if (i < n) { + const c = target.charCodeAt(i) - 'a'.charCodeAt(0); + dp[i][k] = (dp[i][k] + cnt[k][c] * dp[i + 1][k + 1]) % MOD; + } + } + } + + return dp[0][0]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * (n + N))$ +* Space complexity: $O(n * m)$ + +> Where $N$ is the number of words, $m$ is the length of each word, and $n$ is the length of the $target$ string. + +--- + +## 4. Dynamic Programming (Space Optimized) + +::tabs-start + +```python +class Solution: + def numWays(self, words: List[str], target: str) -> int: + MOD = 10**9 + 7 + n, m = len(target), len(words[0]) + + cnt = [[0] * 26 for _ in range(m)] + for word in words: + for i, c in enumerate(word): + cnt[i][ord(c) - ord('a')] += 1 + + dp = [0] * (m + 1) + dp[m] = 1 + + for i in range(n, -1, -1): + nxt = 1 if i == n - 1 else 0 + for k in range(m - 1, -1, -1): + cur = dp[k] + dp[k] = dp[k + 1] + if i < n: + c = ord(target[i]) - ord('a') + dp[k] = (dp[k] + cnt[k][c] * nxt) % MOD + nxt = cur + dp[m] = 0 + + return dp[0] +``` + +```java +public class Solution { + private static final int MOD = 1_000_000_007; + + public int numWays(String[] words, String target) { + int n = target.length(), m = words[0].length(); + + int[][] cnt = new int[m][26]; + for (String word : words) { + for (int i = 0; i < word.length(); i++) { + cnt[i][word.charAt(i) - 'a']++; + } + } + + int[] dp = new int[m + 1]; + dp[m] = 1; + + for (int i = n; i >= 0; i--) { + int nxt = i == n - 1 ? 1 : 0; + for (int k = m - 1; k >= 0; k--) { + int cur = dp[k]; + dp[k] = dp[k + 1]; + if (i < n) { + int c = target.charAt(i) - 'a'; + dp[k] = (int) ((dp[k] + (long) cnt[k][c] * nxt) % MOD); + } + nxt = cur; + } + dp[m] = 0; + } + + return dp[0]; + } +} +``` + +```cpp +class Solution { +private: + static const int MOD = 1e9 + 7; + +public: + int numWays(vector& words, string target) { + int n = target.size(), m = words[0].size(); + + vector> cnt(m, vector(26, 0)); + for (const string& word : words) { + for (int i = 0; i < word.size(); i++) { + cnt[i][word[i] - 'a']++; + } + } + + vector dp(m + 1); + dp[m] = 1; + + for (int i = n; i >= 0; i--) { + int nxt = i == n - 1 ? 1 : 0; + for (int k = m - 1; k >= 0; k--) { + int cur = dp[k]; + dp[k] = dp[k + 1]; + if (i < n) { + int c = target[i] - 'a'; + dp[k] = (dp[k] + (long long) cnt[k][c] * nxt) % MOD; + } + nxt = cur; + } + dp[m] = 0; + } + + return dp[0]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} words + * @param {string} target + * @return {number} + */ + numWays(words, target) { + const MOD = 1e9 + 7; + const n = target.length, m = words[0].length; + + const cnt = Array.from({ length: m }, () => Array(26).fill(0)); + for (const word of words) { + for (let i = 0; i < word.length; i++) { + cnt[i][word.charCodeAt(i) - 'a'.charCodeAt(0)]++; + } + } + + const dp = new Array(m + 1).fill(0); + dp[m] = 1; + + for (let i = n; i >= 0; i--) { + let nxt = i === n - 1 ? 1 : 0; + for (let k = m - 1; k >= 0; k--) { + const cur = dp[k]; + dp[k] = dp[k + 1]; + if (i < n) { + const c = target.charCodeAt(i) - 'a'.charCodeAt(0); + dp[k] = (dp[k] + cnt[k][c] * nxt) % MOD; + } + nxt = cur; + } + dp[m] = 0; + } + + return dp[0]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * (n + N))$ +* Space complexity: $O(m)$ + +> Where $N$ is the number of words, $m$ is the length of each word, and $n$ is the length of the $target$ string. \ No newline at end of file diff --git a/articles/number-of-ways-to-rearrange-sticks-with-k-sticks-visible.md b/articles/number-of-ways-to-rearrange-sticks-with-k-sticks-visible.md new file mode 100644 index 000000000..d192c77b6 --- /dev/null +++ b/articles/number-of-ways-to-rearrange-sticks-with-k-sticks-visible.md @@ -0,0 +1,375 @@ +## 1. Recursion + +::tabs-start + +```python +class Solution: + def rearrangeSticks(self, n: int, k: int) -> int: + MOD = 10**9 + 7 + + def dfs(N, K): + if N == K: + return 1 + if N == 0 or K == 0: + return 0 + return (dfs(N - 1, K - 1) + (N - 1) * dfs(N - 1, K)) % MOD + + return dfs(n, k) +``` + +```java +public class Solution { + private static final int MOD = 1_000_000_007; + + public int rearrangeSticks(int n, int k) { + return dfs(n, k); + } + + private int dfs(int N, int K) { + if (N == K) return 1; + if (N == 0 || K == 0) return 0; + return (int) ((dfs(N - 1, K - 1) + (long) (N - 1) * dfs(N - 1, K)) % MOD); + } +} +``` + +```cpp +class Solution { +private: + static const int MOD = 1e9 + 7; + + int dfs(int N, int K) { + if (N == K) return 1; + if (N == 0 || K == 0) return 0; + return (dfs(N - 1, K - 1) + (long long)(N - 1) * dfs(N - 1, K) % MOD) % MOD; + } + +public: + int rearrangeSticks(int n, int k) { + return dfs(n, k); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number} k + * @return {number} + */ + rearrangeSticks(n, k) { + const MOD = 1e9 + 7; + + const dfs = (N, K) => { + if (N === K) return 1; + if (N === 0 || K === 0) return 0; + return (dfs(N - 1, K - 1) + (N - 1) * dfs(N - 1, K)) % MOD; + }; + + return dfs(n, k); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(2 ^ n)$ +* Space complexity: $O(n)$ for recursion stack. + +--- + +## 2. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def rearrangeSticks(self, n: int, k: int) -> int: + MOD = 10**9 + 7 + dp = [[-1] * (k + 1) for _ in range(n + 1)] + + def dfs(N, K): + if N == K: + return 1 + if N == 0 or K == 0: + return 0 + if dp[N][K] != -1: + return dp[N][K] + dp[N][K] = (dfs(N - 1, K - 1) + (N - 1) * dfs(N - 1, K)) % MOD + return dp[N][K] + + return dfs(n, k) +``` + +```java +public class Solution { + private static final int MOD = 1_000_000_007; + private int[][] dp; + + public int rearrangeSticks(int n, int k) { + dp = new int[n + 1][k + 1]; + for (int[] row : dp) { + Arrays.fill(row, -1); + } + return dfs(n, k); + } + + private int dfs(int N, int K) { + if (N == K) return 1; + if (N == 0 || K == 0) return 0; + if (dp[N][K] != -1) return dp[N][K]; + dp[N][K] = (int) ((dfs(N - 1, K - 1) + (long) (N - 1) * dfs(N - 1, K)) % MOD); + return dp[N][K]; + } +} +``` + +```cpp +class Solution { +private: + static const int MOD = 1e9 + 7; + vector> dp; + + int dfs(int N, int K) { + if (N == K) return 1; + if (N == 0 || K == 0) return 0; + if (dp[N][K] != -1) return dp[N][K]; + dp[N][K] = (dfs(N - 1, K - 1) + (long long)(N - 1) * dfs(N - 1, K) % MOD) % MOD; + return dp[N][K]; + } + +public: + int rearrangeSticks(int n, int k) { + dp = vector>(n + 1, vector(k + 1, -1)); + return dfs(n, k); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number} k + * @return {number} + */ + rearrangeSticks(n, k) { + const MOD = 1e9 + 7; + const dp = Array.from({ length: n + 1 }, () => Array(k + 1).fill(-1)); + + const dfs = (N, K) => { + if (N === K) return 1; + if (N === 0 || K === 0) return 0; + if (dp[N][K] !== -1) return dp[N][K]; + dp[N][K] = (dfs(N - 1, K - 1) + (N - 1) * dfs(N - 1, K)) % MOD; + return dp[N][K]; + }; + + return dfs(n, k); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * k)$ +* Space complexity: $O(n * k)$ + +> Where $n$ represents the total number of sticks, and $k$ denotes the number of sticks that must be visible from the left side. + +--- + +## 3. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def rearrangeSticks(self, n: int, k: int) -> int: + MOD = 10**9 + 7 + dp = [[0] * (k + 1) for _ in range(n + 1)] + dp[1][1] = 1 + + for N in range(2, n + 1): + for K in range(1, k + 1): + dp[N][K] = (dp[N - 1][K - 1] + (N - 1) * dp[N - 1][K]) % MOD + + return dp[n][k] +``` + +```java +public class Solution { + private static final int MOD = 1_000_000_007; + + public int rearrangeSticks(int n, int k) { + int[][] dp = new int[n + 1][k + 1]; + dp[1][1] = 1; + + for (int N = 2; N <= n; N++) { + for (int K = 1; K <= k; K++) { + dp[N][K] = (int) ((dp[N - 1][K - 1] + (long) (N - 1) * dp[N - 1][K]) % MOD); + } + } + + return dp[n][k]; + } +} +``` + +```cpp +class Solution { +public: + int rearrangeSticks(int n, int k) { + const int MOD = 1e9 + 7; + vector> dp(n + 1, vector(k + 1, 0)); + dp[1][1] = 1; + + for (int N = 2; N <= n; N++) { + for (int K = 1; K <= k; K++) { + dp[N][K] = (dp[N - 1][K - 1] + (N - 1) * 1LL * dp[N - 1][K]) % MOD; + } + } + + return dp[n][k]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number} k + * @return {number} + */ + rearrangeSticks(n, k) { + const MOD = 1e9 + 7; + const dp = Array.from({ length: n + 1 }, () => Array(k + 1).fill(0)); + dp[1][1] = 1; + + for (let N = 2; N <= n; N++) { + for (let K = 1; K <= k; K++) { + dp[N][K] = (dp[N - 1][K - 1] + (N - 1) * dp[N - 1][K]) % MOD; + } + } + + return dp[n][k]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * k)$ +* Space complexity: $O(n * k)$ + +> Where $n$ represents the total number of sticks, and $k$ denotes the number of sticks that must be visible from the left side. + +--- + +## 4. Dynamic Programming (Space Optimized) + +::tabs-start + +```python +class Solution: + def rearrangeSticks(self, n: int, k: int) -> int: + MOD = 10**9 + 7 + dp = [0] * (k + 1) + dp[1] = 1 + + for N in range(2, n + 1): + prev = 0 + for K in range(1, k + 1): + tmp = dp[K] + dp[K] = (prev + (N - 1) * dp[K]) % MOD + prev = tmp + + return dp[k] +``` + +```java +public class Solution { + private static final int MOD = 1_000_000_007; + + public int rearrangeSticks(int n, int k) { + int[] dp = new int[k + 1]; + dp[1] = 1; + + for (int N = 2; N <= n; N++) { + int prev = 0; + for (int K = 1; K <= k; K++) { + int tmp = dp[K]; + dp[K] = (int) ((prev + (long) (N - 1) * dp[K]) % MOD); + prev = tmp; + } + } + + return dp[k]; + } +} +``` + +```cpp +class Solution { +public: + int rearrangeSticks(int n, int k) { + const int MOD = 1e9 + 7; + vector dp(k + 1); + dp[1] = 1; + + for (int N = 2; N <= n; N++) { + int prev = 0; + for (int K = 1; K <= k; K++) { + int tmp = dp[K]; + dp[K] = (prev + (N - 1) * 1LL * dp[K]) % MOD; + prev = tmp; + } + } + + return dp[k]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number} k + * @return {number} + */ + rearrangeSticks(n, k) { + const MOD = 1e9 + 7; + const dp = new Array(k + 1).fill(0); + dp[1] = 1; + + for (let N = 2; N <= n; N++) { + let prev = 0; + for (let K = 1; K <= k; K++) { + const tmp = dp[K]; + dp[K] = (prev + (N - 1) * dp[K]) % MOD; + prev = tmp; + } + } + + return dp[k]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * k)$ +* Space complexity: $O(k)$ + +> Where $n$ represents the total number of sticks, and $k$ denotes the number of sticks that must be visible from the left side. \ No newline at end of file diff --git a/articles/number-of-ways-to-stay-in-the-same-place-after-some-steps.md b/articles/number-of-ways-to-stay-in-the-same-place-after-some-steps.md new file mode 100644 index 000000000..2c9577d2a --- /dev/null +++ b/articles/number-of-ways-to-stay-in-the-same-place-after-some-steps.md @@ -0,0 +1,503 @@ +## 1. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def numWays(self, steps: int, arrLen: int) -> int: + mod = 10**9 + 7 + arrLen = min(arrLen, steps) + dp = {} + + def dfs(i, steps): + if steps == 0: + return i == 0 + if (i, steps) in dp: + return dp[(i, steps)] + + res = dfs(i, steps - 1) + if i > 0: + res = (res + dfs(i - 1, steps - 1)) % mod + if i < arrLen - 1: + res = (res + dfs(i + 1, steps - 1)) % mod + + dp[(i, steps)] = res + return res + + return dfs(0, steps) +``` + +```java +public class Solution { + private static final int MOD = 1_000_000_007; + private int[][] dp; + + public int numWays(int steps, int arrLen) { + int maxPos = Math.min(steps, arrLen); + dp = new int[maxPos + 1][steps + 1]; + for (int i = 0; i <= maxPos; i++) { + for (int j = 0; j <= steps; j++) { + dp[i][j] = -1; + } + } + return dfs(0, steps, maxPos); + } + + private int dfs(int i, int steps, int maxPos) { + if (steps == 0) { + return i == 0 ? 1 : 0; + } + if (dp[i][steps] != -1) { + return dp[i][steps]; + } + + int res = dfs(i, steps - 1, maxPos); + if (i > 0) { + res = (res + dfs(i - 1, steps - 1, maxPos)) % MOD; + } + if (i < maxPos - 1) { + res = (res + dfs(i + 1, steps - 1, maxPos)) % MOD; + } + + dp[i][steps] = res; + return res; + } +} +``` + +```cpp +class Solution { +private: + static const int MOD = 1e9 + 7; + vector> dp; + + int dfs(int i, int steps, int maxPos) { + if (steps == 0) { + return i == 0 ? 1 : 0; + } + if (dp[i][steps] != -1) { + return dp[i][steps]; + } + + int res = dfs(i, steps - 1, maxPos); + if (i > 0) { + res = (res + dfs(i - 1, steps - 1, maxPos)) % MOD; + } + if (i < maxPos - 1) { + res = (res + dfs(i + 1, steps - 1, maxPos)) % MOD; + } + + dp[i][steps] = res; + return res; + } + +public: + int numWays(int steps, int arrLen) { + int maxPos = min(steps, arrLen); + dp.assign(maxPos + 1, vector(steps + 1, -1)); + return dfs(0, steps, maxPos); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} steps + * @param {number} arrLen + * @return {number} + */ + numWays(steps, arrLen) { + const MOD = 1e9 + 7; + const maxPos = Math.min(steps, arrLen); + const dp = Array.from({ length: maxPos + 1 }, () => Array(steps + 1).fill(-1)); + + const dfs = (i, steps) => { + if (steps === 0) return i === 0 ? 1 : 0; + if (dp[i][steps] !== -1) return dp[i][steps]; + + let res = dfs(i, steps - 1); + if (i > 0) res = (res + dfs(i - 1, steps - 1)) % MOD; + if (i < maxPos - 1) res = (res + dfs(i + 1, steps - 1)) % MOD; + + dp[i][steps] = res; + return res; + }; + + return dfs(0, steps); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * min(n, m))$ +* Space complexity: $O(n * min(n, m))$ + +> Where $n$ is the number of steps and $m$ is the size of the array. + +--- + +## 2. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def numWays(self, steps: int, arrLen: int) -> int: + mod = 10**9 + 7 + arrLen = min(arrLen, steps) + dp = [[0] * (arrLen + 1) for _ in range(steps + 1)] + dp[0][0] = 1 + + for step in range(1, steps + 1): + for i in range(arrLen): + res = dp[step - 1][i] + if i > 0: + res = (res + dp[step - 1][i - 1]) % mod + if i < arrLen - 1: + res = (res + dp[step - 1][i + 1]) % mod + dp[step][i] = res + + return dp[steps][0] +``` + +```java +public class Solution { + public int numWays(int steps, int arrLen) { + final int MOD = 1_000_000_007; + arrLen = Math.min(arrLen, steps); + int[][] dp = new int[steps + 1][arrLen + 1]; + dp[0][0] = 1; + + for (int step = 1; step <= steps; step++) { + for (int i = 0; i < arrLen; i++) { + int res = dp[step - 1][i]; + if (i > 0) { + res = (res + dp[step - 1][i - 1]) % MOD; + } + if (i < arrLen - 1) { + res = (res + dp[step - 1][i + 1]) % MOD; + } + dp[step][i] = res; + } + } + + return dp[steps][0]; + } +} +``` + +```cpp +class Solution { +public: + int numWays(int steps, int arrLen) { + const int MOD = 1e9 + 7; + arrLen = min(arrLen, steps); + vector> dp(steps + 1, vector(arrLen + 1, 0)); + dp[0][0] = 1; + + for (int step = 1; step <= steps; step++) { + for (int i = 0; i < arrLen; i++) { + int res = dp[step - 1][i]; + if (i > 0) { + res = (res + dp[step - 1][i - 1]) % MOD; + } + if (i < arrLen - 1) { + res = (res + dp[step - 1][i + 1]) % MOD; + } + dp[step][i] = res; + } + } + + return dp[steps][0]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} steps + * @param {number} arrLen + * @return {number} + */ + numWays(steps, arrLen) { + const MOD = 1e9 + 7; + arrLen = Math.min(arrLen, steps); + const dp = Array.from({ length: steps + 1 }, () => Array(arrLen + 1).fill(0)); + dp[0][0] = 1; + + for (let step = 1; step <= steps; step++) { + for (let i = 0; i < arrLen; i++) { + let res = dp[step - 1][i]; + if (i > 0) { + res = (res + dp[step - 1][i - 1]) % MOD; + } + if (i < arrLen - 1) { + res = (res + dp[step - 1][i + 1]) % MOD; + } + dp[step][i] = res; + } + } + + return dp[steps][0]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * min(n, m))$ +* Space complexity: $O(n * min(n, m))$ + +> Where $n$ is the number of steps and $m$ is the size of the array. + +--- + +## 3. Dynamic Programming (Space Optimized) + +::tabs-start + +```python +class Solution: + def numWays(self, steps: int, arrLen: int) -> int: + mod = 10**9 + 7 + arrLen = min(steps, arrLen) + dp = [0] * arrLen + dp[0] = 1 + + for _ in range(steps): + next_dp = [0] * arrLen + for i in range(arrLen): + next_dp[i] = dp[i] + if i > 0: + next_dp[i] = (next_dp[i] + dp[i - 1]) % mod + if i < arrLen - 1: + next_dp[i] = (next_dp[i] + dp[i + 1]) % mod + dp = next_dp + + return dp[0] +``` + +```java +public class Solution { + public int numWays(int steps, int arrLen) { + final int MOD = 1_000_000_007; + arrLen = Math.min(steps, arrLen); + int[] dp = new int[arrLen]; + dp[0] = 1; + + for (int step = 0; step < steps; step++) { + int[] nextDp = new int[arrLen]; + for (int i = 0; i < arrLen; i++) { + nextDp[i] = dp[i]; + if (i > 0) { + nextDp[i] = (nextDp[i] + dp[i - 1]) % MOD; + } + if (i < arrLen - 1) { + nextDp[i] = (nextDp[i] + dp[i + 1]) % MOD; + } + } + dp = nextDp; + } + + return dp[0]; + } +} +``` + +```cpp +class Solution { +public: + int numWays(int steps, int arrLen) { + const int MOD = 1e9 + 7; + arrLen = min(steps, arrLen); + vector dp(arrLen, 0); + dp[0] = 1; + + for (int step = 0; step < steps; step++) { + vector nextDp(arrLen, 0); + for (int i = 0; i < arrLen; i++) { + nextDp[i] = dp[i]; + if (i > 0) { + nextDp[i] = (nextDp[i] + dp[i - 1]) % MOD; + } + if (i < arrLen - 1) { + nextDp[i] = (nextDp[i] + dp[i + 1]) % MOD; + } + } + dp = nextDp; + } + + return dp[0]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} steps + * @param {number} arrLen + * @return {number} + */ + numWays(steps, arrLen) { + const MOD = 1e9 + 7; + arrLen = Math.min(steps, arrLen); + let dp = new Array(arrLen).fill(0); + dp[0] = 1; + + for (let step = 0; step < steps; step++) { + const nextDp = new Array(arrLen).fill(0); + for (let i = 0; i < arrLen; i++) { + nextDp[i] = dp[i]; + if (i > 0) { + nextDp[i] = (nextDp[i] + dp[i - 1]) % MOD; + } + if (i < arrLen - 1) { + nextDp[i] = (nextDp[i] + dp[i + 1]) % MOD; + } + } + dp = nextDp; + } + + return dp[0]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * min(n, m))$ +* Space complexity: $O(min(n, m))$ + +> Where $n$ is the number of steps and $m$ is the size of the array. + +--- + +## 4. Dynamic Programming (Optimal) + +::tabs-start + +```python +class Solution: + def numWays(self, steps: int, arrLen: int) -> int: + mod = 10**9 + 7 + arrLen = min(steps, arrLen) + dp = [0] * arrLen + dp[0] = 1 + + for _ in range(steps): + prev = 0 + for i in range(arrLen): + cur = dp[i] + if i > 0: + dp[i] = (dp[i] + prev) % mod + if i < arrLen - 1: + dp[i] = (dp[i] + dp[i + 1]) % mod + prev = cur + + return dp[0] +``` + +```java +public class Solution { + public int numWays(int steps, int arrLen) { + final int MOD = 1_000_000_007; + arrLen = Math.min(steps, arrLen); + int[] dp = new int[arrLen]; + dp[0] = 1; + + for (int step = 0; step < steps; step++) { + int prev = 0; + for (int i = 0; i < arrLen; i++) { + int cur = dp[i]; + if (i > 0) { + dp[i] = (dp[i] + prev) % MOD; + } + if (i < arrLen - 1) { + dp[i] = (dp[i] + dp[i + 1]) % MOD; + } + prev = cur; + } + } + + return dp[0]; + } +} +``` + +```cpp +class Solution { +public: + int numWays(int steps, int arrLen) { + const int MOD = 1e9 + 7; + arrLen = min(steps, arrLen); + vector dp(arrLen, 0); + dp[0] = 1; + + for (int step = 0; step < steps; step++) { + int prev = 0; + for (int i = 0; i < arrLen; i++) { + int cur = dp[i]; + if (i > 0) { + dp[i] = (dp[i] + prev) % MOD; + } + if (i < arrLen - 1) { + dp[i] = (dp[i] + dp[i + 1]) % MOD; + } + prev = cur; + } + } + + return dp[0]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} steps + * @param {number} arrLen + * @return {number} + */ + numWays(steps, arrLen) { + const MOD = 1e9 + 7; + arrLen = Math.min(steps, arrLen); + const dp = new Array(arrLen).fill(0); + dp[0] = 1; + + for (let step = 0; step < steps; step++) { + for (let i = 0; i < arrLen; i++) { + const cur = dp[i]; + if (i > 0) { + dp[i] = (dp[i] + prev) % MOD; + } + if (i < arrLen - 1) { + dp[i] = (dp[i] + dp[i + 1]) % MOD; + } + prev = cur; + } + } + + return dp[0]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * min(n, m))$ +* Space complexity: $O(min(n, m))$ + +> Where $n$ is the number of steps and $m$ is the size of the array. \ No newline at end of file diff --git a/articles/out-of-boundary-paths.md b/articles/out-of-boundary-paths.md new file mode 100644 index 000000000..ced7639b1 --- /dev/null +++ b/articles/out-of-boundary-paths.md @@ -0,0 +1,525 @@ +## 1. Recursion + +::tabs-start + +```python +class Solution: + def findPaths(self, m: int, n: int, maxMove: int, startRow: int, startColumn: int) -> int: + ROWS, COLS = m, n + MOD = 10**9 + 7 + + def dfs(r, c, moves): + if r < 0 or r >= ROWS or c < 0 or c >= COLS: + return 1 + if moves == 0: + return 0 + + return ( + dfs(r + 1, c, moves - 1) + + dfs(r - 1, c, moves - 1) + + dfs(r, c + 1, moves - 1) + + dfs(r, c - 1, moves - 1) + ) % MOD + + return dfs(startRow, startColumn, maxMove) +``` + +```java +public class Solution { + private static final int MOD = 1_000_000_007; + + private int dfs(int r, int c, int moves, int m, int n) { + if (r < 0 || r >= m || c < 0 || c >= n) return 1; + if (moves == 0) return 0; + + return ( + (dfs(r + 1, c, moves - 1, m, n) + dfs(r - 1, c, moves - 1, m, n)) % MOD + + (dfs(r, c + 1, moves - 1, m, n) + dfs(r, c - 1, moves - 1, m, n)) % MOD + ) % MOD; + } + + public int findPaths(int m, int n, int maxMove, int startRow, int startColumn) { + return dfs(startRow, startColumn, maxMove, m, n); + } +} +``` + +```cpp +class Solution { +private: + int MOD = 1'000'000'007; + + int dfs(int r, int c, int moves, int m, int n) { + if (r < 0 || r >= m || c < 0 || c >= n) return 1; + if (moves == 0) return 0; + + return ( + (dfs(r + 1, c, moves - 1, m, n) + dfs(r - 1, c, moves - 1, m, n)) % MOD + + (dfs(r, c + 1, moves - 1, m, n) + dfs(r, c - 1, moves - 1, m, n)) % MOD + ) % MOD; + } + +public: + int findPaths(int m, int n, int maxMove, int startRow, int startColumn) { + return dfs(startRow, startColumn, maxMove, m, n); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} m + * @param {number} n + * @param {number} maxMove + * @param {number} startRow + * @param {number} startColumn + * @return {number} + */ + findPaths(m, n, maxMove, startRow, startColumn) { + const MOD = 1_000_000_007; + + const dfs = (r, c, moves) => { + if (r < 0 || r >= m || c < 0 || c >= n) return 1; + if (moves === 0) return 0; + + return ( + (dfs(r + 1, c, moves - 1) + dfs(r - 1, c, moves - 1)) % MOD + + (dfs(r, c + 1, moves - 1) + dfs(r, c - 1, moves - 1)) % MOD + ) % MOD; + }; + + return dfs(startRow, startColumn, maxMove); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(4 ^ N)$ +* Space complexity: $O(N)$ + +> Where $m$ is the number of rows, $n$ is the number of columns, and $N$ is the maximum number of allowed moves. + +--- + +## 2. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def findPaths(self, m: int, n: int, maxMove: int, startRow: int, startColumn: int) -> int: + ROWS, COLS = m, n + MOD = 10**9 + 7 + cache = {} + + def dfs(r, c, moves): + if r < 0 or r >= ROWS or c < 0 or c >= COLS: + return 1 + if moves == 0: + return 0 + if (r, c, moves) in cache: + return cache[(r, c, moves)] + + cache[(r, c, moves)] = ( + dfs(r + 1, c, moves - 1) + + dfs(r - 1, c, moves - 1) + + dfs(r, c + 1, moves - 1) + + dfs(r, c - 1, moves - 1) + ) % MOD + return cache[(r, c, moves)] + + return dfs(startRow, startColumn, maxMove) +``` + +```java +public class Solution { + private int[][][] dp; + private static final int MOD = 1_000_000_007; + + private int dfs(int r, int c, int moves, int m, int n) { + if (r < 0 || r >= m || c < 0 || c >= n) return 1; + if (moves == 0) return 0; + if (dp[r][c][moves] != -1) return dp[r][c][moves]; + + dp[r][c][moves] = ( + (dfs(r + 1, c, moves - 1, m, n) + dfs(r - 1, c, moves - 1, m, n)) % MOD + + (dfs(r, c + 1, moves - 1, m, n) + dfs(r, c - 1, moves - 1, m, n)) % MOD + ) % MOD; + return dp[r][c][moves]; + } + + public int findPaths(int m, int n, int maxMove, int startRow, int startColumn) { + dp = new int[m][n][maxMove + 1]; + for (int[][] layer : dp) { + for (int[] row : layer) { + Arrays.fill(row, -1); + } + } + return dfs(startRow, startColumn, maxMove, m, n); + } +} +``` + +```cpp +class Solution { +private: + vector>> dp; + int MOD = 1'000'000'007; + + int dfs(int r, int c, int moves, int m, int n) { + if (r < 0 || r >= m || c < 0 || c >= n) return 1; + if (moves == 0) return 0; + if (dp[r][c][moves] != -1) return dp[r][c][moves]; + + dp[r][c][moves] = ( + (dfs(r + 1, c, moves - 1, m, n) + dfs(r - 1, c, moves - 1, m, n)) % MOD + + (dfs(r, c + 1, moves - 1, m, n) + dfs(r, c - 1, moves - 1, m, n)) % MOD + ) % MOD; + return dp[r][c][moves]; + } + +public: + int findPaths(int m, int n, int maxMove, int startRow, int startColumn) { + dp = vector>>(m, vector>(n, vector(maxMove + 1, -1))); + return dfs(startRow, startColumn, maxMove, m, n); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} m + * @param {number} n + * @param {number} maxMove + * @param {number} startRow + * @param {number} startColumn + * @return {number} + */ + findPaths(m, n, maxMove, startRow, startColumn) { + const MOD = 1_000_000_007; + const dp = Array.from({ length: m }, () => + Array.from({ length: n }, () => Array(maxMove + 1).fill(-1)) + ); + + const dfs = (r, c, moves) => { + if (r < 0 || r >= m || c < 0 || c >= n) return 1; + if (moves === 0) return 0; + if (dp[r][c][moves] !== -1) return dp[r][c][moves]; + + dp[r][c][moves] = ( + (dfs(r + 1, c, moves - 1) + dfs(r - 1, c, moves - 1)) % MOD + + (dfs(r, c + 1, moves - 1) + dfs(r, c - 1, moves - 1)) % MOD + ) % MOD; + return dp[r][c][moves]; + }; + + return dfs(startRow, startColumn, maxMove); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n * N)$ +* Space complexity: $O(m * n * N)$ + +> Where $m$ is the number of rows, $n$ is the number of columns, and $N$ is the maximum number of allowed moves. + +--- + +## 3. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def findPaths(self, m: int, n: int, maxMove: int, startRow: int, startColumn: int) -> int: + MOD = 10**9 + 7 + dp = [[[0] * (maxMove + 1) for _ in range(n)] for _ in range(m)] + + for moves in range(1, maxMove + 1): + for r in range(m): + for c in range(n): + dp[r][c][moves] = ( + (dp[r - 1][c][moves - 1] if r > 0 else 1) + + (dp[r + 1][c][moves - 1] if r < m - 1 else 1) + + (dp[r][c - 1][moves - 1] if c > 0 else 1) + + (dp[r][c + 1][moves - 1] if c < n - 1 else 1) + ) % MOD + + return dp[startRow][startColumn][maxMove] +``` + +```java +public class Solution { + public int findPaths(int m, int n, int maxMove, int startRow, int startColumn) { + final int MOD = 1_000_000_007; + int[][][] dp = new int[m][n][maxMove + 1]; + + for (int moves = 1; moves <= maxMove; moves++) { + for (int r = 0; r < m; r++) { + for (int c = 0; c < n; c++) { + long up = (r > 0) ? dp[r - 1][c][moves - 1] : 1; + long down = (r < m - 1) ? dp[r + 1][c][moves - 1] : 1; + long left = (c > 0) ? dp[r][c - 1][moves - 1] : 1; + long right = (c < n - 1) ? dp[r][c + 1][moves - 1] : 1; + + dp[r][c][moves] = (int) ((up + down + left + right) % MOD); + } + } + } + + return dp[startRow][startColumn][maxMove]; + } +} +``` + +```cpp +class Solution { +public: + int findPaths(int m, int n, int maxMove, int startRow, int startColumn) { + const int MOD = 1'000'000'007; + vector>> dp(m, vector>(n, vector(maxMove + 1, 0))); + + for (int moves = 1; moves <= maxMove; moves++) { + for (int r = 0; r < m; r++) { + for (int c = 0; c < n; c++) { + dp[r][c][moves] = ( + (r > 0 ? dp[r - 1][c][moves - 1] : 1) + + (r < m - 1 ? dp[r + 1][c][moves - 1] : 1) + + (c > 0 ? dp[r][c - 1][moves - 1] : 1) + + (c < n - 1 ? dp[r][c + 1][moves - 1] : 1) + ) % MOD; + } + } + } + + return dp[startRow][startColumn][maxMove]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} m + * @param {number} n + * @param {number} maxMove + * @param {number} startRow + * @param {number} startColumn + * @return {number} + */ + findPaths(m, n, maxMove, startRow, startColumn) { + const MOD = 1_000_000_007; + const dp = Array.from({ length: m }, () => + Array.from({ length: n }, () => Array(maxMove + 1).fill(0)) + ); + + for (let moves = 1; moves <= maxMove; moves++) { + for (let r = 0; r < m; r++) { + for (let c = 0; c < n; c++) { + dp[r][c][moves] = ( + (r > 0 ? dp[r - 1][c][moves - 1] : 1) + + (r < m - 1 ? dp[r + 1][c][moves - 1] : 1) + + (c > 0 ? dp[r][c - 1][moves - 1] : 1) + + (c < n - 1 ? dp[r][c + 1][moves - 1] : 1) + ) % MOD; + } + } + } + + return dp[startRow][startColumn][maxMove]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n * N)$ +* Space complexity: $O(m * n * N)$ + +> Where $m$ is the number of rows, $n$ is the number of columns, and $N$ is the maximum number of allowed moves. + +--- + +## 4. Dynamic Programming (Space Optimized) + +::tabs-start + +```python +class Solution: + def findPaths(self, m: int, n: int, maxMove: int, startRow: int, startColumn: int) -> int: + MOD = 10**9 + 7 + dp = [[0] * n for _ in range(m)] + + for moves in range(1, maxMove + 1): + tmp = [[0] * n for _ in range(m)] + for r in range(m): + for c in range(n): + if r + 1 == m: + tmp[r][c] = (tmp[r][c] + 1) % MOD + else: + tmp[r][c] = (tmp[r][c] + dp[r + 1][c]) % MOD + if r - 1 < 0: + tmp[r][c] = (tmp[r][c] + 1) % MOD + else: + tmp[r][c] = (tmp[r][c] + dp[r - 1][c]) % MOD + if c + 1 == n: + tmp[r][c] = (tmp[r][c] + 1) % MOD + else: + tmp[r][c] = (tmp[r][c] + dp[r][c + 1]) % MOD + if c - 1 < 0: + tmp[r][c] = (tmp[r][c] + 1) % MOD + else: + tmp[r][c] = (tmp[r][c] + dp[r][c - 1]) % MOD + dp = tmp + + return dp[startRow][startColumn] +``` + +```java +public class Solution { + public int findPaths(int m, int n, int maxMove, int startRow, int startColumn) { + final int MOD = 1_000_000_007; + int[][] dp = new int[m][n]; + + for (int moves = 1; moves <= maxMove; moves++) { + int[][] tmp = new int[m][n]; + for (int r = 0; r < m; r++) { + for (int c = 0; c < n; c++) { + if (r + 1 == m) { + tmp[r][c] = (tmp[r][c] + 1) % MOD; + } else { + tmp[r][c] = (tmp[r][c] + dp[r + 1][c]) % MOD; + } + if (r - 1 < 0) { + tmp[r][c] = (tmp[r][c] + 1) % MOD; + } else { + tmp[r][c] = (tmp[r][c] + dp[r - 1][c]) % MOD; + } + if (c + 1 == n) { + tmp[r][c] = (tmp[r][c] + 1) % MOD; + } else { + tmp[r][c] = (tmp[r][c] + dp[r][c + 1]) % MOD; + } + if (c - 1 < 0) { + tmp[r][c] = (tmp[r][c] + 1) % MOD; + } else { + tmp[r][c] = (tmp[r][c] + dp[r][c - 1]) % MOD; + } + } + } + dp = tmp; + } + + return dp[startRow][startColumn]; + } +} +``` + +```cpp +class Solution { +public: + int findPaths(int m, int n, int maxMove, int startRow, int startColumn) { + const int MOD = 1'000'000'007; + vector> dp(m, vector(n, 0)); + + for (int moves = 1; moves <= maxMove; moves++) { + vector> tmp(m, vector(n, 0)); + for (int r = 0; r < m; r++) { + for (int c = 0; c < n; c++) { + if (r + 1 == m) { + tmp[r][c] = (tmp[r][c] + 1) % MOD; + } else { + tmp[r][c] = (tmp[r][c] + dp[r + 1][c]) % MOD; + } + if (r - 1 < 0) { + tmp[r][c] = (tmp[r][c] + 1) % MOD; + } else { + tmp[r][c] = (tmp[r][c] + dp[r - 1][c]) % MOD; + } + if (c + 1 == n) { + tmp[r][c] = (tmp[r][c] + 1) % MOD; + } else { + tmp[r][c] = (tmp[r][c] + dp[r][c + 1]) % MOD; + } + if (c - 1 < 0) { + tmp[r][c] = (tmp[r][c] + 1) % MOD; + } else { + tmp[r][c] = (tmp[r][c] + dp[r][c - 1]) % MOD; + } + } + } + dp = tmp; + } + + return dp[startRow][startColumn]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} m + * @param {number} n + * @param {number} maxMove + * @param {number} startRow + * @param {number} startColumn + * @return {number} + */ + findPaths(m, n, maxMove, startRow, startColumn) { + const MOD = 1_000_000_007; + let dp = Array.from({ length: m }, () => Array(n).fill(0)); + + for (let moves = 1; moves <= maxMove; moves++) { + const tmp = Array.from({ length: m }, () => Array(n).fill(0)); + for (let r = 0; r < m; r++) { + for (let c = 0; c < n; c++) { + if (r + 1 === m) { + tmp[r][c] = (tmp[r][c] + 1) % MOD; + } else { + tmp[r][c] = (tmp[r][c] + dp[r + 1][c]) % MOD; + } + if (r - 1 < 0) { + tmp[r][c] = (tmp[r][c] + 1) % MOD; + } else { + tmp[r][c] = (tmp[r][c] + dp[r - 1][c]) % MOD; + } + if (c + 1 === n) { + tmp[r][c] = (tmp[r][c] + 1) % MOD; + } else { + tmp[r][c] = (tmp[r][c] + dp[r][c + 1]) % MOD; + } + if (c - 1 < 0) { + tmp[r][c] = (tmp[r][c] + 1) % MOD; + } else { + tmp[r][c] = (tmp[r][c] + dp[r][c - 1]) % MOD; + } + } + } + dp = tmp; + } + + return dp[startRow][startColumn]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n * N)$ +* Space complexity: $O(m * n)$ + +> Where $m$ is the number of rows, $n$ is the number of columns, and $N$ is the maximum number of allowed moves. \ No newline at end of file diff --git a/articles/painting-the-walls.md b/articles/painting-the-walls.md new file mode 100644 index 000000000..1942d0042 --- /dev/null +++ b/articles/painting-the-walls.md @@ -0,0 +1,430 @@ +## 1. Recursion + +::tabs-start + +```python +class Solution: + def paintWalls(self, cost: List[int], time: List[int]) -> int: + + def dfs(i, remain): + if remain <= 0: + return 0 + if i == len(cost): + return float("inf") + + paint = cost[i] + dfs(i + 1, remain - 1 - time[i]) + skip = dfs(i + 1, remain) + return min(paint, skip) + + return dfs(0, len(cost)) +``` + +```java +public class Solution { + public int paintWalls(int[] cost, int[] time) { + return dfs(cost, time, 0, cost.length); + } + + private int dfs(int[] cost, int[] time, int i, int remain) { + if (remain <= 0) { + return 0; + } + if (i == cost.length) { + return Integer.MAX_VALUE; + } + + int paint = dfs(cost, time, i + 1, remain - 1 - time[i]); + if (paint != Integer.MAX_VALUE) paint += cost[i]; + int skip = dfs(cost, time, i + 1, remain); + return Math.min(paint, skip); + } +} +``` + +```cpp +class Solution { +public: + int paintWalls(vector& cost, vector& time) { + return dfs(cost, time, 0, cost.size()); + } + +private: + int dfs(vector& cost, vector& time, int i, int remain) { + if (remain <= 0) { + return 0; + } + if (i == cost.size()) { + return INT_MAX; + } + + int paint = dfs(cost, time, i + 1, remain - 1 - time[i]); + if (paint != INT_MAX) paint += cost[i]; + + int skip = dfs(cost, time, i + 1, remain); + return min(paint, skip); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} cost + * @param {number[]} time + * @return {number} + */ + paintWalls(cost, time) { + const dfs = (i, remain) => { + if (remain <= 0) { + return 0; + } + if (i === cost.length) { + return Infinity; + } + + const paint = cost[i] + dfs(i + 1, remain - 1 - time[i]); + const skip = dfs(i + 1, remain); + return Math.min(paint, skip); + }; + + return dfs(0, cost.length); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(2 ^ n)$ +* Space complexity: $O(n)$ for recursion stack. + +--- + +## 2. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def paintWalls(self, cost: List[int], time: List[int]) -> int: + dp = {} + + def dfs(i, remain): + if remain <= 0: + return 0 + if i == len(cost): + return float("inf") + if (i, remain) in dp: + return dp[(i, remain)] + + paint = cost[i] + dfs(i + 1, remain - 1 - time[i]) + skip = dfs(i + 1, remain) + dp[(i, remain)] = min(paint, skip) + return dp[(i, remain)] + + return dfs(0, len(cost)) +``` + +```java +public class Solution { + private int[][] dp; + + public int paintWalls(int[] cost, int[] time) { + dp = new int[cost.length][cost.length + 1]; + for (int[] row : dp) { + Arrays.fill(row, -1); + } + return dfs(cost, time, 0, cost.length); + } + + private int dfs(int[] cost, int[] time, int i, int remain) { + if (remain <= 0) { + return 0; + } + if (i == cost.length) { + return Integer.MAX_VALUE; + } + if (dp[i][remain] != -1) { + return dp[i][remain]; + } + + int paint = dfs(cost, time, i + 1, remain - 1 - time[i]); + if (paint != Integer.MAX_VALUE) paint += cost[i]; + + int skip = dfs(cost, time, i + 1, remain); + return dp[i][remain] = Math.min(paint, skip); + } +} +``` + +```cpp +class Solution { + vector> dp; + +public: + int paintWalls(vector& cost, vector& time) { + dp.assign(cost.size(), vector(cost.size() + 1, -1)); + return dfs(cost, time, 0, cost.size()); + } + +private: + int dfs(vector& cost, vector& time, int i, int remain) { + if (remain <= 0) { + return 0; + } + if (i == cost.size()) { + return INT_MAX; + } + if (dp[i][remain] != -1) { + return dp[i][remain]; + } + + int paint = dfs(cost, time, i + 1, remain - 1 - time[i]); + if (paint != INT_MAX) paint += cost[i]; + + int skip = dfs(cost, time, i + 1, remain); + return dp[i][remain] = min(paint, skip); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} cost + * @param {number[]} time + * @return {number} + */ + paintWalls(cost, time) { + const n = cost.length; + const dp = Array.from({ length: n }, () => Array(n + 1).fill(-1)); + const dfs = (i, remain) => { + if (remain <= 0) { + return 0; + } + if (i === n) { + return Infinity; + } + if (dp[i][remain] !== -1) { + return dp[i][remain]; + } + + const paint = cost[i] + dfs(i + 1, remain - 1 - time[i]); + const skip = dfs(i + 1, remain); + return dp[i][remain] = Math.min(paint, skip); + }; + + return dfs(0, cost.length); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n ^ 2)$ + +--- + +## 3. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def paintWalls(self, cost: List[int], time: List[int]) -> int: + n = len(cost) + dp = [[0] * (n + 2) for _ in range(n + 1)] + for remain in range(1, n + 1): + dp[n][remain] = float("inf") + + for i in range(n - 1, -1, -1): + for remain in range(1, n + 1): + paint = cost[i] + dp[i + 1][max(remain - 1 - time[i], 0)] + skip = dp[i + 1][remain] + dp[i][remain] = min(paint, skip) + + return dp[0][n] +``` + +```java +public class Solution { + public int paintWalls(int[] cost, int[] time) { + int n = cost.length; + int[][] dp = new int[n + 1][n + 2]; + for (int remain = 1; remain <= n; remain++) { + dp[n][remain] = Integer.MAX_VALUE; + } + + for (int i = n - 1; i >= 0; i--) { + for (int remain = 1; remain <= n; remain++) { + int paint = dp[i + 1][Math.max(remain - 1 - time[i], 0)]; + if (paint != Integer.MAX_VALUE) paint += cost[i]; + + int skip = dp[i + 1][remain]; + dp[i][remain] = Math.min(paint, skip); + } + } + + return dp[0][n]; + } +} +``` + +```cpp +class Solution { +public: + int paintWalls(vector& cost, vector& time) { + int n = cost.size(); + vector> dp(n + 1, vector(n + 2, 0)); + + for (int remain = 1; remain <= n; remain++) { + dp[n][remain] = INT_MAX; + } + + for (int i = n - 1; i >= 0; i--) { + for (int remain = 1; remain <= n; remain++) { + int paint = dp[i + 1][max(remain - 1 - time[i], 0)]; + if (paint != INT_MAX) paint += cost[i]; + + int skip = dp[i + 1][remain]; + dp[i][remain] = min(paint, skip); + } + } + + return dp[0][n]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} cost + * @param {number[]} time + * @return {number} + */ + paintWalls(cost, time) { + const n = cost.length; + const dp = Array.from({ length: n + 1 }, () => Array(n + 2).fill(0)); + + for (let remain = 1; remain <= n; remain++) { + dp[n][remain] = Infinity; + } + + for (let i = n - 1; i >= 0; i--) { + for (let remain = 1; remain <= n; remain++) { + const paint = cost[i] + dp[i + 1][Math.max(remain - 1 - time[i], 0)]; + const skip = dp[i + 1][remain]; + dp[i][remain] = Math.min(paint, skip); + } + } + + return dp[0][n]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n ^ 2)$ + +--- + +## 4. Dynamic Programming (Space Optimized) + +::tabs-start + +```python +class Solution: + def paintWalls(self, cost: List[int], time: List[int]) -> int: + n = len(cost) + dp = [float("inf")] * (n + 2) + dp[0] = 0 + + for i in range(n): + for remain in range(n, 0, -1): + paint = cost[i] + dp[max(remain - 1 - time[i], 0)] + dp[remain] = min(paint, dp[remain]) + + return dp[n] +``` + +```java +public class Solution { + public int paintWalls(int[] cost, int[] time) { + int n = cost.length; + int[] dp = new int[n + 2]; + Arrays.fill(dp, Integer.MAX_VALUE); + dp[0] = 0; + + for (int i = 0; i < n; i++) { + for (int remain = n; remain > 0; remain--) { + int paint = dp[Math.max(remain - 1 - time[i], 0)]; + if (paint != Integer.MAX_VALUE) paint += cost[i]; + dp[remain] = Math.min(paint, dp[remain]); + } + } + + return dp[n]; + } +} +``` + +```cpp +class Solution { +public: + int paintWalls(vector& cost, vector& time) { + int n = cost.size(); + vector dp(n + 2, INT_MAX); + dp[0] = 0; + + for (int i = 0; i < n; i++) { + for (int remain = n; remain > 0; remain--) { + int paint = dp[max(remain - 1 - time[i], 0)]; + if (paint != INT_MAX) paint += cost[i]; + dp[remain] = min(paint, dp[remain]); + } + } + + return dp[n]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} cost + * @param {number[]} time + * @return {number} + */ + paintWalls(cost, time) { + const n = cost.length; + const dp = Array(n + 2).fill(Infinity); + dp[0] = 0; + + for (let i = 0; i < n; i++) { + for (let remain = n; remain > 0; remain--) { + const paint = cost[i] + dp[Math.max(remain - 1 - time[i], 0)]; + dp[remain] = Math.min(paint, dp[remain]); + } + } + + return dp[n]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/profitable-schemes.md b/articles/profitable-schemes.md new file mode 100644 index 000000000..98986e70a --- /dev/null +++ b/articles/profitable-schemes.md @@ -0,0 +1,529 @@ +## 1. Recursion + +::tabs-start + +```python +class Solution: + def profitableSchemes(self, n: int, minProfit: int, group: List[int], profit: List[int]) -> int: + mod = 10**9 + 7 + + def dfs(i, n, p): + if i == len(group): + return 1 if p >= minProfit else 0 + + res = dfs(i + 1, n, p) + if n - group[i] >= 0: + res = (res + dfs(i + 1, n - group[i], p + profit[i])) % mod + + return res + + return dfs(0, n, 0) +``` + +```java +public class Solution { + private static final int MOD = 1_000_000_007; + + public int profitableSchemes(int n, int minProfit, int[] group, int[] profit) { + return dfs(0, n, 0, group, profit, minProfit); + } + + private int dfs(int i, int n, int p, int[] group, int[] profit, int minProfit) { + if (i == group.length) { + return p >= minProfit ? 1 : 0; + } + + int res = dfs(i + 1, n, p, group, profit, minProfit); + if (n - group[i] >= 0) { + res = (res + dfs(i + 1, n - group[i], p + profit[i], group, profit, minProfit)) % MOD; + } + + return res; + } +} +``` + +```cpp +class Solution { +private: + static const int MOD = 1e9 + 7; + +public: + int profitableSchemes(int n, int minProfit, vector& group, vector& profit) { + return dfs(0, n, 0, group, profit, minProfit); + } + +private: + int dfs(int i, int n, int p, const vector& group, const vector& profit, int minProfit) { + if (i == group.size()) { + return p >= minProfit ? 1 : 0; + } + + int res = dfs(i + 1, n, p, group, profit, minProfit); + if (n - group[i] >= 0) { + res = (res + dfs(i + 1, n - group[i], p + profit[i], group, profit, minProfit)) % MOD; + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number} minProfit + * @param {number[]} group + * @param {number[]} profit + * @return {number} + */ + profitableSchemes(n, minProfit, group, profit) { + const MOD = 1e9 + 7; + + const dfs = (i, n, p) => { + if (i === group.length) { + return p >= minProfit ? 1 : 0; + } + + let res = dfs(i + 1, n, p); + if (n - group[i] >= 0) { + res = (res + dfs(i + 1, n - group[i], p + profit[i])) % MOD; + } + + return res; + }; + + return dfs(0, n, 0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(2 ^ N)$ +* Space complexity: $O(N)$ + +> Where $N$ is the size of the $group$ array. + +--- + +## 2. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def profitableSchemes(self, n: int, minProfit: int, group: List[int], profit: List[int]) -> int: + mod = 10**9 + 7 + dp = {} + + def dfs(i, n, p): + if i == len(group): + return 1 if p >= minProfit else 0 + if (i, n, p) in dp: + return dp[(i, n, p)] + + res = dfs(i + 1, n, p) + if n - group[i] >= 0: + nxtP = min(p + profit[i], minProfit) + res = (res + dfs(i + 1, n - group[i], nxtP)) % mod + + dp[(i, n, p)] = res + return res + + return dfs(0, n, 0) +``` + +```java +public class Solution { + private static final int MOD = 1_000_000_007; + private int[][][] dp; + + public int profitableSchemes(int n, int minProfit, int[] group, int[] profit) { + dp = new int[group.length][n + 1][minProfit + 1]; + for (int[][] layer : dp) { + for (int[] row : layer) { + Arrays.fill(row, -1); + } + } + return dfs(0, n, 0, group, profit, minProfit); + } + + private int dfs(int i, int n, int p, int[] group, int[] profit, int minProfit) { + if (i == group.length) { + return p >= minProfit ? 1 : 0; + } + if (dp[i][n][p] != -1) { + return dp[i][n][p]; + } + + int res = dfs(i + 1, n, p, group, profit, minProfit); + if (n - group[i] >= 0) { + int nxtP = Math.min(p + profit[i], minProfit); + res = (res + dfs(i + 1, n - group[i], nxtP, group, profit, minProfit)) % MOD; + } + + dp[i][n][p] = res; + return res; + } +} +``` + +```cpp +class Solution { +private: + static const int MOD = 1e9 + 7; + vector>> dp; + +public: + int profitableSchemes(int n, int minProfit, vector& group, vector& profit) { + dp = vector>>(group.size(), vector>(n + 1, vector(minProfit + 1, -1))); + return dfs(0, n, 0, group, profit, minProfit); + } + +private: + int dfs(int i, int n, int p, vector& group, vector& profit, int minProfit) { + if (i == group.size()) { + return p >= minProfit ? 1 : 0; + } + if (dp[i][n][p] != -1) { + return dp[i][n][p]; + } + + int res = dfs(i + 1, n, p, group, profit, minProfit); + if (n >= group[i]) { + int nxtP = min(p + profit[i], minProfit); + res = (res + dfs(i + 1, n - group[i], nxtP, group, profit, minProfit)) % MOD; + } + + dp[i][n][p] = res; + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number} minProfit + * @param {number[]} group + * @param {number[]} profit + * @return {number} + */ + profitableSchemes(n, minProfit, group, profit) { + const MOD = 1e9 + 7; + const dp = Array.from({ length: group.length }, () => + Array.from({ length: n + 1 }, () => Array(minProfit + 1).fill(-1)) + ); + + const dfs = (i, n, p) => { + if (i === group.length) { + return p >= minProfit ? 1 : 0; + } + if (dp[i][n][p] !== -1) { + return dp[i][n][p]; + } + + let res = dfs(i + 1, n, p); + if (n >= group[i]) { + const nxtP = Math.min(p + profit[i], minProfit); + res = (res + dfs(i + 1, n - group[i], nxtP)) % MOD; + } + + dp[i][n][p] = res; + return res; + }; + + return dfs(0, n, 0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(N * m * n)$ +* Space complexity: $O(N * m * n)$ + +> Where $N$ is the size of the $group$ array, $m$ is the given minimum profit, and $n$ is the number of group members. + +--- + +## 3. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def profitableSchemes(self, n: int, minProfit: int, group: List[int], profit: List[int]) -> int: + mod = 10**9 + 7 + N = len(group) + + dp = [[[0] * (minProfit + 1) for j in range(n + 2)] for i in range(N + 1)] + for j in range(n + 1): + dp[N][j][minProfit] = 1 + + for i in range(N - 1, -1, -1): + for j in range(n + 1): + for p in range(minProfit + 1): + res = dp[i + 1][j][p] + if j >= group[i]: + nxtP = min(profit[i] + p, minProfit) + res = (res + dp[i + 1][j - group[i]][nxtP]) % mod + dp[i][j][p] = res + + return dp[0][n][0] +``` + +```java +public class Solution { + private static final int MOD = 1_000_000_007; + + public int profitableSchemes(int n, int minProfit, int[] group, int[] profit) { + int N = group.length; + int[][][] dp = new int[N + 1][n + 2][minProfit + 1]; + + for (int j = 0; j <= n; j++) { + dp[N][j][minProfit] = 1; + } + + for (int i = N - 1; i >= 0; i--) { + for (int j = 0; j <= n; j++) { + for (int p = 0; p <= minProfit; p++) { + int res = dp[i + 1][j][p]; + if (j >= group[i]) { + int nxtP = Math.min(profit[i] + p, minProfit); + res = (res + dp[i + 1][j - group[i]][nxtP]) % MOD; + } + dp[i][j][p] = res; + } + } + } + + return dp[0][n][0]; + } +} +``` + +```cpp +class Solution { +private: + static const int MOD = 1e9 + 7; + +public: + int profitableSchemes(int n, int minProfit, vector& group, vector& profit) { + int N = group.size(); + vector>> dp(N + 1, vector>(n + 2, vector(minProfit + 1, 0))); + + for (int j = 0; j <= n; j++) { + dp[N][j][minProfit] = 1; + } + + for (int i = N - 1; i >= 0; i--) { + for (int j = 0; j <= n; j++) { + for (int p = 0; p <= minProfit; p++) { + int res = dp[i + 1][j][p]; + if (j >= group[i]) { + int nxtP = min(profit[i] + p, minProfit); + res = (res + dp[i + 1][j - group[i]][nxtP]) % MOD; + } + dp[i][j][p] = res; + } + } + } + + return dp[0][n][0]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number} minProfit + * @param {number[]} group + * @param {number[]} profit + * @return {number} + */ + profitableSchemes(n, minProfit, group, profit) { + const MOD = 1e9 + 7; + const N = group.length; + + const dp = Array.from({ length: N + 1 }, () => + Array.from({ length: n + 2 }, () => Array(minProfit + 1).fill(0)) + ); + + for (let j = 0; j <= n; j++) { + dp[N][j][minProfit] = 1; + } + + for (let i = N - 1; i >= 0; i--) { + for (let j = 0; j <= n; j++) { + for (let p = 0; p <= minProfit; p++) { + let res = dp[i + 1][j][p]; + if (j >= group[i]) { + const nxtP = Math.min(profit[i] + p, minProfit); + res = (res + dp[i + 1][j - group[i]][nxtP]) % MOD; + } + dp[i][j][p] = res; + } + } + } + + return dp[0][n][0]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(N * m * n)$ +* Space complexity: $O(N * m * n)$ + +> Where $N$ is the size of the $group$ array, $m$ is the given minimum profit, and $n$ is the number of group members. + +--- + +## 4. Dynamic Programming (Space Optimized) + +::tabs-start + +```python +class Solution: + def profitableSchemes(self, n: int, minProfit: int, group: List[int], profit: List[int]) -> int: + mod = 10**9 + 7 + N = len(group) + + dp = [[0] * (minProfit + 1) for j in range(n + 2)] + for j in range(n + 1): + dp[j][minProfit] = 1 + + for i in range(N - 1, -1, -1): + for j in range(n, -1, -1): + for p in range(minProfit + 1): + res = dp[j][p] + if j >= group[i]: + nxtP = min(profit[i] + p, minProfit) + res = (res + dp[j - group[i]][nxtP]) % mod + dp[j][p] = res + + return dp[n][0] +``` + +```java +public class Solution { + private static final int MOD = 1_000_000_007; + + public int profitableSchemes(int n, int minProfit, int[] group, int[] profit) { + int N = group.length; + int[][] dp = new int[n + 2][minProfit + 1]; + + for (int j = 0; j <= n; j++) { + dp[j][minProfit] = 1; + } + + for (int i = N - 1; i >= 0; i--) { + for (int j = n; j >= 0; j--) { + for (int p = 0; p <= minProfit; p++) { + int res = dp[j][p]; + if (j >= group[i]) { + int nxtP = Math.min(profit[i] + p, minProfit); + res = (res + dp[j - group[i]][nxtP]) % MOD; + } + dp[j][p] = res; + } + } + } + + return dp[n][0]; + } +} +``` + +```cpp +class Solution { +private: + static const int MOD = 1e9 + 7; + +public: + int profitableSchemes(int n, int minProfit, vector& group, vector& profit) { + int N = group.size(); + vector> dp(n + 2, vector(minProfit + 1, 0)); + + for (int j = 0; j <= n; j++) { + dp[j][minProfit] = 1; + } + + for (int i = N - 1; i >= 0; i--) { + for (int j = n; j >= 0; j--) { + for (int p = 0; p <= minProfit; p++) { + int res = dp[j][p]; + if (j >= group[i]) { + int nxtP = min(profit[i] + p, minProfit); + res = (res + dp[j - group[i]][nxtP]) % MOD; + } + dp[j][p] = res; + } + } + } + + return dp[n][0]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number} minProfit + * @param {number[]} group + * @param {number[]} profit + * @return {number} + */ + profitableSchemes(n, minProfit, group, profit) { + const MOD = 1e9 + 7; + const N = group.length; + + const dp = Array.from({ length: n + 2 }, () => + Array(minProfit + 1).fill(0) + ); + + for (let j = 0; j <= n; j++) { + dp[j][minProfit] = 1; + } + + for (let i = N - 1; i >= 0; i--) { + for (let j = n; j >= 0; j--) { + for (let p = 0; p <= minProfit; p++) { + let res = dp[j][p]; + if (j >= group[i]) { + const nxtP = Math.min(profit[i] + p, minProfit); + res = (res + dp[j - group[i]][nxtP]) % MOD; + } + dp[j][p] = res; + } + } + } + + return dp[n][0]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(N * m * n)$ +* Space complexity: $O(m * n)$ + +> Where $N$ is the size of the $group$ array, $m$ is the given minimum profit, and $n$ is the number of group members. \ No newline at end of file diff --git a/articles/stone-game-ii.md b/articles/stone-game-ii.md new file mode 100644 index 000000000..b9468738a --- /dev/null +++ b/articles/stone-game-ii.md @@ -0,0 +1,540 @@ +## 1. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def stoneGameII(self, piles: List[int]) -> int: + dp = {} + + def dfs(alice, i, M): + if i == len(piles): + return 0 + if (alice, i, M) in dp: + return dp[(alice, i, M)] + + res = 0 if alice else float("inf") + total = 0 + for X in range(1, 2 * M + 1): + if i + X > len(piles): + break + total += piles[i + X - 1] + if alice: + res = max(res, total + dfs(not alice, i + X, max(M, X))) + else: + res = min(res, dfs(not alice, i + X, max(M, X))) + + dp[(alice, i, M)] = res + return res + + return dfs(True, 0, 1) +``` + +```java +public class Solution { + private int[][][] dp; + + public int stoneGameII(int[] piles) { + int n = piles.length; + dp = new int[2][n][n + 1]; + for (int[][] layer : dp) { + for (int[] row : layer) { + Arrays.fill(row, -1); + } + } + + return dfs(1, 0, 1, piles); + } + + private int dfs(int alice, int i, int M, int[] piles) { + if (i == piles.length) return 0; + if (dp[alice][i][M] != -1) return dp[alice][i][M]; + + int res = alice == 1 ? 0 : Integer.MAX_VALUE; + int total = 0; + + for (int X = 1; X <= 2 * M; X++) { + if (i + X > piles.length) break; + total += piles[i + X - 1]; + if (alice == 1) { + res = Math.max(res, total + dfs(0, i + X, Math.max(M, X), piles)); + } else { + res = Math.min(res, dfs(1, i + X, Math.max(M, X), piles)); + } + } + + dp[alice][i][M] = res; + return res; + } +} +``` + +```cpp +class Solution { +private: + vector>> dp; + +public: + int stoneGameII(vector& piles) { + int n = piles.size(); + dp.resize(2, vector>(n, vector(n + 1, -1))); + return dfs(1, 0, 1, piles); + } + +private: + int dfs(int alice, int i, int M, vector& piles) { + if (i == piles.size()) return 0; + if (dp[alice][i][M] != -1) return dp[alice][i][M]; + + int res = alice == 1 ? 0 : INT_MAX; + int total = 0; + + for (int X = 1; X <= 2 * M; X++) { + if (i + X > piles.size()) break; + total += piles[i + X - 1]; + if (alice == 1) { + res = max(res, total + dfs(0, i + X, max(M, X), piles)); + } else { + res = min(res, dfs(1, i + X, max(M, X), piles)); + } + } + + dp[alice][i][M] = res; + return dp[alice][i][M]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} piles + * @return {number} + */ + stoneGameII(piles) { + const n = piles.length; + this.dp = Array.from({ length: 2 }, () => + Array.from({ length: n }, () => Array(n + 1).fill(-1)) + ); + + const dfs = (alice, i, M) => { + if (i === n) return 0; + if (this.dp[alice][i][M] !== -1) return this.dp[alice][i][M]; + + let res = alice === 1 ? 0 : Infinity; + let total = 0; + + for (let X = 1; X <= 2 * M; X++) { + if (i + X > n) break; + total += piles[i + X - 1]; + if (alice === 1) { + res = Math.max(res, total + dfs(0, i + X, Math.max(M, X))); + } else { + res = Math.min(res, dfs(1, i + X, Math.max(M, X))); + } + } + + this.dp[alice][i][M] = res; + return res; + }; + + return dfs(1, 0, 1); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 3)$ +* Space complexity: $O(n ^ 2)$ + +--- + +## 2. Dynamic Programming (Top-Down) + Suffix Sum + +::tabs-start + +```python +class Solution: + def stoneGameII(self, piles: List[int]) -> int: + n = len(piles) + dp = [[None] * (n + 1) for _ in range(n)] + suffix_sum = [0] * n + suffix_sum[-1] = piles[-1] + for i in range(n - 2, -1, -1): + suffix_sum[i] = piles[i] + suffix_sum[i + 1] + + def dfs(i, M): + if i == n: + return 0 + if dp[i][M] is not None: + return dp[i][M] + + res = 0 + for X in range(1, 2 * M + 1): + if i + X > n: + break + res = max(res, suffix_sum[i] - dfs(i + X, max(M, X))) + dp[i][M] = res + return res + + return dfs(0, 1) +``` + +```java +public class Solution { + private int[][] dp; + private int[] suffixSum; + + public int stoneGameII(int[] piles) { + int n = piles.length; + dp = new int[n][n + 1]; + for (int i = 0; i < n; i++) { + Arrays.fill(dp[i], -1); + } + + suffixSum = new int[n]; + suffixSum[n - 1] = piles[n - 1]; + for (int i = n - 2; i >= 0; i--) { + suffixSum[i] = piles[i] + suffixSum[i + 1]; + } + + return dfs(0, 1); + } + + private int dfs(int i, int M) { + if (i == suffixSum.length) return 0; + if (dp[i][M] != -1) return dp[i][M]; + + int res = 0; + for (int X = 1; X <= 2 * M; X++) { + if (i + X > suffixSum.length) break; + res = Math.max(res, suffixSum[i] - dfs(i + X, Math.max(M, X))); + } + + return dp[i][M] = res; + } +} +``` + +```cpp +class Solution { +private: + vector> dp; + vector suffixSum; + +public: + int stoneGameII(vector& piles) { + int n = piles.size(); + dp.resize(n, vector(n + 1, -1)); + + suffixSum.resize(n); + suffixSum[n - 1] = piles[n - 1]; + for (int i = n - 2; i >= 0; i--) { + suffixSum[i] = piles[i] + suffixSum[i + 1]; + } + + return dfs(0, 1, piles); + } + +private: + int dfs(int i, int M, vector& piles) { + if (i == suffixSum.size()) return 0; + if (dp[i][M] != -1) return dp[i][M]; + + int res = 0; + for (int X = 1; X <= 2 * M; X++) { + if (i + X > suffixSum.size()) break; + res = max(res, suffixSum[i] - dfs(i + X, max(M, X), piles)); + } + + return dp[i][M] = res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} piles + * @return {number} + */ + stoneGameII(piles) { + const n = piles.length; + const dp = Array.from({ length: n }, () => Array(n + 1).fill(-1)); + + const suffixSum = Array(n).fill(0); + suffixSum[n - 1] = piles[n - 1]; + for (let i = n - 2; i >= 0; i--) { + suffixSum[i] = piles[i] + suffixSum[i + 1]; + } + + const dfs = (i, M) => { + if (i === n) return 0; + if (dp[i][M] !== -1) return dp[i][M]; + + let res = 0; + for (let X = 1; X <= 2 * M; X++) { + if (i + X > n) break; + res = Math.max(res, suffixSum[i] - dfs(i + X, Math.max(M, X))); + } + + return dp[i][M] = res; + }; + + return dfs(0, 1); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 3)$ +* Space complexity: $O(n ^ 2)$ + +--- + +## 3. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def stoneGameII(self, piles: List[int]) -> int: + n = len(piles) + dp = [[[0] * (n + 1) for _ in range(n + 1)] for _ in range(2)] + + for i in range(n - 1, -1, -1): + for M in range(1, n + 1): + total = 0 + dp[1][i][M] = 0 + dp[0][i][M] = float("inf") + + for X in range(1, 2 * M + 1): + if i + X > n: + break + total += piles[i + X - 1] + + dp[1][i][M] = max(dp[1][i][M], total + dp[0][i + X][max(M, X)]) + dp[0][i][M] = min(dp[0][i][M], dp[1][i + X][max(M, X)]) + + return dp[1][0][1] +``` + +```java +public class Solution { + public int stoneGameII(int[] piles) { + int n = piles.length; + int[][][] dp = new int[2][n + 1][n + 1]; + + for (int i = n - 1; i >= 0; i--) { + for (int M = 1; M <= n; M++) { + int total = 0; + dp[1][i][M] = 0; + dp[0][i][M] = Integer.MAX_VALUE; + + for (int X = 1; X <= 2 * M; X++) { + if (i + X > n) break; + total += piles[i + X - 1]; + dp[1][i][M] = Math.max(dp[1][i][M], total + dp[0][i + X][Math.max(M, X)]); + dp[0][i][M] = Math.min(dp[0][i][M], dp[1][i + X][Math.max(M, X)]); + } + } + } + + return dp[1][0][1]; + } +} +``` + +```cpp +class Solution { +public: + int stoneGameII(vector& piles) { + int n = piles.size(); + vector>> dp(2, vector>(n + 1, vector(n + 1, 0))); + + for (int i = n - 1; i >= 0; i--) { + for (int M = 1; M <= n; M++) { + int total = 0; + dp[1][i][M] = 0; + dp[0][i][M] = INT_MAX; + + for (int X = 1; X <= 2 * M; X++) { + if (i + X > n) break; + total += piles[i + X - 1]; + + dp[1][i][M] = max(dp[1][i][M], total + dp[0][i + X][max(M, X)]); + dp[0][i][M] = min(dp[0][i][M], dp[1][i + X][max(M, X)]); + } + } + } + + return dp[1][0][1]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} piles + * @return {number} + */ + stoneGameII(piles) { + const n = piles.length; + const dp = Array.from({ length: 2 }, () => + Array.from({ length: n + 1 }, () => Array(n + 1).fill(0)) + ); + + for (let i = n - 1; i >= 0; i--) { + for (let M = 1; M <= n; M++) { + let total = 0; + dp[1][i][M] = 0; + dp[0][i][M] = Infinity; + + for (let X = 1; X <= 2 * M; X++) { + if (i + X > n) break; + total += piles[i + X - 1]; + + dp[1][i][M] = Math.max(dp[1][i][M], total + dp[0][i + X][Math.max(M, X)]); + dp[0][i][M] = Math.min(dp[0][i][M], dp[1][i + X][Math.max(M, X)]); + } + } + } + + return dp[1][0][1]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 3)$ +* Space complexity: $O(n ^ 2)$ + +--- + +## 4. Dynamic Programming (Bottom-Up) + Suffix Sum + +::tabs-start + +```python +class Solution: + def stoneGameII(self, piles: List[int]) -> int: + n = len(piles) + suffix_sum = [0] * n + suffix_sum[-1] = piles[-1] + for i in range(n - 2, -1, -1): + suffix_sum[i] = piles[i] + suffix_sum[i + 1] + + dp = [[0] * (n + 1) for _ in range(n + 1)] + + for i in range(n - 1, -1, -1): + for M in range(1, n + 1): + for X in range(1, 2 * M + 1): + if i + X > n: + break + dp[i][M] = max(dp[i][M], suffix_sum[i] - dp[i + X][max(M, X)]) + + return dp[0][1] +``` + +```java +public class Solution { + public int stoneGameII(int[] piles) { + int n = piles.length; + + int[] suffixSum = new int[n]; + suffixSum[n - 1] = piles[n - 1]; + for (int i = n - 2; i >= 0; i--) { + suffixSum[i] = piles[i] + suffixSum[i + 1]; + } + + int[][] dp = new int[n + 1][n + 1]; + + for (int i = n - 1; i >= 0; i--) { + for (int M = 1; M <= n; M++) { + for (int X = 1; X <= 2 * M; X++) { + if (i + X > n) break; + dp[i][M] = Math.max(dp[i][M], suffixSum[i] - dp[i + X][Math.max(M, X)]); + } + } + } + + return dp[0][1]; + } +} +``` + +```cpp +class Solution { +public: + int stoneGameII(vector& piles) { + int n = piles.size(); + + vector suffixSum(n, 0); + suffixSum[n - 1] = piles[n - 1]; + for (int i = n - 2; i >= 0; i--) { + suffixSum[i] = piles[i] + suffixSum[i + 1]; + } + + vector> dp(n + 1, vector(n + 1, 0)); + + for (int i = n - 1; i >= 0; i--) { + for (int M = 1; M <= n; M++) { + for (int X = 1; X <= 2 * M; X++) { + if (i + X > n) break; + dp[i][M] = max(dp[i][M], suffixSum[i] - dp[i + X][max(M, X)]); + } + } + } + + return dp[0][1]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} piles + * @return {number} + */ + stoneGameII(piles) { + const n = piles.length; + + const suffixSum = Array(n).fill(0); + suffixSum[n - 1] = piles[n - 1]; + for (let i = n - 2; i >= 0; i--) { + suffixSum[i] = piles[i] + suffixSum[i + 1]; + } + + const dp = Array.from({ length: n + 1 }, () => Array(n + 1).fill(0)); + + for (let i = n - 1; i >= 0; i--) { + for (let M = 1; M <= n; M++) { + for (let X = 1; X <= 2 * M; X++) { + if (i + X > n) break; + dp[i][M] = Math.max(dp[i][M], suffixSum[i] - dp[i + X][Math.max(M, X)]); + } + } + } + + return dp[0][1]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 3)$ +* Space complexity: $O(n ^ 2)$ \ No newline at end of file diff --git a/articles/string-compression-ii.md b/articles/string-compression-ii.md new file mode 100644 index 000000000..d1021b9ea --- /dev/null +++ b/articles/string-compression-ii.md @@ -0,0 +1,480 @@ +## 1. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def getLengthOfOptimalCompression(self, s: str, k: int) -> int: + cache = {} + + def count(i, k, prev, prev_cnt): + if (i, k, prev, prev_cnt) in cache: + return cache[(i, k, prev, prev_cnt)] + if k < 0: + return float("inf") + if i == len(s): + return 0 + + if s[i] == prev: + incr = 1 if prev_cnt in [1, 9, 99] else 0 + res = incr + count(i + 1, k, prev, prev_cnt + 1) + else: + res = min( + count(i + 1, k - 1, prev, prev_cnt), # delete s[i] + 1 + count(i + 1, k, s[i], 1) # don't delete + ) + + cache[(i, k, prev, prev_cnt)] = res + return res + + return count(0, k, "", 0) +``` + +```java +public class Solution { + private final int INF = Integer.MAX_VALUE / 2; + private String s; + private int[][][][] dp; + + public int getLengthOfOptimalCompression(String s, int k) { + this.s = s; + int n = s.length(); + dp = new int[n + 1][k + 1][27][n + 1]; + for (int[][][] arr1 : dp) { + for (int[][] arr2 : arr1) { + for (int[] arr3 : arr2) { + Arrays.fill(arr3, -1); + } + } + } + return count(0, k, 26, 0); + } + + private int count(int i, int k, int prev, int prevCnt) { + if (k < 0) return INF; + if (i == s.length()) return 0; + if (dp[i][k][prev][prevCnt] != -1) return dp[i][k][prev][prevCnt]; + + int res; + if (prev == (s.charAt(i) - 'a')) { + int incr = (prevCnt == 1 || prevCnt == 9 || prevCnt == 99) ? 1 : 0; + res = incr + count(i + 1, k, prev, prevCnt + 1); + } else { + res = Math.min( + count(i + 1, k - 1, prev, prevCnt), // delete s[i] + 1 + count(i + 1, k, s.charAt(i) - 'a', 1) // don't delete + ); + } + + return dp[i][k][prev][prevCnt] = res; + } +} +``` + +```cpp +class Solution { + static const int INF = INT_MAX / 2; + vector>>> dp; + + int count(int i, int k, int prev, int prevCnt, string& s) { + if (k < 0) return INF; + if (i == s.size()) return 0; + if (dp[i][k][prev][prevCnt] != -1) return dp[i][k][prev][prevCnt]; + + int res; + if (prev == s[i] - 'a') { + int incr = (prevCnt == 1 || prevCnt == 9 || prevCnt == 99) ? 1 : 0; + res = incr + count(i + 1, k, prev, prevCnt + 1, s); + } else { + res = 1 + count(i + 1, k, s[i] - 'a', 1, s); // don't delete + if (k > 0) { + res = min(res, count(i + 1, k - 1, prev, prevCnt, s)); // delete s[i] + } + } + + return dp[i][k][prev][prevCnt] = res; + } + +public: + int getLengthOfOptimalCompression(string s, int k) { + int n = s.size(); + dp = vector>>>( + n + 1, vector>>(k + 1, vector>(27, vector(101, -1))) + ); + return count(0, k, 26, 0, s); + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @param {number} k + * @return {number} + */ + getLengthOfOptimalCompression(s, k) { + const INF = 1e9; + const n = s.length; + const dp = {}; + + const count = (i, k, prev, prevCnt) => { + if (k < 0) return INF; + if (i === n) return 0; + const key = `${i},${k},${prev},${prevCnt}`; + if (key in dp) return dp[key]; + + let res; + if (prev === s.charCodeAt(i) - 97) { + const incr = prevCnt === 1 || prevCnt === 9 || prevCnt === 99 ? 1 : 0; + res = incr + count(i + 1, k, prev, prevCnt + 1); + } else { + res = 1 + count(i + 1, k, s.charCodeAt(i) - 97, 1); // don't delete + if (k > 0) { + res = Math.min(res, count(i + 1, k - 1, prev, prevCnt)); // delete s[i] + } + } + + dp[key] = res; + return res; + }; + + return count(0, k, 26, 0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(k * n ^ 2)$ +* Space complexity: $O(k * n ^ 2)$ + +> Where $n$ is the length of the string $s$ and $k$ is the maximum number of characters that can be deleted from the string. + +--- + +## 2. Dynamic Programming (Top-Down Optimized) + +::tabs-start + +```python +class Solution: + def getLengthOfOptimalCompression(self, s: str, k: int) -> int: + n = len(s) + dp = {} + + def dfs(i, k): + if n - i <= k: + return 0 + if (i, k) in dp: + return dp[(i, k)] + + res = 150 + if k > 0: + res = dfs(i + 1, k - 1) + + freq = delCnt = 0 + comp_len = 1 + for j in range(i, n): + if s[i] == s[j]: + if freq in [1, 9, 99]: + comp_len += 1 + freq += 1 + else: + delCnt += 1 + if delCnt > k: + break + res = min(res, comp_len + dfs(j + 1, k - delCnt)) + dp[(i, k)] = res + return res + + return dfs(0, k) +``` + +```java +public class Solution { + private int n; + private int[][] dp; + + public int getLengthOfOptimalCompression(String s, int k) { + n = s.length(); + dp = new int[n + 1][k + 1]; + for (int[] row : dp) Arrays.fill(row, -1); + return dfs(0, k, s); + } + + private int dfs(int i, int k, String s) { + if (n - i <= k) return 0; + if (dp[i][k] != -1) return dp[i][k]; + + int res = 150; + if (k > 0) res = dfs(i + 1, k - 1, s); + + int freq = 0, delCnt = 0, comp_len = 1; + for (int j = i; j < n; j++) { + if (s.charAt(i) == s.charAt(j)) { + if (freq == 1 || freq == 9 || freq == 99) comp_len++; + freq++; + } else { + delCnt++; + if (delCnt > k) break; + } + res = Math.min(res, comp_len + dfs(j + 1, k - delCnt, s)); + } + dp[i][k] = res; + return res; + } +} +``` + +```cpp +class Solution { +private: + int n; + vector> dp; + + int dfs(int i, int k, const string& s) { + if (n - i <= k) return 0; + if (dp[i][k] != -1) return dp[i][k]; + + int res = 150; + if (k > 0) res = dfs(i + 1, k - 1, s); + + int freq = 0, delCnt = 0, comp_len = 1; + for (int j = i; j < n; j++) { + if (s[i] == s[j]) { + if (freq == 1 || freq == 9 || freq == 99) comp_len++; + freq++; + } else { + delCnt++; + if (delCnt > k) break; + } + res = min(res, comp_len + dfs(j + 1, k - delCnt, s)); + } + dp[i][k] = res; + return res; + } + +public: + int getLengthOfOptimalCompression(string s, int k) { + n = s.size(); + dp = vector>(n + 1, vector(k + 1, -1)); + return dfs(0, k, s); + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @param {number} k + * @return {number} + */ + getLengthOfOptimalCompression(s, k) { + const n = s.length; + const dp = Array.from({ length: n + 1 }, () => Array(k + 1).fill(-1)); + + const dfs = (i, k) => { + if (n - i <= k) return 0; + if (dp[i][k] !== -1) return dp[i][k]; + + let res = 150; + if (k > 0) res = dfs(i + 1, k - 1); + + let freq = 0, delCnt = 0, comp_len = 1; + for (let j = i; j < n; j++) { + if (s[i] === s[j]) { + if (freq === 1 || freq === 9 || freq === 99) comp_len++; + freq++; + } else { + delCnt++; + if (delCnt > k) break; + } + res = Math.min(res, comp_len + dfs(j + 1, k - delCnt)); + } + dp[i][k] = res; + return res; + }; + + return dfs(0, k); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2 * k)$ +* Space complexity: $O(n * k)$ + +> Where $n$ is the length of the string $s$ and $k$ is the maximum number of characters that can be deleted from the string. + +--- + +## 3. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def getLengthOfOptimalCompression(self, s: str, k: int) -> int: + n = len(s) + dp = [[150] * (k + 1) for _ in range(n)] + dp.append([0] * (k + 1)) + + for i in range(n - 1, -1, -1): + for rem_k in range(k + 1): + if rem_k > 0: + dp[i][rem_k] = dp[i + 1][rem_k - 1] + + freq = delCnt = 0 + comp_len = 1 + for j in range(i, n): + if s[i] == s[j]: + if freq in [1, 9, 99]: + comp_len += 1 + freq += 1 + else: + delCnt += 1 + if delCnt > rem_k: + break + dp[i][rem_k] = min(dp[i][rem_k], comp_len + dp[j + 1][rem_k - delCnt]) + + return dp[0][k] +``` + +```java +public class Solution { + public int getLengthOfOptimalCompression(String s, int k) { + int n = s.length(); + int[][] dp = new int[n + 1][k + 1]; + + for (int i = 0; i <= n; i++) { + for (int j = 0; j <= k; j++) { + dp[i][j] = 150; + } + } + + for (int remK = 0; remK <= k; remK++) { + dp[n][remK] = 0; + } + + for (int i = n - 1; i >= 0; i--) { + for (int remK = 0; remK <= k; remK++) { + if (remK > 0) { + dp[i][remK] = dp[i + 1][remK - 1]; + } + + int freq = 0, delCnt = 0, compLen = 1; + for (int j = i; j < n; j++) { + if (s.charAt(i) == s.charAt(j)) { + if (freq == 1 || freq == 9 || freq == 99) { + compLen++; + } + freq++; + } else { + delCnt++; + if (delCnt > remK) break; + } + dp[i][remK] = Math.min(dp[i][remK], compLen + dp[j + 1][remK - delCnt]); + } + } + } + + return dp[0][k]; + } +} +``` + +```cpp +class Solution { +public: + int getLengthOfOptimalCompression(string s, int k) { + int n = s.size(); + vector> dp(n + 1, vector(k + 1, 150)); + + for (int remK = 0; remK <= k; remK++) { + dp[n][remK] = 0; + } + + for (int i = n - 1; i >= 0; i--) { + for (int remK = 0; remK <= k; remK++) { + if (remK > 0) { + dp[i][remK] = dp[i + 1][remK - 1]; + } + + int freq = 0, delCnt = 0, compLen = 1; + for (int j = i; j < n; j++) { + if (s[i] == s[j]) { + if (freq == 1 || freq == 9 || freq == 99) { + compLen++; + } + freq++; + } else { + delCnt++; + if (delCnt > remK) break; + } + dp[i][remK] = min(dp[i][remK], compLen + dp[j + 1][remK - delCnt]); + } + } + } + + return dp[0][k]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @param {number} k + * @return {number} + */ + getLengthOfOptimalCompression(s, k) { + const n = s.length; + const dp = Array.from({ length: n + 1 }, () => Array(k + 1).fill(150)); + + for (let remK = 0; remK <= k; remK++) { + dp[n][remK] = 0; + } + + for (let i = n - 1; i >= 0; i--) { + for (let remK = 0; remK <= k; remK++) { + if (remK > 0) { + dp[i][remK] = dp[i + 1][remK - 1]; + } + + let freq = 0, delCnt = 0, compLen = 1; + for (let j = i; j < n; j++) { + if (s[i] === s[j]) { + if (freq === 1 || freq === 9 || freq === 99) { + compLen++; + } + freq++; + } else { + delCnt++; + if (delCnt > remK) break; + } + dp[i][remK] = Math.min(dp[i][remK], compLen + dp[j + 1][remK - delCnt]); + } + } + } + + return dp[0][k]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2 * k)$ +* Space complexity: $O(n * k)$ + +> Where $n$ is the length of the string $s$ and $k$ is the maximum number of characters that can be deleted from the string. \ No newline at end of file diff --git a/articles/target-sum.md b/articles/target-sum.md index aa52f1b10..819f11b56 100644 --- a/articles/target-sum.md +++ b/articles/target-sum.md @@ -335,10 +335,10 @@ class Solution { ### Time & Space Complexity -* Time complexity: $O(n * t)$ -* Space complexity: $O(n * t)$ +* Time complexity: $O(n * m)$ +* Space complexity: $O(n * m)$ -> Where $n$ is the length of the array $nums$ and $t$ is the sum of all the elements in the array. +> Where $n$ is the length of the array $nums$ and $m$ is the sum of all the elements in the array. --- @@ -506,10 +506,10 @@ class Solution { ### Time & Space Complexity -* Time complexity: $O(n * t)$ -* Space complexity: $O(n * t)$ +* Time complexity: $O(n * m)$ +* Space complexity: $O(n * m)$ -> Where $n$ is the length of the array $nums$ and $t$ is the sum of all the elements in the array. +> Where $n$ is the length of the array $nums$ and $m$ is the sum of all the elements in the array. --- @@ -675,7 +675,7 @@ class Solution { ### Time & Space Complexity -* Time complexity: $O(n * t)$ -* Space complexity: $O(t)$ +* Time complexity: $O(n * m)$ +* Space complexity: $O(m)$ -> Where $n$ is the length of the array $nums$ and $t$ is the sum of all the elements in the array. \ No newline at end of file +> Where $n$ is the length of the array $nums$ and $m$ is the sum of all the elements in the array. \ No newline at end of file From de6c6a97d2cf4ac8e6f7e82062e98a4ddd033c31 Mon Sep 17 00:00:00 2001 From: Sri Hari <94112314+Srihari2222@users.noreply.github.com> Date: Mon, 30 Dec 2024 04:52:30 +0530 Subject: [PATCH 22/45] Sri Hari: Batch-4/Neetcode-All/Added-articles (#3777) * Batch-4/Neetcode-All/Added-articles * Batch-4/Neetcode-All/Added-articles --- articles/4sum.md | 647 +++++++++++++ articles/arithmetic-slices-ii-subsequence.md | 499 ++++++++++ articles/boats-to-save-people.md | 221 +++++ articles/cherry-pickup-ii.md | 559 ++++++++++++ articles/concatenation-of-array.md | 4 +- articles/design-word-search-data-structure.md | 2 +- articles/find-k-closest-elements.md | 555 ++++++++++++ articles/freedom-trail.md | 845 +++++++++++++++++ articles/k-inverse-pairs-array.md | 599 ++++++++++++ .../minimum-difficulty-of-a-job-schedule.md | 682 ++++++++++++++ articles/minimum-falling-path-sum-ii.md | 854 ++++++++++++++++++ articles/rotate-array.md | 433 +++++++++ 12 files changed, 5897 insertions(+), 3 deletions(-) create mode 100644 articles/4sum.md create mode 100644 articles/arithmetic-slices-ii-subsequence.md create mode 100644 articles/boats-to-save-people.md create mode 100644 articles/cherry-pickup-ii.md create mode 100644 articles/find-k-closest-elements.md create mode 100644 articles/freedom-trail.md create mode 100644 articles/k-inverse-pairs-array.md create mode 100644 articles/minimum-difficulty-of-a-job-schedule.md create mode 100644 articles/minimum-falling-path-sum-ii.md create mode 100644 articles/rotate-array.md diff --git a/articles/4sum.md b/articles/4sum.md new file mode 100644 index 000000000..f99c11ddd --- /dev/null +++ b/articles/4sum.md @@ -0,0 +1,647 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def fourSum(self, nums: List[int], target: int) -> List[List[int]]: + n = len(nums) + nums.sort() + res = set() + + for a in range(n): + for b in range(a + 1, n): + for c in range(b + 1, n): + for d in range(c + 1, n): + if nums[a] + nums[b] + nums[c] + nums[d] == target: + res.add((nums[a], nums[b], nums[c], nums[d])) + return list(res) +``` + +```java +public class Solution { + public List> fourSum(int[] nums, int target) { + int n = nums.length; + Arrays.sort(nums); + Set> res = new HashSet<>(); + + for (int a = 0; a < n; a++) { + for (int b = a + 1; b < n; b++) { + for (int c = b + 1; c < n; c++) { + for (int d = c + 1; d < n; d++) { + if (nums[a] + nums[b] + 0L + nums[c] + nums[d] == target) { + res.add(Arrays.asList(nums[a], nums[b], nums[c], nums[d])); + } + } + } + } + } + + return new ArrayList<>(res); + } +} +``` + +```cpp +class Solution { +public: + vector> fourSum(vector& nums, int target) { + int n = nums.size(); + sort(nums.begin(), nums.end()); + set> res; + + for (int a = 0; a < n; a++) { + for (int b = a + 1; b < n; b++) { + for (int c = b + 1; c < n; c++) { + for (int d = c + 1; d < n; d++) { + if (nums[a] + nums[b] + 0LL + nums[c] + nums[d] == target) { + res.insert({nums[a], nums[b], nums[c], nums[d]}); + } + } + } + } + } + + return vector>(res.begin(), res.end()); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} target + * @return {number[][]} + */ + fourSum(nums, target) { + let n = nums.length; + nums.sort((a, b) => a - b); + let res = new Set(); + + for (let a = 0; a < n; a++) { + for (let b = a + 1; b < n; b++) { + for (let c = b + 1; c < n; c++) { + for (let d = c + 1; d < n; d++) { + if (nums[a] + nums[b] + nums[c] + nums[d] === target) { + res.add(JSON.stringify([nums[a], nums[b], nums[c], nums[d]])); + } + } + } + } + } + + return Array.from(res).map(JSON.parse); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 4)$ +* Space complexity: $O(m)$ + +> Where $n$ is the size of the array $nums$ and $m$ is the number of quadruplets. + +--- + +## 2. Hash Map + +::tabs-start + +```python +class Solution: + def fourSum(self, nums: List[int], target: int) -> List[List[int]]: + nums.sort() + count = defaultdict(int) + for num in nums: + count[num] += 1 + + res = [] + for i in range(len(nums)): + count[nums[i]] -= 1 + if i > 0 and nums[i] == nums[i - 1]: + continue + + for j in range(i + 1, len(nums)): + count[nums[j]] -= 1 + if j > i + 1 and nums[j] == nums[j - 1]: + continue + + for k in range(j + 1, len(nums)): + count[nums[k]] -= 1 + if k > j + 1 and nums[k] == nums[k - 1]: + continue + + fourth = target - (nums[i] + nums[j] + nums[k]) + if count[fourth] > 0: + res.append([nums[i], nums[j], nums[k], fourth]) + + for k in range(j + 1, len(nums)): + count[nums[k]] += 1 + + for j in range(i + 1, len(nums)): + count[nums[j]] += 1 + + return res +``` + +```java +public class Solution { + public List> fourSum(int[] nums, int target) { + Arrays.sort(nums); + Map count = new HashMap<>(); + for (int num : nums) { + count.put(num, count.getOrDefault(num, 0) + 1); + } + List> res = new ArrayList<>(); + + for (int i = 0; i < nums.length; i++) { + count.put(nums[i], count.get(nums[i]) - 1); + if (i > 0 && nums[i] == nums[i - 1]) continue; + + for (int j = i + 1; j < nums.length; j++) { + count.put(nums[j], count.get(nums[j]) - 1); + if (j > i + 1 && nums[j] == nums[j - 1]) continue; + + for (int k = j + 1; k < nums.length; k++) { + count.put(nums[k], count.get(nums[k]) - 1); + if (k > j + 1 && nums[k] == nums[k - 1]) continue; + + long fourth = target - (nums[i] + nums[j] + 0L + nums[k]); + if (fourth > Integer.MAX_VALUE || fourth < Integer.MIN_VALUE) { + continue; + } + if (count.getOrDefault((int) fourth, 0) > 0) { + res.add(Arrays.asList(nums[i], nums[j], nums[k], (int) fourth)); + } + } + + for (int k = j + 1; k < nums.length; k++) { + count.put(nums[k], count.get(nums[k]) + 1); + } + } + + for (int j = i + 1; j < nums.length; j++) { + count.put(nums[j], count.get(nums[j]) + 1); + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector> fourSum(vector& nums, int target) { + sort(nums.begin(), nums.end()); + unordered_map count; + for (int num : nums) { + count[num]++; + } + vector> res; + + for (int i = 0; i < nums.size(); i++) { + count[nums[i]]--; + if (i > 0 && nums[i] == nums[i - 1]) continue; + + for (int j = i + 1; j < nums.size(); j++) { + count[nums[j]]--; + if (j > i + 1 && nums[j] == nums[j - 1]) continue; + + for (int k = j + 1; k < nums.size(); k++) { + count[nums[k]]--; + if (k > j + 1 && nums[k] == nums[k - 1]) continue; + + long long fourth = target - (nums[i] + nums[j] + 0LL + nums[k]); + if (fourth < INT_MIN || fourth > INT_MAX) continue; + if (count[fourth] > 0) { + res.push_back({nums[i], nums[j], nums[k], int(fourth)}); + } + } + + for (int k = j + 1; k < nums.size(); k++) { + count[nums[k]]++; + } + } + + for (int j = i + 1; j < nums.size(); j++) { + count[nums[j]]++; + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} target + * @return {number[][]} + */ + fourSum(nums, target) { + nums.sort((a, b) => a - b); + const count = new Map(); + for (const num of nums) { + count.set(num, (count.get(num) || 0) + 1); + } + const res = []; + + for (let i = 0; i < nums.length; i++) { + count.set(nums[i], count.get(nums[i]) - 1); + if (i > 0 && nums[i] === nums[i - 1]) continue; + + for (let j = i + 1; j < nums.length; j++) { + count.set(nums[j], count.get(nums[j]) - 1); + if (j > i + 1 && nums[j] === nums[j - 1]) continue; + + for (let k = j + 1; k < nums.length; k++) { + count.set(nums[k], count.get(nums[k]) - 1); + if (k > j + 1 && nums[k] === nums[k - 1]) continue; + + const fourth = target - (nums[i] + nums[j] + nums[k]); + if ((count.get(fourth) || 0) > 0) { + res.push([nums[i], nums[j], nums[k], fourth]); + } + } + + for (let k = j + 1; k < nums.length; k++) { + count.set(nums[k], count.get(nums[k]) + 1); + } + } + + for (let j = i + 1; j < nums.length; j++) { + count.set(nums[j], count.get(nums[j]) + 1); + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 3)$ +* Space complexity: + * $O(n)$ space for the hash map. + * $O(m)$ space for the output array. + +> Where $n$ is the size of the array $nums$ and $m$ is the number of quadruplets. + +--- + +## 3. Two Pointers + +::tabs-start + +```python +class Solution: + def fourSum(self, nums: List[int], target: int) -> List[List[int]]: + nums.sort() + n = len(nums) + res = [] + + for i in range(n): + if i > 0 and nums[i] == nums[i - 1]: + continue + for j in range(i + 1, n): + if j > i + 1 and nums[j] == nums[j - 1]: + continue + left, right = j + 1, n - 1 + while left < right: + total = nums[i] + nums[j] + nums[left] + nums[right] + if total == target: + res.append([nums[i], nums[j], nums[left], nums[right]]) + left += 1 + right -= 1 + while left < right and nums[left] == nums[left - 1]: + left += 1 + while left < right and nums[right] == nums[right + 1]: + right -= 1 + elif total < target: + left += 1 + else: + right -= 1 + + return res +``` + +```java +public class Solution { + public List> fourSum(int[] nums, int target) { + Arrays.sort(nums); + List> res = new ArrayList<>(); + int n = nums.length; + + for (int i = 0; i < n; i++) { + if (i > 0 && nums[i] == nums[i - 1]) continue; + + for (int j = i + 1; j < n; j++) { + if (j > i + 1 && nums[j] == nums[j - 1]) continue; + + int left = j + 1, right = n - 1; + while (left < right) { + long sum = (long) nums[i] + nums[j] + nums[left] + nums[right]; + if (sum == target) { + res.add(Arrays.asList(nums[i], nums[j], nums[left], nums[right])); + left++; + right--; + while (left < right && nums[left] == nums[left - 1]) left++; + while (left < right && nums[right] == nums[right + 1]) right--; + } else if (sum < target) { + left++; + } else { + right--; + } + } + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + vector> fourSum(vector& nums, int target) { + vector> res; + int n = nums.size(); + sort(nums.begin(), nums.end()); + + for (int i = 0; i < n; i++) { + if (i > 0 && nums[i] == nums[i - 1]) continue; + + for (int j = i + 1; j < n; j++) { + if (j > i + 1 && nums[j] == nums[j - 1]) continue; + + int left = j + 1, right = n - 1; + while (left < right) { + long long sum = (long long) nums[i] + nums[j] + nums[left] + nums[right]; + if (sum == target) { + res.push_back({nums[i], nums[j], nums[left], nums[right]}); + left++; + right--; + while (left < right && nums[left] == nums[left - 1]) left++; + while (left < right && nums[right] == nums[right + 1]) right--; + } else if (sum < target) { + left++; + } else { + right--; + } + } + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} target + * @return {number[][]} + */ + fourSum(nums, target) { + nums.sort((a, b) => a - b); + const res = []; + const n = nums.length; + + for (let i = 0; i < n; i++) { + if (i > 0 && nums[i] === nums[i - 1]) continue; + + for (let j = i + 1; j < n; j++) { + if (j > i + 1 && nums[j] === nums[j - 1]) continue; + + let left = j + 1, right = n - 1; + while (left < right) { + const sum = nums[i] + nums[j] + nums[left] + nums[right]; + if (sum === target) { + res.push([nums[i], nums[j], nums[left], nums[right]]); + left++; + right--; + while (left < right && nums[left] === nums[left - 1]) left++; + while (left < right && nums[right] === nums[right + 1]) right--; + } else if (sum < target) { + left++; + } else { + right--; + } + } + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 3)$ +* Space complexity: + * $O(1)$ or $O(n)$ space depending on the sorting algorithm. + * $O(m)$ space for the output array. + +--- + +## 4. K-Sum + Two Pointers + +::tabs-start + +```python +class Solution: + def fourSum(self, nums: List[int], target: int) -> List[List[int]]: + nums.sort() + res, quad = [], [] + + def kSum(k, start, target): + if k == 2: + l, r = start, len(nums) - 1 + while l < r: + if nums[l] + nums[r] < target: + l += 1 + elif nums[l] + nums[r] > target: + r -= 1 + else: + res.append(quad + [nums[l], nums[r]]) + l += 1 + r -= 1 + while l < r and nums[l] == nums[l - 1]: + l += 1 + while l < r and nums[r] == nums[r + 1]: + r -= 1 + return + + for i in range(start, len(nums) - k + 1): + if i > start and nums[i] == nums[i - 1]: + continue + quad.append(nums[i]) + kSum(k - 1, i + 1, target - nums[i]) + quad.pop() + + kSum(4, 0, target) + return res +``` + +```java +public class Solution { + private List> res; + private List quad; + + public List> fourSum(int[] nums, int target) { + Arrays.sort(nums); + res = new ArrayList<>(); + quad = new ArrayList<>(); + kSum(nums, 4, 0, target); + return res; + } + + private void kSum(int[] nums, int k, int start, long target) { + if (k == 2) { + int l = start, r = nums.length - 1; + while (l < r) { + long sum = nums[l] + nums[r]; + if (sum < target) { + l++; + } else if (sum > target) { + r--; + } else { + res.add(new ArrayList<>(quad)); + res.get(res.size() - 1).add(nums[l]); + res.get(res.size() - 1).add(nums[r]); + l++; + r--; + while (l < r && nums[l] == nums[l - 1]) l++; + while (l < r && nums[r] == nums[r + 1]) r--; + } + } + return; + } + + for (int i = start; i < nums.length - k + 1; i++) { + if (i > start && nums[i] == nums[i - 1]) continue; + quad.add(nums[i]); + kSum(nums, k - 1, i + 1, target - nums[i]); + quad.remove(quad.size() - 1); + } + } +} +``` + +```cpp +class Solution { + vector> res; + vector quad; + +public: + vector> fourSum(vector& nums, int target) { + if (nums.size() < 4) return {}; + sort(nums.begin(), nums.end()); + kSum(nums, 4, 0, (long long) target); + return res; + } + +private: + void kSum(vector& nums, int k, int start, long long target) { + if (k == 2) { + int l = start, r = nums.size() - 1; + while (l < r) { + long long sum = (long long) nums[l] + nums[r]; + if (sum < target) { + l++; + } else if (sum > target) { + r--; + } else { + quad.push_back(nums[l]); + quad.push_back(nums[r]); + res.push_back(quad); + quad.pop_back(); + quad.pop_back(); + l++; + r--; + while (l < r && nums[l] == nums[l - 1]) l++; + while (l < r && nums[r] == nums[r + 1]) r--; + } + } + return; + } + + for (int i = start; i < nums.size() - k + 1; i++) { + if (i > start && nums[i] == nums[i - 1]) continue; + quad.push_back(nums[i]); + kSum(nums, k - 1, i + 1, target - nums[i]); + quad.pop_back(); + } + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} target + * @return {number[][]} + */ + fourSum(nums, target) { + nums.sort((a, b) => a - b); + const res = []; + const quad = []; + + const kSum = (k, start, target) => { + if (k === 2) { + let l = start, r = nums.length - 1; + while (l < r) { + const sum = nums[l] + nums[r]; + if (sum < target) { + l++; + } else if (sum > target) { + r--; + } else { + res.push([...quad, nums[l], nums[r]]); + l++; + r--; + while (l < r && nums[l] === nums[l - 1]) l++; + while (l < r && nums[r] === nums[r + 1]) r--; + } + } + return; + } + + for (let i = start; i < nums.length - k + 1; i++) { + if (i > start && nums[i] === nums[i - 1]) continue; + quad.push(nums[i]); + kSum(k - 1, i + 1, target - nums[i]); + quad.pop(); + } + }; + + kSum(4, 0, target); + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 3)$ +* Space complexity: + * $O(1)$ or $O(n)$ space depending on the sorting algorithm. + * $O(m)$ space for the output array. \ No newline at end of file diff --git a/articles/arithmetic-slices-ii-subsequence.md b/articles/arithmetic-slices-ii-subsequence.md new file mode 100644 index 000000000..427b1557c --- /dev/null +++ b/articles/arithmetic-slices-ii-subsequence.md @@ -0,0 +1,499 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def numberOfArithmeticSlices(self, nums: List[int]) -> int: + n = len(nums) + if n < 3: + return 0 + + INF = float('inf') + dp = {} + + def dfs(i, j, diff, flag): + if i == n: + return flag + if (i, j, diff, flag) in dp: + return dp[(i, j, diff, flag)] + + res = dfs(i + 1, j, diff, flag) + if j == -1: + res += dfs(i + 1, i, INF, flag) + else: + if diff == INF: + res += dfs(i + 1, i, nums[i] - nums[j], flag) + elif diff == nums[i] - nums[j]: + res += dfs(i + 1, i, diff, 1) + + dp[(i, j, diff, flag)] = res + return res + + return dfs(0, -1, INF, 0) +``` + +```java +public class Solution { + private static final long INF = (long) 1e15; + private Map dp = new HashMap<>(); + + public int numberOfArithmeticSlices(int[] nums) { + int n = nums.length; + if (n < 3) return 0; + + return dfs(nums, 0, -1, INF, 0); + } + + private int dfs(int[] nums, int i, int j, long diff, int flag) { + if (i == nums.length) { + return flag; + } + String key = i + "," + j + "," + diff + "," + flag; + if (dp.containsKey(key)) { + return dp.get(key); + } + + int res = dfs(nums, i + 1, j, diff, flag); + if (j == -1) { + res += dfs(nums, i + 1, i, INF, flag); + } else { + if (diff == INF) { + res += dfs(nums, i + 1, i, nums[i] - 0L - nums[j], flag); + } else if (diff == nums[i] - 0L - nums[j]) { + res += dfs(nums, i + 1, i, diff, 1); + } + } + + dp.put(key, res); + return res; + } +} +``` + +```cpp +class Solution { + static const long long INF = 1e15; + unordered_map dp; + + int dfs(vector& nums, int i, int j, long long diff, int flag) { + if (i == nums.size()) { + return flag; + } + + string key = to_string(i) + "," + to_string(j) + "," + to_string(diff) + "," + to_string(flag); + if (dp.count(key)) { + return dp[key]; + } + + int res = dfs(nums, i + 1, j, diff, flag); + if (j == -1) { + res += dfs(nums, i + 1, i, INF, flag); + } else { + if (diff == INF) { + res += dfs(nums, i + 1, i, nums[i] - 0LL - nums[j], flag); + } else if (diff == nums[i] - 0LL - nums[j]) { + res += dfs(nums, i + 1, i, diff, 1); + } + } + + dp[key] = res; + return res; + } + +public: + int numberOfArithmeticSlices(vector& nums) { + if (nums.size() < 3) return 0; + return dfs(nums, 0, -1, INF, 0); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + numberOfArithmeticSlices(nums) { + const n = nums.length; + if (n < 3) return 0; + + const INF = Infinity; + const dp = new Map(); + + const dfs = (i, j, diff, flag) => { + if (i === n) { + return flag; + } + const key = `${i},${j},${diff},${flag}`; + if (dp.has(key)) { + return dp.get(key); + } + + let res = dfs(i + 1, j, diff, flag); + if (j === -1) { + res += dfs(i + 1, i, INF, flag); + } else { + if (diff === INF) { + res += dfs(i + 1, i, nums[i] - nums[j], flag); + } else if (diff === nums[i] - nums[j]) { + res += dfs(i + 1, i, diff, 1); + } + } + + dp.set(key, res); + return res; + }; + + return dfs(0, -1, INF, 0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 3)$ +* Space complexity: $O(n ^ 3)$ + +--- + +## 2. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def numberOfArithmeticSlices(self, nums: List[int]) -> int: + res, n = 0, len(nums) + dp = [defaultdict(int) for _ in range(n)] + + for i in range(n): + for j in range(i): + diff = nums[i] - nums[j] + dp[i][diff] += 1 + dp[j][diff] + res += dp[j][diff] + + return res +``` + +```java +public class Solution { + public int numberOfArithmeticSlices(int[] nums) { + int n = nums.length; + int res = 0; + Map[] dp = new HashMap[n]; + + for (int i = 0; i < n; i++) { + dp[i] = new HashMap<>(); + for (int j = 0; j < i; j++) { + long diff = (long) nums[i] - nums[j]; + int count = dp[j].getOrDefault(diff, 0); + dp[i].put(diff, dp[i].getOrDefault(diff, 0) + count + 1); + res += count; + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int numberOfArithmeticSlices(vector& nums) { + int n = nums.size(); + int res = 0; + vector> dp(n); + + for (int i = 0; i < n; i++) { + for (int j = 0; j < i; j++) { + long long diff = (long long) nums[i] - nums[j]; + int count = dp[j][diff]; + dp[i][diff] += count + 1; + res += count; + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + numberOfArithmeticSlices(nums) { + const n = nums.length; + let res = 0; + const dp = Array.from({ length: n }, () => new Map()); + + for (let i = 0; i < n; i++) { + for (let j = 0; j < i; j++) { + const diff = nums[i] - nums[j]; + const count = dp[j].get(diff) || 0; + dp[i].set(diff, (dp[i].get(diff) || 0) + count + 1); + res += count; + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n ^ 2)$ + +--- + +## 3. Dynamic Programming (Optimization) - I + +::tabs-start + +```python +class Solution: + def numberOfArithmeticSlices(self, nums: List[int]) -> int: + res, n = 0, len(nums) + s = set(nums) + dp = [defaultdict(int) for _ in range(n)] + + for i in range(n): + for j in range(i): + diff = nums[i] - nums[j] + cnt = dp[j].get(diff, 0) + if nums[i] + diff in s: + dp[i][diff] += cnt + 1 + res += cnt + + return res +``` + +```java +public class Solution { + public int numberOfArithmeticSlices(int[] nums) { + int res = 0, n = nums.length; + Set s = new HashSet<>(); + for (int num : nums) s.add(num); + + Map[] dp = new HashMap[n]; + for (int i = 0; i < n; i++) dp[i] = new HashMap<>(); + + for (int i = 0; i < n; i++) { + for (int j = 0; j < i; j++) { + long diff = (long) nums[i] - nums[j]; + int cnt = dp[j].getOrDefault(diff, 0); + if (s.contains((int) (nums[i] + diff))) { + dp[i].put(diff, dp[i].getOrDefault(diff, 0) + cnt + 1); + } + res += cnt; + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int numberOfArithmeticSlices(vector& nums) { + int res = 0, n = nums.size(); + unordered_set s(nums.begin(), nums.end()); + vector> dp(n); + + for (int i = 0; i < n; i++) { + for (int j = 0; j < i; j++) { + long long diff = (long long)nums[i] - nums[j]; + int cnt = dp[j].count(diff) ? dp[j][diff] : 0; + if (s.count(nums[i] + diff)) { + dp[i][diff] += cnt + 1; + } + res += cnt; + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + numberOfArithmeticSlices(nums) { + let res = 0, n = nums.length; + const s = new Set(nums); + const dp = Array.from({ length: n }, () => new Map()); + + for (let i = 0; i < n; i++) { + for (let j = 0; j < i; j++) { + const diff = nums[i] - nums[j]; + const cnt = dp[j].get(diff) || 0; + if (s.has(nums[i] + diff)) { + dp[i].set(diff, (dp[i].get(diff) || 0) + cnt + 1); + } + res += cnt; + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n ^ 2)$ + +--- + +## 4. Dynamic Programming (Optimization) - II + +::tabs-start + +```python +class Solution: + def numberOfArithmeticSlices(self, nums: List[int]) -> int: + res = 0 + mpIdx = defaultdict(list) + n = len(nums) + dp = [[0] * n for _ in range(n)] + + for i in range(n): + mpIdx[nums[i]].append(i) + + for i in range(n): + for j in range(i): + prev = 2 * nums[j] - nums[i] + if prev in mpIdx: + for k in mpIdx[prev]: + if k >= j: + break + dp[i][j] += dp[j][k] + 1 + res += dp[i][j] + + return res +``` + +```java +public class Solution { + public int numberOfArithmeticSlices(int[] nums) { + int res = 0; + Map> mpIdx = new HashMap<>(); + int n = nums.length; + int[][] dp = new int[n][n]; + + for (int i = 0; i < n; i++) { + mpIdx.putIfAbsent(nums[i], new ArrayList<>()); + mpIdx.get(nums[i]).add(i); + } + + for (int i = 0; i < n; i++) { + for (int j = 0; j < i; j++) { + long prev = 2L * nums[j] - nums[i]; + if (prev < Integer.MIN_VALUE || prev > Integer.MAX_VALUE) { + continue; + } + + if (mpIdx.containsKey((int) prev)) { + for (int k : mpIdx.get((int) prev)) { + if (k >= j) break; + dp[i][j] += dp[j][k] + 1; + } + } + res += dp[i][j]; + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int numberOfArithmeticSlices(vector& nums) { + int res = 0; + unordered_map> mpIdx; + int n = nums.size(); + vector> dp(n, vector(n, 0)); + + for (int i = 0; i < n; i++) { + mpIdx[nums[i]].push_back(i); + } + + for (int i = 0; i < n; i++) { + for (int j = 0; j < i; j++) { + long prev = 2L * nums[j] - nums[i]; + if (prev < INT_MIN || prev > INT_MAX) continue; + + if (mpIdx.count(prev)) { + for (int k : mpIdx[prev]) { + if (k >= j) break; + dp[i][j] += dp[j][k] + 1; + } + } + res += dp[i][j]; + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + numberOfArithmeticSlices(nums) { + let res = 0; + const mpIdx = new Map(); + const n = nums.length; + const dp = Array.from({ length: n }, () => Array(n).fill(0)); + + for (let i = 0; i < n; i++) { + if (!mpIdx.has(nums[i])) mpIdx.set(nums[i], []); + mpIdx.get(nums[i]).push(i); + } + + for (let i = 0; i < n; i++) { + for (let j = 0; j < i; j++) { + const prev = 2 * nums[j] - nums[i]; + + if (mpIdx.has(prev)) { + for (const k of mpIdx.get(prev)) { + if (k >= j) break; + dp[i][j] += dp[j][k] + 1; + } + } + res += dp[i][j]; + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n ^ 2)$ \ No newline at end of file diff --git a/articles/boats-to-save-people.md b/articles/boats-to-save-people.md new file mode 100644 index 000000000..8abd6a9a7 --- /dev/null +++ b/articles/boats-to-save-people.md @@ -0,0 +1,221 @@ +## 1. Sorting + Two Pointers + +::tabs-start + +```python +class Solution: + def numRescueBoats(self, people: List[int], limit: int) -> int: + people.sort() + res, l, r = 0, 0, len(people) - 1 + while l <= r: + remain = limit - people[r] + r -= 1 + res += 1 + if l <= r and remain >= people[l]: + l += 1 + return res +``` + +```java +public class Solution { + public int numRescueBoats(int[] people, int limit) { + Arrays.sort(people); + int res = 0, l = 0, r = people.length - 1; + while (l <= r) { + int remain = limit - people[r--]; + res++; + if (l <= r && remain >= people[l]) { + l++; + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int numRescueBoats(vector& people, int limit) { + sort(people.begin(), people.end()); + int res = 0, l = 0, r = people.size() - 1; + while (l <= r) { + int remain = limit - people[r--]; + res++; + if (l <= r && remain >= people[l]) { + l++; + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} people + * @param {number} limit + * @return {number} + */ + numRescueBoats(people, limit) { + people.sort((a, b) => a - b); + let res = 0, l = 0, r = people.length - 1; + while (l <= r) { + let remain = limit - people[r--]; + res++; + if (l <= r && remain >= people[l]) { + l++; + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(1)$ or $O(n)$ depending on the sorting algorithm. + +--- + +## 2. Counting Sort + +::tabs-start + +```python +class Solution: + def numRescueBoats(self, people: List[int], limit: int) -> int: + m = max(people) + count = [0] * (m + 1) + for p in people: + count[p] += 1 + + idx, i = 0, 1 + while idx < len(people): + while count[i] == 0: + i += 1 + people[idx] = i + count[i] -= 1 + idx += 1 + + res, l, r = 0, 0, len(people) - 1 + while l <= r: + remain = limit - people[r] + r -= 1 + res += 1 + if l <= r and remain >= people[l]: + l += 1 + return res +``` + +```java +public class Solution { + public int numRescueBoats(int[] people, int limit) { + int m = Arrays.stream(people).max().getAsInt(); + int[] count = new int[m + 1]; + for (int p : people) { + count[p]++; + } + + int idx = 0, i = 1; + while (idx < people.length) { + while (count[i] == 0) { + i++; + } + people[idx++] = i; + count[i]--; + } + + int res = 0, l = 0, r = people.length - 1; + while (l <= r) { + int remain = limit - people[r--]; + res++; + if (l <= r && remain >= people[l]) { + l++; + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int numRescueBoats(vector& people, int limit) { + int m = *max_element(people.begin(), people.end()); + vector count(m + 1, 0); + for (int p : people) { + count[p]++; + } + + int idx = 0, i = 1; + while (idx < people.size()) { + while (count[i] == 0) { + i++; + } + people[idx++] = i; + count[i]--; + } + + int res = 0, l = 0, r = people.size() - 1; + while (l <= r) { + int remain = limit - people[r--]; + res++; + if (l <= r && remain >= people[l]) { + l++; + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} people + * @param {number} limit + * @return {number} + */ + numRescueBoats(people, limit) { + const m = Math.max(...people); + const count = new Array(m + 1).fill(0); + for (const p of people) { + count[p]++; + } + + let idx = 0, i = 1; + while (idx < people.length) { + while (count[i] === 0) { + i++; + } + people[idx++] = i; + count[i]--; + } + + let res = 0, l = 0, r = people.length - 1; + while (l <= r) { + const remain = limit - people[r--]; + res++; + if (l <= r && remain >= people[l]) { + l++; + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(m)$ + +> Where $n$ is the size of the input array and $m$ is the maximum value in the array. \ No newline at end of file diff --git a/articles/cherry-pickup-ii.md b/articles/cherry-pickup-ii.md new file mode 100644 index 000000000..cc5ccc3da --- /dev/null +++ b/articles/cherry-pickup-ii.md @@ -0,0 +1,559 @@ +## 1. Recursion + +::tabs-start + +```python +class Solution: + def cherryPickup(self, grid: List[List[int]]) -> int: + ROWS, COLS = len(grid), len(grid[0]) + + def dfs(r, c1, c2): + if c1 < 0 or c2 < 0 or c1 >= COLS or c2 >= COLS or c1 > c2: + return 0 + if r == ROWS - 1: + return grid[r][c1] + (grid[r][c2] if c1 != c2 else 0) + + res = 0 + for c1_d in [-1, 0, 1]: + for c2_d in [-1, 0, 1]: + res = max(res, dfs(r + 1, c1 + c1_d, c2 + c2_d)) + + return res + grid[r][c1] + (grid[r][c2] if c1 != c2 else 0) + + return dfs(0, 0, COLS - 1) +``` + +```java +public class Solution { + public int cherryPickup(int[][] grid) { + int ROWS = grid.length, COLS = grid[0].length; + + return dfs(0, 0, COLS - 1, grid, ROWS, COLS); + } + + private int dfs(int r, int c1, int c2, int[][] grid, int ROWS, int COLS) { + if (c1 < 0 || c2 < 0 || c1 >= COLS || c2 >= COLS || c1 > c2) { + return 0; + } + if (r == ROWS - 1) { + return grid[r][c1] + (c1 == c2 ? 0 : grid[r][c2]); + } + + int res = 0; + for (int c1_d = -1; c1_d <= 1; c1_d++) { + for (int c2_d = -1; c2_d <= 1; c2_d++) { + res = Math.max(res, dfs(r + 1, c1 + c1_d, c2 + c2_d, grid, ROWS, COLS)); + } + } + return res + grid[r][c1] + (c1 == c2 ? 0 : grid[r][c2]); + } +} +``` + +```cpp +class Solution { +public: + int cherryPickup(vector>& grid) { + int ROWS = grid.size(), COLS = grid[0].size(); + return dfs(0, 0, COLS - 1, grid, ROWS, COLS); + } + +private: + int dfs(int r, int c1, int c2, vector>& grid, int ROWS, int COLS) { + if (c1 < 0 || c2 < 0 || c1 >= COLS || c2 >= COLS || c1 > c2) { + return 0; + } + if (r == ROWS - 1) { + return grid[r][c1] + (c1 == c2 ? 0 : grid[r][c2]); + } + + int res = 0; + for (int c1_d = -1; c1_d <= 1; c1_d++) { + for (int c2_d = -1; c2_d <= 1; c2_d++) { + res = max(res, dfs(r + 1, c1 + c1_d, c2 + c2_d, grid, ROWS, COLS)); + } + } + return res + grid[r][c1] + (c1 == c2 ? 0 : grid[r][c2]); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} grid + * @return {number} + */ + cherryPickup(grid) { + const ROWS = grid.length, COLS = grid[0].length; + + const dfs = (r, c1, c2) => { + if (c1 < 0 || c2 < 0 || c1 >= COLS || c2 >= COLS || c1 > c2) return 0; + if (r === ROWS - 1) { + return grid[r][c1] + (c1 === c2 ? 0 : grid[r][c2]); + } + + let res = 0; + for (let c1_d = -1; c1_d <= 1; c1_d++) { + for (let c2_d = -1; c2_d <= 1; c2_d++) { + res = Math.max(res, dfs(r + 1, c1 + c1_d, c2 + c2_d)); + } + } + return res + grid[r][c1] + (c1 === c2 ? 0 : grid[r][c2]); + }; + + return dfs(0, 0, COLS - 1); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * 9 ^ n)$ +* Space complexity: $O(n)$ for recursion stack. + +> Where $n$ is the number of rows and $m$ is the number of columns in the grid. + +--- + +## 2. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def cherryPickup(self, grid: List[List[int]]) -> int: + ROWS, COLS = len(grid), len(grid[0]) + cache = {} + + def dfs(r, c1, c2): + if (r, c1, c2) in cache: + return cache[(r, c1, c2)] + if c1 == c2 or min(c1, c2) < 0 or max(c1, c2) >= COLS: + return 0 + if r == ROWS - 1: + return grid[r][c1] + grid[r][c2] + + res = 0 + for c1_d in [-1, 0, 1]: + for c2_d in [-1, 0, 1]: + res = max(res, dfs(r + 1, c1 + c1_d, c2 + c2_d)) + + cache[(r, c1, c2)] = res + grid[r][c1] + (grid[r][c2] if c1 != c2 else 0) + return cache[(r, c1, c2)] + + return dfs(0, 0, COLS - 1) +``` + +```java +public class Solution { + private int[][][] cache; + public int cherryPickup(int[][] grid) { + int ROWS = grid.length, COLS = grid[0].length; + cache = new int[ROWS][COLS][COLS]; + for (int[][] i : cache) { + for (int[] j : i) { + Arrays.fill(j, -1); + } + } + + return dfs(0, 0, COLS - 1, grid); + } + + private int dfs(int r, int c1, int c2, int[][] grid) { + if (Math.min(c1, c2) < 0 || Math.max(c1, c2) >= grid[0].length) { + return 0; + } + if (cache[r][c1][c2] != -1) { + return cache[r][c1][c2]; + } + if (r == grid.length - 1) { + return cache[r][c1][c2] = grid[r][c1] + (c1 == c2 ? 0 : grid[r][c2]); + } + + int res = 0; + for (int c1_d = -1; c1_d <= 1; c1_d++) { + for (int c2_d = -1; c2_d <= 1; c2_d++) { + res = Math.max(res, dfs(r + 1, c1 + c1_d, c2 + c2_d, grid)); + } + } + return cache[r][c1][c2] = res + grid[r][c1] + (c1 == c2 ? 0 : grid[r][c2]); + } +} +``` + +```cpp +class Solution { + vector>> cache; + +public: + int cherryPickup(vector>& grid) { + int ROWS = grid.size(), COLS = grid[0].size(); + cache.assign(ROWS, vector>(COLS, vector(COLS, -1))); + return dfs(0, 0, COLS - 1, grid); + } + +private: + int dfs(int r, int c1, int c2, vector>& grid) { + if (min(c1, c2) < 0 || max(c1, c2) >= grid[0].size()) { + return 0; + } + if (cache[r][c1][c2] != -1) { + return cache[r][c1][c2]; + } + if (r == grid.size() - 1) { + return cache[r][c1][c2] = grid[r][c1] + (c1 == c2 ? 0 : grid[r][c2]); + } + + int res = 0; + for (int c1_d = -1; c1_d <= 1; c1_d++) { + for (int c2_d = -1; c2_d <= 1; c2_d++) { + res = max(res, dfs(r + 1, c1 + c1_d, c2 + c2_d, grid)); + } + } + return cache[r][c1][c2] = res + grid[r][c1] + (c1 == c2 ? 0 : grid[r][c2]); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} grid + * @return {number} + */ + cherryPickup(grid) { + const ROWS = grid.length, COLS = grid[0].length; + const cache = Array.from({ length: ROWS }, () => + Array.from({ length: COLS }, () => + Array(COLS).fill(-1) + ) + ); + + const dfs = (r, c1, c2) => { + if (Math.min(c1, c2) < 0 || Math.max(c1, c2) >= COLS) { + return 0; + } + if (cache[r][c1][c2] !== -1) return cache[r][c1][c2]; + if (r === ROWS - 1) { + return cache[r][c1][c2] = grid[r][c1] + (c1 === c2 ? 0 : grid[r][c2]); + } + + let res = 0; + for (let c1_d = -1; c1_d <= 1; c1_d++) { + for (let c2_d = -1; c2_d <= 1; c2_d++) { + res = Math.max(res, dfs(r + 1, c1 + c1_d, c2 + c2_d)); + } + } + return cache[r][c1][c2] = res + grid[r][c1] + (c1 === c2 ? 0 : grid[r][c2]); + }; + + return dfs(0, 0, COLS - 1); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * m ^ 2)$ +* Space complexity: $O(n * m ^ 2)$ + +> Where $n$ is the number of rows and $m$ is the number of columns in the grid. + +--- + +## 3. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def cherryPickup(self, grid: List[List[int]]) -> int: + ROWS, COLS = len(grid), len(grid[0]) + dp = [[[0] * COLS for _ in range(COLS)] for _ in range(ROWS)] + + for r in range(ROWS - 1, -1, -1): + for c1 in range(COLS): + for c2 in range(COLS): + res = grid[r][c1] + if c1 != c2: + res += grid[r][c2] + + if r != ROWS - 1: + max_cherries = 0 + for d1 in [-1, 0, 1]: + for d2 in [-1, 0, 1]: + nc1, nc2 = c1 + d1, c2 + d2 + if 0 <= nc1 < COLS and 0 <= nc2 < COLS: + max_cherries = max(max_cherries, dp[r + 1][nc1][nc2]) + res += max_cherries + + dp[r][c1][c2] = res + + return dp[0][0][COLS - 1] +``` + +```java +public class Solution { + public int cherryPickup(int[][] grid) { + int ROWS = grid.length, COLS = grid[0].length; + int[][][] dp = new int[ROWS][COLS][COLS]; + + for (int r = ROWS - 1; r >= 0; r--) { + for (int c1 = 0; c1 < COLS; c1++) { + for (int c2 = 0; c2 < COLS; c2++) { + int res = grid[r][c1]; + if (c1 != c2) { + res += grid[r][c2]; + } + + if (r != ROWS - 1) { + int maxCherries = 0; + for (int d1 = -1; d1 <= 1; d1++) { + for (int d2 = -1; d2 <= 1; d2++) { + int nc1 = c1 + d1, nc2 = c2 + d2; + if (nc1 >= 0 && nc1 < COLS && nc2 >= 0 && nc2 < COLS) { + maxCherries = Math.max(maxCherries, dp[r + 1][nc1][nc2]); + } + } + } + res += maxCherries; + } + + dp[r][c1][c2] = res; + } + } + } + + return dp[0][0][COLS - 1]; + } +} +``` + +```cpp +class Solution { +public: + int cherryPickup(vector>& grid) { + int ROWS = grid.size(), COLS = grid[0].size(); + int dp[ROWS][COLS][COLS]; + + for (int r = ROWS - 1; r >= 0; r--) { + for (int c1 = 0; c1 < COLS; c1++) { + for (int c2 = 0; c2 < COLS; c2++) { + int res = grid[r][c1]; + if (c1 != c2) { + res += grid[r][c2]; + } + + if (r != ROWS - 1) { + int maxCherries = 0; + for (int d1 = -1; d1 <= 1; d1++) { + for (int d2 = -1; d2 <= 1; d2++) { + int nc1 = c1 + d1, nc2 = c2 + d2; + if (nc1 >= 0 && nc1 < COLS && nc2 >= 0 && nc2 < COLS) { + maxCherries = max(maxCherries, dp[r + 1][nc1][nc2]); + } + } + } + res += maxCherries; + } + + dp[r][c1][c2] = res; + } + } + } + + return dp[0][0][COLS - 1]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} grid + * @return {number} + */ + cherryPickup(grid) { + const ROWS = grid.length, COLS = grid[0].length; + const dp = Array.from({ length: ROWS }, () => + Array.from({ length: COLS }, () => Array(COLS).fill(0)) + ); + + for (let r = ROWS - 1; r >= 0; r--) { + for (let c1 = 0; c1 < COLS; c1++) { + for (let c2 = 0; c2 < COLS; c2++) { + let res = grid[r][c1]; + if (c1 !== c2) { + res += grid[r][c2]; + } + + if (r !== ROWS - 1) { + let maxCherries = 0; + for (let d1 = -1; d1 <= 1; d1++) { + for (let d2 = -1; d2 <= 1; d2++) { + const nc1 = c1 + d1, nc2 = c2 + d2; + if (nc1 >= 0 && nc1 < COLS && nc2 >= 0 && nc2 < COLS) { + maxCherries = Math.max(maxCherries, dp[r + 1][nc1][nc2]); + } + } + } + res += maxCherries; + } + + dp[r][c1][c2] = res; + } + } + } + + return dp[0][0][COLS - 1]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * m ^ 2)$ +* Space complexity: $O(n * m ^ 2)$ + +> Where $n$ is the number of rows and $m$ is the number of columns in the grid. + +--- + +## 4. Dynamic Programming (Space Optimized) + +::tabs-start + +```python +class Solution: + def cherryPickup(self, grid: List[List[int]]) -> int: + ROWS, COLS = len(grid), len(grid[0]) + dp = [[0] * COLS for _ in range(COLS)] + + for r in reversed(range(ROWS)): + cur_dp = [[0] * COLS for _ in range(COLS)] + for c1 in range(COLS): + for c2 in range(c1, COLS): + max_cherries = 0 + cherries = grid[r][c1] + (grid[r][c2] if c1 != c2 else 0) + for d1 in [-1, 0, 1]: + for d2 in [-1, 0, 1]: + nc1, nc2 = c1 + d1, c2 + d2 + if 0 <= nc1 < COLS and 0 <= nc2 < COLS: + max_cherries = max(max_cherries, cherries + dp[nc1][nc2]) + cur_dp[c1][c2] = max_cherries + dp = cur_dp + + return dp[0][COLS - 1] +``` + +```java +public class Solution { + public int cherryPickup(int[][] grid) { + int ROWS = grid.length, COLS = grid[0].length; + int[][] dp = new int[COLS][COLS]; + + for (int r = ROWS - 1; r >= 0; r--) { + int[][] cur_dp = new int[COLS][COLS]; + for (int c1 = 0; c1 < COLS; c1++) { + for (int c2 = c1; c2 < COLS; c2++) { + int maxCherries = 0; + int cherries = grid[r][c1] + (c1 == c2 ? 0 : grid[r][c2]); + for (int d1 = -1; d1 <= 1; d1++) { + for (int d2 = -1; d2 <= 1; d2++) { + int nc1 = c1 + d1, nc2 = c2 + d2; + if (nc1 >= 0 && nc1 < COLS && nc2 >= 0 && nc2 < COLS) { + maxCherries = Math.max(maxCherries, cherries + dp[nc1][nc2]); + } + } + } + cur_dp[c1][c2] = maxCherries; + } + } + dp = cur_dp; + } + return dp[0][COLS - 1]; + } +} +``` + +```cpp +class Solution { +public: + int cherryPickup(vector>& grid) { + int ROWS = grid.size(), COLS = grid[0].size(); + vector> dp(COLS, vector(COLS, 0)); + + for (int r = ROWS - 1; r >= 0; r--) { + vector> cur_dp(COLS, vector(COLS, 0)); + for (int c1 = 0; c1 < COLS; c1++) { + for (int c2 = c1; c2 < COLS; c2++) { + int maxCherries = 0; + int cherries = grid[r][c1] + (c1 == c2 ? 0 : grid[r][c2]); + for (int d1 = -1; d1 <= 1; d1++) { + for (int d2 = -1; d2 <= 1; d2++) { + int nc1 = c1 + d1, nc2 = c2 + d2; + if (nc1 >= 0 && nc1 < COLS && nc2 >= 0 && nc2 < COLS) { + maxCherries = max(maxCherries, cherries + dp[nc1][nc2]); + } + } + } + cur_dp[c1][c2] = maxCherries; + } + } + dp = cur_dp; + } + return dp[0][COLS - 1]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} grid + * @return {number} + */ + cherryPickup(grid) { + const ROWS = grid.length, COLS = grid[0].length; + let dp = Array.from({ length: COLS }, () => Array(COLS).fill(0)); + + for (let r = ROWS - 1; r >= 0; r--) { + const cur_dp = Array.from({ length: COLS }, () => Array(COLS).fill(0)); + for (let c1 = 0; c1 < COLS; c1++) { + for (let c2 = c1; c2 < COLS; c2++) { + let maxCherries = 0; + const cherries = grid[r][c1] + (c1 === c2 ? 0 : grid[r][c2]); + for (let d1 = -1; d1 <= 1; d1++) { + for (let d2 = -1; d2 <= 1; d2++) { + const nc1 = c1 + d1, nc2 = c2 + d2; + if (nc1 >= 0 && nc1 < COLS && nc2 >= 0 && nc2 < COLS) { + maxCherries = Math.max(maxCherries, cherries + dp[nc1][nc2]); + } + } + } + cur_dp[c1][c2] = maxCherries; + } + } + dp = cur_dp; + } + + return dp[0][COLS - 1]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * m ^ 2)$ +* Space complexity: $O(m ^ 2)$ + +> Where $n$ is the number of rows and $m$ is the number of columns in the grid. \ No newline at end of file diff --git a/articles/concatenation-of-array.md b/articles/concatenation-of-array.md index 24e788da2..3ea2a1e68 100644 --- a/articles/concatenation-of-array.md +++ b/articles/concatenation-of-array.md @@ -65,7 +65,7 @@ class Solution { ### Time & Space Complexity * Time complexity: $O(n)$ -* Space complexity: $O(1)$ +* Space complexity: $O(n)$ for the output array. --- @@ -132,4 +132,4 @@ class Solution { ### Time & Space Complexity * Time complexity: $O(n)$ -* Space complexity: $O(1)$ \ No newline at end of file +* Space complexity: $O(n)$ for the output array. \ No newline at end of file diff --git a/articles/design-word-search-data-structure.md b/articles/design-word-search-data-structure.md index 2771fc3f8..501e42785 100644 --- a/articles/design-word-search-data-structure.md +++ b/articles/design-word-search-data-structure.md @@ -226,7 +226,7 @@ class WordDictionary { ### Time & Space Complexity -* Time complexity: $O(n)$ for $addWord()$, $O(m * n)$ for $search()$. +* Time complexity: $O(1)$ for $addWord()$, $O(m * n)$ for $search()$. * Space complexity: $O(m * n)$ > Where $m$ is the number of words added and $n$ is the length of the string. diff --git a/articles/find-k-closest-elements.md b/articles/find-k-closest-elements.md new file mode 100644 index 000000000..d4a177c89 --- /dev/null +++ b/articles/find-k-closest-elements.md @@ -0,0 +1,555 @@ +## 1. Sorting (Custom Comparator) + +::tabs-start + +```python +class Solution: + def findClosestElements(self, arr: List[int], k: int, x: int) -> List[int]: + arr.sort(key=lambda num: (abs(num - x), num)) + return sorted(arr[:k]) +``` + +```java +public class Solution { + public List findClosestElements(int[] arr, int k, int x) { + List list = new ArrayList<>(); + for (int num : arr) { + list.add(num); + } + + list.sort((a, b) -> { + int diff = Math.abs(a - x) - Math.abs(b - x); + return diff == 0 ? Integer.compare(a, b) : diff; + }); + + List result = list.subList(0, k); + Collections.sort(result); + return result; + } +} +``` + +```cpp +class Solution { +public: + vector findClosestElements(vector& arr, int k, int x) { + sort(arr.begin(), arr.end(), [x](int a, int b) { + int diff = abs(a - x) - abs(b - x); + return diff == 0 ? a < b : diff < 0; + }); + vector result(arr.begin(), arr.begin() + k); + sort(result.begin(), result.end()); + return result; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} arr + * @param {number} k + * @param {number} x + * @return {number[]} + */ + findClosestElements(arr, k, x) { + arr.sort((a, b) => { + const diff = Math.abs(a - x) - Math.abs(b - x); + return diff === 0 ? a - b : diff; + }); + const result = arr.slice(0, k); + return result.sort((a, b) => a - b); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n + k \log k)$ +* Space complexity: + * $O(1)$ or $O(n)$ space depending on the sorting algorithm. + * $O(k)$ space for the output array. + +> Where $n$ is the size of the input array and $k$ is the number of closest elements to find. + +--- + +## 2. Linear Scan + Two Pointers + +::tabs-start + +```python +class Solution: + def findClosestElements(self, arr: List[int], k: int, x: int) -> List[int]: + n = len(arr) + idx = 0 + for i in range(1, n): + if abs(x - arr[idx]) > abs(x - arr[i]): + idx = i + + res = [arr[idx]] + l, r = idx - 1, idx + 1 + + while len(res) < k: + if l >= 0 and r < n: + if abs(x - arr[l]) <= abs(x - arr[r]): + res.append(arr[l]) + l -= 1 + else: + res.append(arr[r]) + r += 1 + elif l >= 0: + res.append(arr[l]) + l -= 1 + elif r < n: + res.append(arr[r]) + r += 1 + + return sorted(res) +``` + +```java +public class Solution { + public List findClosestElements(int[] arr, int k, int x) { + int n = arr.length; + int idx = 0; + for (int i = 1; i < n; i++) { + if (Math.abs(x - arr[idx]) > Math.abs(x - arr[i])) { + idx = i; + } + } + + List res = new ArrayList<>(); + res.add(arr[idx]); + int l = idx - 1, r = idx + 1; + + while (res.size() < k) { + if (l >= 0 && r < n) { + if (Math.abs(x - arr[l]) <= Math.abs(x - arr[r])) { + res.add(arr[l--]); + } else { + res.add(arr[r++]); + } + } else if (l >= 0) { + res.add(arr[l--]); + } else if (r < n) { + res.add(arr[r++]); + } + } + + Collections.sort(res); + return res; + } +} +``` + +```cpp +class Solution { +public: + vector findClosestElements(vector& arr, int k, int x) { + int n = arr.size(); + int idx = 0; + for (int i = 1; i < n; i++) { + if (abs(x - arr[idx]) > abs(x - arr[i])) { + idx = i; + } + } + + vector res = {arr[idx]}; + int l = idx - 1, r = idx + 1; + + while (res.size() < k) { + if (l >= 0 && r < n) { + if (abs(x - arr[l]) <= abs(x - arr[r])) { + res.push_back(arr[l--]); + } else { + res.push_back(arr[r++]); + } + } else if (l >= 0) { + res.push_back(arr[l--]); + } else if (r < n) { + res.push_back(arr[r++]); + } + } + + sort(res.begin(), res.end()); + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} arr + * @param {number} k + * @param {number} x + * @return {number[]} + */ + findClosestElements(arr, k, x) { + const n = arr.length; + let idx = 0; + for (let i = 1; i < n; i++) { + if (Math.abs(x - arr[idx]) > Math.abs(x - arr[i])) { + idx = i; + } + } + + const res = [arr[idx]]; + let l = idx - 1, r = idx + 1; + + while (res.length < k) { + if (l >= 0 && r < n) { + if (Math.abs(x - arr[l]) <= Math.abs(x - arr[r])) { + res.push(arr[l--]); + } else { + res.push(arr[r++]); + } + } else if (l >= 0) { + res.push(arr[l--]); + } else if (r < n) { + res.push(arr[r++]); + } + } + + return res.sort((a, b) => a - b); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n + k \log k)$ +* Space complexity: + * $O(1)$ or $O(k)$ space depending on the sorting algorithm. + * $O(k)$ space for the output array. + +> Where $n$ is the size of the input array and $k$ is the number of closest elements to find. + +--- + +## 3. Two Pointers + +::tabs-start + +```python +class Solution: + def findClosestElements(self, arr: List[int], k: int, x: int) -> List[int]: + l, r = 0, len(arr) - 1 + while r - l >= k: + if abs(x - arr[l]) <= abs(x - arr[r]): + r -= 1 + else: + l += 1 + + return arr[l: r + 1] +``` + +```java +public class Solution { + public List findClosestElements(int[] arr, int k, int x) { + int l = 0, r = arr.length - 1; + while (r - l >= k) { + if (Math.abs(x - arr[l]) <= Math.abs(x - arr[r])) { + r--; + } else { + l++; + } + } + List result = new ArrayList<>(); + for (int i = l; i <= r; i++) { + result.add(arr[i]); + } + return result; + } +} +``` + +```cpp +class Solution { +public: + vector findClosestElements(vector& arr, int k, int x) { + int l = 0, r = arr.size() - 1; + while (r - l >= k) { + if (abs(x - arr[l]) <= abs(x - arr[r])) { + r--; + } else { + l++; + } + } + return vector(arr.begin() + l, arr.begin() + r + 1); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} arr + * @param {number} k + * @param {number} x + * @return {number[]} + */ + findClosestElements(arr, k, x) { + let l = 0, r = arr.length - 1; + while (r - l >= k) { + if (Math.abs(x - arr[l]) <= Math.abs(x - arr[r])) { + r--; + } else { + l++; + } + } + return arr.slice(l, r + 1); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n - k)$ +* Space complexity: $O(k)$ for the output array. + +> Where $n$ is the size of the input array and $k$ is the number of closest elements to find. + +--- + +## 4. Binary Search + Two Pointers + +::tabs-start + +```python +class Solution: + def findClosestElements(self, arr: List[int], k: int, x: int) -> List[int]: + l, r = 0, len(arr) - 1 + while l < r: + mid = (l + r) // 2 + if arr[mid] < x: + l = mid + 1 + else: + r = mid + + l, r = l - 1, l + while r - l - 1 < k: + if l < 0: + r += 1 + elif r >= len(arr): + l -= 1 + elif abs(arr[l] - x) <= abs(arr[r] - x): + l -= 1 + else: + r += 1 + + return arr[l + 1:r] +``` + +```java +public class Solution { + public List findClosestElements(int[] arr, int k, int x) { + int l = 0, r = arr.length - 1; + while (l < r) { + int mid = (l + r) / 2; + if (arr[mid] < x) { + l = mid + 1; + } else { + r = mid; + } + } + + l = l - 1; + r = l + 1; + while (r - l - 1 < k) { + if (l < 0) { + r++; + } else if (r >= arr.length) { + l--; + } else if (Math.abs(arr[l] - x) <= Math.abs(arr[r] - x)) { + l--; + } else { + r++; + } + } + + List result = new ArrayList<>(); + for (int i = l + 1; i < r; i++) { + result.add(arr[i]); + } + return result; + } +} +``` + +```cpp +class Solution { +public: + vector findClosestElements(vector& arr, int k, int x) { + int l = 0, r = arr.size() - 1; + while (l < r) { + int mid = (l + r) / 2; + if (arr[mid] < x) { + l = mid + 1; + } else { + r = mid; + } + } + + l = l - 1; + r = l + 1; + while (r - l - 1 < k) { + if (l < 0) { + r++; + } else if (r >= arr.size()) { + l--; + } else if (abs(arr[l] - x) <= abs(arr[r] - x)) { + l--; + } else { + r++; + } + } + + return vector(arr.begin() + l + 1, arr.begin() + r); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} arr + * @param {number} k + * @param {number} x + * @return {number[]} + */ + findClosestElements(arr, k, x) { + let l = 0, r = arr.length - 1; + while (l < r) { + const mid = Math.floor((l + r) / 2); + if (arr[mid] < x) { + l = mid + 1; + } else { + r = mid; + } + } + + l = l - 1; + r = l + 1; + while (r - l - 1 < k) { + if (l < 0) { + r++; + } else if (r >= arr.length) { + l--; + } else if (Math.abs(arr[l] - x) <= Math.abs(arr[r] - x)) { + l--; + } else { + r++; + } + } + + return arr.slice(l + 1, r); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(\log n + k)$ +* Space complexity: $O(k)$ for the output array. + +> Where $n$ is the size of the input array and $k$ is the number of closest elements to find. + +--- + +## 5. Binary Search + +::tabs-start + +```python +class Solution: + def findClosestElements(self, arr: List[int], k: int, x: int) -> List[int]: + l, r = 0, len(arr) - k + while l < r: + m = (l + r) // 2 + if x - arr[m] > arr[m + k] - x: + l = m + 1 + else: + r = m + return arr[l:l + k] +``` + +```java +public class Solution { + public List findClosestElements(int[] arr, int k, int x) { + int l = 0, r = arr.length - k; + while (l < r) { + int m = (l + r) / 2; + if (x - arr[m] > arr[m + k] - x) { + l = m + 1; + } else { + r = m; + } + } + List result = new ArrayList<>(); + for (int i = l; i < l + k; i++) { + result.add(arr[i]); + } + return result; + } +} +``` + +```cpp +class Solution { +public: + vector findClosestElements(vector& arr, int k, int x) { + int l = 0, r = arr.size() - k; + while (l < r) { + int m = (l + r) / 2; + if (x - arr[m] > arr[m + k] - x) { + l = m + 1; + } else { + r = m; + } + } + return vector(arr.begin() + l, arr.begin() + l + k); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} arr + * @param {number} k + * @param {number} x + * @return {number[]} + */ + findClosestElements(arr, k, x) { + let l = 0, r = arr.length - k; + while (l < r) { + const m = Math.floor((l + r) / 2); + if (x - arr[m] > arr[m + k] - x) { + l = m + 1; + } else { + r = m; + } + } + return arr.slice(l, l + k); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(\log (n - k) + k)$ +* Space complexity: $O(k)$ for the output array. + +> Where $n$ is the size of the input array and $k$ is the number of closest elements to find. \ No newline at end of file diff --git a/articles/freedom-trail.md b/articles/freedom-trail.md new file mode 100644 index 000000000..9c7ef1c89 --- /dev/null +++ b/articles/freedom-trail.md @@ -0,0 +1,845 @@ +## 1. Recursion + +::tabs-start + +```python +class Solution: + def findRotateSteps(self, ring: str, key: str) -> int: + def dfs(r, k): + if k == len(key): + return 0 + + res = float("inf") + for i, c in enumerate(ring): + if c == key[k]: + min_dist = min(abs(r - i), len(ring) - abs(r - i)) + res = min(res, min_dist + 1 + dfs(i, k + 1)) + return res + + return dfs(0, 0) +``` + +```java +public class Solution { + public int findRotateSteps(String ring, String key) { + return dfs(0, 0, ring, key); + } + + private int dfs(int r, int k, String ring, String key) { + if (k == key.length()) return 0; + + int res = Integer.MAX_VALUE; + for (int i = 0; i < ring.length(); i++) { + if (ring.charAt(i) == key.charAt(k)) { + int minDist = Math.min(Math.abs(r - i), ring.length() - Math.abs(r - i)); + res = Math.min(res, minDist + 1 + dfs(i, k + 1, ring, key)); + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int findRotateSteps(string ring, string key) { + return dfs(0, 0, ring, key); + } + +private: + int dfs(int r, int k, const string& ring, const string& key) { + if (k == key.size()) return 0; + + int res = INT_MAX; + for (int i = 0; i < ring.size(); i++) { + if (ring[i] == key[k]) { + int minDist = min(abs(r - i), int(ring.size()) - abs(r - i)); + res = min(res, minDist + 1 + dfs(i, k + 1, ring, key)); + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} ring + * @param {string} key + * @return {number} + */ + findRotateSteps(ring, key) { + const dfs = (r, k) => { + if (k === key.length) return 0; + + let res = Infinity; + for (let i = 0; i < ring.length; i++) { + if (ring[i] === key[k]) { + const minDist = Math.min( + Math.abs(r - i), + ring.length - Math.abs(r - i) + ); + res = Math.min(res, minDist + 1 + dfs(i, k + 1)); + } + } + return res; + }; + + return dfs(0, 0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ m)$ +* Space complexity: $O(m)$ for recursion stack. + +> Where $n$ is the length of the $ring$ and $m$ is the length of the $key$. + +--- + +## 2. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def findRotateSteps(self, ring: str, key: str) -> int: + n = len(ring) + m = len(key) + dp = {} + + def dfs(r, k): + if k == m: + return 0 + if (r, k) in dp: + return dp[(r, k)] + + res = float("inf") + for i, c in enumerate(ring): + if c == key[k]: + min_dist = min(abs(r - i), n - abs(r - i)) + res = min(res, min_dist + 1 + dfs(i, k + 1)) + dp[(r, k)] = res + return res + + return dfs(0, 0) +``` + +```java +public class Solution { + private int[][] dp; + + public int findRotateSteps(String ring, String key) { + int n = ring.length(); + int m = key.length(); + dp = new int[n][m]; + for (int[] row : dp) { + Arrays.fill(row, -1); + } + return dfs(0, 0, ring, key); + } + + private int dfs(int r, int k, String ring, String key) { + if (k == key.length()) return 0; + if (dp[r][k] != -1) return dp[r][k]; + + int res = Integer.MAX_VALUE; + for (int i = 0; i < ring.length(); i++) { + if (ring.charAt(i) == key.charAt(k)) { + int minDist = Math.min(Math.abs(r - i), ring.length() - Math.abs(r - i)); + res = Math.min(res, minDist + 1 + dfs(i, k + 1, ring, key)); + } + } + + dp[r][k] = res; + return res; + } +} +``` + +```cpp +class Solution { + vector> dp; + +public: + int findRotateSteps(string ring, string key) { + int n = ring.size(); + int m = key.size(); + dp.assign(n, vector(m, -1)); + return dfs(0, 0, ring, key); + } + +private: + int dfs(int r, int k, string& ring, string& key) { + if (k == key.size()) return 0; + if (dp[r][k] != -1) return dp[r][k]; + + int res = INT_MAX; + for (int i = 0; i < ring.size(); i++) { + if (ring[i] == key[k]) { + int minDist = min(abs(r - i), int(ring.size()) - abs(r - i)); + res = min(res, minDist + 1 + dfs(i, k + 1, ring, key)); + } + } + + dp[r][k] = res; + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} ring + * @param {string} key + * @return {number} + */ + findRotateSteps(ring, key) { + const n = ring.length; + const m = key.length; + const dp = Array.from({ length: n }, () => Array(m).fill(-1)); + + const dfs = (r, k) => { + if (k === key.length) return 0; + if (dp[r][k] !== -1) return dp[r][k]; + + let res = Infinity; + for (let i = 0; i < ring.length; i++) { + if (ring[i] === key[k]) { + const minDist = Math.min( + Math.abs(r - i), + ring.length - Math.abs(r - i) + ); + res = Math.min(res, minDist + 1 + dfs(i, k + 1)); + } + } + + dp[r][k] = res; + return res; + }; + + return dfs(0, 0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2 * m)$ +* Space complexity: $O(n * m)$ + +> Where $n$ is the length of the $ring$ and $m$ is the length of the $key$. + +--- + +## 3. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def findRotateSteps(self, ring: str, key: str) -> int: + n = len(ring) + m = len(key) + dp = [[float("inf")] * n for _ in range(m + 1)] + + for i in range(n): + dp[m][i] = 0 + + for k in range(m - 1, -1, -1): + for r in range(n): + for i in range(n): + if ring[i] == key[k]: + min_dist = min(abs(r - i), n - abs(r - i)) + dp[k][r] = min(dp[k][r], min_dist + 1 + dp[k + 1][i]) + + return dp[0][0] +``` + +```java +public class Solution { + public int findRotateSteps(String ring, String key) { + int n = ring.length(); + int m = key.length(); + int[][] dp = new int[m + 1][n]; + for (int i = 0; i <= m; i++) { + for (int j = 0; j < n; j++) { + dp[i][j] = Integer.MAX_VALUE; + } + } + + for (int i = 0; i < n; i++) { + dp[m][i] = 0; + } + + for (int k = m - 1; k >= 0; k--) { + for (int r = 0; r < n; r++) { + for (int i = 0; i < n; i++) { + if (ring.charAt(i) == key.charAt(k)) { + int minDist = Math.min(Math.abs(r - i), n - Math.abs(r - i)); + dp[k][r] = Math.min(dp[k][r], minDist + 1 + dp[k + 1][i]); + } + } + } + } + return dp[0][0]; + } +} +``` + +```cpp +class Solution { +public: + int findRotateSteps(string ring, string key) { + int n = ring.size(); + int m = key.size(); + vector> dp(m + 1, vector(n, INT_MAX)); + + for (int i = 0; i < n; ++i) { + dp[m][i] = 0; + } + + for (int k = m - 1; k >= 0; --k) { + for (int r = 0; r < n; ++r) { + for (int i = 0; i < n; ++i) { + if (ring[i] == key[k]) { + int minDist = min(abs(r - i), n - abs(r - i)); + dp[k][r] = min(dp[k][r], minDist + 1 + dp[k + 1][i]); + } + } + } + } + return dp[0][0]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} ring + * @param {string} key + * @return {number} + */ + findRotateSteps(ring, key) { + const n = ring.length; + const m = key.length; + const dp = Array.from({ length: m + 1 }, () => + Array(n).fill(Infinity) + ); + + for (let i = 0; i < n; i++) { + dp[m][i] = 0; + } + + for (let k = m - 1; k >= 0; k--) { + for (let r = 0; r < n; r++) { + for (let i = 0; i < n; i++) { + if (ring[i] === key[k]) { + const minDist = Math.min(Math.abs(r - i), n - Math.abs(r - i)); + dp[k][r] = Math.min(dp[k][r], minDist + 1 + dp[k + 1][i]); + } + } + } + } + return dp[0][0]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2 * m)$ +* Space complexity: $O(n * m)$ + +> Where $n$ is the length of the $ring$ and $m$ is the length of the $key$. + +--- + +## 4. Dynamic Programming (Space Optimized) - I + +::tabs-start + +```python +class Solution: + def findRotateSteps(self, ring: str, key: str) -> int: + n = len(ring) + m = len(key) + dp = [0] * n + + adj = [[] for _ in range(26)] + for i in range(n): + adj[ord(ring[i]) - ord('a')].append(i) + + for k in range(m - 1, -1, -1): + next_dp = [float("inf")] * n + for r in range(n): + for i in adj[ord(key[k]) - ord('a')]: + min_dist = min(abs(r - i), n - abs(r - i)) + next_dp[r] = min(next_dp[r], min_dist + 1 + dp[i]) + dp = next_dp + + return dp[0] +``` + +```java +public class Solution { + public int findRotateSteps(String ring, String key) { + int n = ring.length(); + int m = key.length(); + int[] dp = new int[n]; + + List[] adj = new ArrayList[26]; + for (int i = 0; i < 26; i++) { + adj[i] = new ArrayList<>(); + } + for (int i = 0; i < n; i++) { + adj[ring.charAt(i) - 'a'].add(i); + } + + for (int k = m - 1; k >= 0; k--) { + int[] nextDp = new int[n]; + Arrays.fill(nextDp, Integer.MAX_VALUE); + for (int r = 0; r < n; r++) { + for (int i : adj[key.charAt(k) - 'a']) { + int minDist = Math.min(Math.abs(r - i), n - Math.abs(r - i)); + nextDp[r] = Math.min(nextDp[r], minDist + 1 + dp[i]); + } + } + dp = nextDp; + } + + return dp[0]; + } +} +``` + +```cpp +class Solution { +public: + int findRotateSteps(string ring, string key) { + int n = ring.size(); + int m = key.size(); + vector dp(n, 0); + + vector> adj(26); + for (int i = 0; i < n; ++i) { + adj[ring[i] - 'a'].push_back(i); + } + + for (int k = m - 1; k >= 0; --k) { + vector nextDp(n, INT_MAX); + for (int r = 0; r < n; ++r) { + for (int& i : adj[key[k] - 'a']) { + int minDist = min(abs(r - i), n - abs(r - i)); + nextDp[r] = min(nextDp[r], minDist + 1 + dp[i]); + } + } + dp = nextDp; + } + + return dp[0]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} ring + * @param {string} key + * @return {number} + */ + findRotateSteps(ring, key) { + const n = ring.length; + const m = key.length; + let dp = new Array(n).fill(0); + + const adj = Array.from({ length: 26 }, () => []); + for (let i = 0; i < n; i++) { + adj[ring.charCodeAt(i) - 97].push(i); + } + + for (let k = m - 1; k >= 0; k--) { + const nextDp = new Array(n).fill(Infinity); + for (let r = 0; r < n; r++) { + for (const i of adj[key.charCodeAt(k) - 97]) { + const minDist = Math.min(Math.abs(r - i), n - Math.abs(r - i)); + nextDp[r] = Math.min(nextDp[r], minDist + 1 + dp[i]); + } + } + dp = nextDp; + } + + return dp[0]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2 * m)$ +* Space complexity: $O(n)$ + +> Where $n$ is the length of the $ring$ and $m$ is the length of the $key$. + +--- + +## 5. Dynamic Programming (Space optimized) - II + +::tabs-start + +```python +class Solution: + def findRotateSteps(self, ring: str, key: str) -> int: + n = len(ring) + m = len(key) + dp = [min(i, n - i) for i in range(n)] + + adj = [[] for _ in range(26)] + for i in range(n): + adj[ord(ring[i]) - ord('a')].append(i) + + for k in range(1, m): + for r in adj[ord(key[k]) - ord('a')]: + min_dist = float("inf") + for i in adj[ord(key[k - 1]) - ord('a')]: + min_dist = min( + min_dist, + min(abs(r - i), n - abs(r - i)) + dp[i] + ) + dp[r] = min_dist + + return min(dp[i] for i in adj[ord(key[-1]) - ord('a')]) + m +``` + +```java +public class Solution { + public int findRotateSteps(String ring, String key) { + int n = ring.length(); + int m = key.length(); + int[] dp = new int[n]; + + for (int i = 0; i < n; i++) { + dp[i] = Math.min(i, n - i); + } + + List[] adj = new ArrayList[26]; + for (int i = 0; i < 26; i++) { + adj[i] = new ArrayList<>(); + } + + for (int i = 0; i < n; i++) { + adj[ring.charAt(i) - 'a'].add(i); + } + + for (int k = 1; k < m; k++) { + for (int r : adj[key.charAt(k) - 'a']) { + int minDist = Integer.MAX_VALUE; + for (int i : adj[key.charAt(k - 1) - 'a']) { + minDist = Math.min(minDist, + Math.min(Math.abs(r - i), n - Math.abs(r - i)) + dp[i] + ); + } + dp[r] = minDist; + } + } + + int result = Integer.MAX_VALUE; + for (int i : adj[key.charAt(m - 1) - 'a']) { + result = Math.min(result, dp[i]); + } + + return result + m; + } +} +``` + +```cpp +class Solution { +public: + int findRotateSteps(string ring, string key) { + int n = ring.size(), m = key.size(); + vector dp(n); + + for (int i = 0; i < n; i++) { + dp[i] = min(i, n - i); + } + + vector> adj(26); + for (int i = 0; i < n; i++) { + adj[ring[i] - 'a'].push_back(i); + } + + for (int k = 1; k < m; k++) { + for (int r : adj[key[k] - 'a']) { + int minDist = INT_MAX; + for (int i : adj[key[k - 1] - 'a']) { + minDist = min(minDist, min(abs(r - i), n - abs(r - i)) + dp[i]); + } + dp[r] = minDist; + } + } + + int result = INT_MAX; + for (int& i : adj[key[m - 1] - 'a']) { + result = min(result, dp[i]); + } + + return result + m; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} ring + * @param {string} key + * @return {number} + */ + findRotateSteps(ring, key) { + const n = ring.length; + const m = key.length; + const dp = Array(n).fill(0).map((_, i) => Math.min(i, n - i)); + + const adj = Array.from({ length: 26 }, () => []); + for (let i = 0; i < n; i++) { + adj[ring.charCodeAt(i) - 97].push(i); + } + + for (let k = 1; k < m; k++) { + for (let r of adj[key.charCodeAt(k) - 97]) { + let minDist = Infinity; + for (let i of adj[key.charCodeAt(k - 1) - 97]) { + minDist = Math.min(minDist, + Math.min(Math.abs(r - i), n - Math.abs(r - i)) + dp[i] + ); + } + dp[r] = minDist; + } + } + + return Math.min(...adj[key.charCodeAt(m - 1) - 97].map(i => dp[i])) + m; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2 * m)$ +* Space complexity: $O(n)$ + +> Where $n$ is the length of the $ring$ and $m$ is the length of the $key$. + +--- + +## 6. Dynamic Programming (Optimal) + +::tabs-start + +```python +class Solution: + def findRotateSteps(self, ring: str, key: str) -> int: + n = len(ring) + m = len(key) + + dp = [0] * n + next_dp = [0] * n + + adj = [[] for _ in range(26)] + for i, c in enumerate(ring): + adj[ord(c) - ord('a')].append(i) + + for k in range(m - 1, -1, -1): + c = ord(key[k]) - ord('a') + it, N = 0, len(adj[c]) + + for r in range(n): + if ord(ring[r]) - ord('a') != c: + next_dp[r] = float('inf') + while it < N and adj[c][it] < r: + it += 1 + + nextIdx = adj[c][it] if it < N else adj[c][0] + prevIdx = adj[c][it - 1] if it > 0 else adj[c][-1] + + next_dp[r] = min( + (r - prevIdx if r > prevIdx else n - (prevIdx - r)) + dp[prevIdx], + (nextIdx - r if nextIdx > r else n - (r - nextIdx)) + dp[nextIdx] + ) + else: + next_dp[r] = dp[r] + + dp, next_dp = next_dp, dp + + return dp[0] + m +``` + +```java +public class Solution { + public int findRotateSteps(String ring, String key) { + int n = ring.length(); + int m = key.length(); + + int[] dp = new int[n]; + int[] nextDp = new int[n]; + List[] adj = new ArrayList[26]; + + for (int i = 0; i < 26; i++) { + adj[i] = new ArrayList<>(); + } + for (int i = 0; i < n; i++) { + adj[ring.charAt(i) - 'a'].add(i); + } + + for (int k = m - 1; k >= 0; k--) { + int c = key.charAt(k) - 'a'; + int it = 0, N = adj[c].size(); + + for (int r = 0; r < n; r++) { + if (ring.charAt(r) - 'a' != c) { + nextDp[r] = Integer.MAX_VALUE; + while (it < N && adj[c].get(it) < r) { + it++; + } + + int nextIdx = it < N ? adj[c].get(it) : adj[c].get(0); + int prevIdx = it > 0 ? adj[c].get(it - 1) : adj[c].get(N - 1); + + nextDp[r] = Math.min( + (r > prevIdx ? r - prevIdx : n - (prevIdx - r)) + dp[prevIdx], + (nextIdx > r ? nextIdx - r : n - (r - nextIdx)) + dp[nextIdx] + ); + } else { + nextDp[r] = dp[r]; + } + } + + int[] temp = dp; + dp = nextDp; + nextDp = temp; + } + + return dp[0] + m; + } +} +``` + +```cpp +class Solution { +public: + int findRotateSteps(string ring, string key) { + int n = ring.size(), m = key.size(); + + vector dp(n, 0); + vector nextDp(n, 0); + vector> adj(26); + + for (int i = 0; i < n; i++) { + adj[ring[i] - 'a'].push_back(i); + } + + for (int k = m - 1; k >= 0; k--) { + int c = key[k] - 'a'; + int it = 0, N = adj[c].size(); + + for (int r = 0; r < n; r++) { + if (ring[r] - 'a' != c) { + nextDp[r] = INT_MAX; + while (it < N && adj[c][it] < r) { + it++; + } + + int nextIdx = it < N ? adj[c][it] : adj[c][0]; + int prevIdx = it > 0 ? adj[c][it - 1] : adj[c][N - 1]; + + nextDp[r] = min( + (r > prevIdx ? r - prevIdx : n - (prevIdx - r)) + dp[prevIdx], + (nextIdx > r ? nextIdx - r : n - (r - nextIdx)) + dp[nextIdx] + ); + } else { + nextDp[r] = dp[r]; + } + } + + dp.swap(nextDp); + } + + return dp[0] + m; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} ring + * @param {string} key + * @return {number} + */ + findRotateSteps(ring, key) { + const n = ring.length, m = key.length; + + let dp = Array(n).fill(0); + let nextDp = Array(n).fill(0); + const adj = Array.from({ length: 26 }, () => []); + + for (let i = 0; i < n; i++) { + adj[ring.charCodeAt(i) - 97].push(i); + } + + for (let k = m - 1; k >= 0; k--) { + const c = key.charCodeAt(k) - 97; + let it = 0, N = adj[c].length; + + for (let r = 0; r < n; r++) { + if (ring.charCodeAt(r) - 97 !== c) { + nextDp[r] = Infinity; + while (it < N && adj[c][it] < r) { + it++; + } + + const nextIdx = it < N ? adj[c][it] : adj[c][0]; + const prevIdx = it > 0 ? adj[c][it - 1] : adj[c][N - 1]; + + nextDp[r] = Math.min( + (r > prevIdx ? r - prevIdx : n - (prevIdx - r)) + dp[prevIdx], + (nextIdx > r ? nextIdx - r : n - (r - nextIdx)) + dp[nextIdx] + ); + } else { + nextDp[r] = dp[r]; + } + } + + [dp, nextDp] = [nextDp, dp]; + } + + return dp[0] + m; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * m)$ +* Space complexity: $O(n)$ + +> Where $n$ is the length of the $ring$ and $m$ is the length of the $key$. \ No newline at end of file diff --git a/articles/k-inverse-pairs-array.md b/articles/k-inverse-pairs-array.md new file mode 100644 index 000000000..926e484d8 --- /dev/null +++ b/articles/k-inverse-pairs-array.md @@ -0,0 +1,599 @@ +## 1. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def kInversePairs(self, n: int, k: int) -> int: + MOD = 10**9 + 7 + cache = {} + + def count(n, k): + if n == 0: + return 1 if k == 0 else 0 + if k < 0: + return 0 + if (n, k) in cache: + return cache[(n, k)] + + cache[(n, k)] = 0 + for i in range(n): + cache[(n, k)] = (cache[(n, k)] + count(n - 1, k - i)) % MOD + + return cache[(n, k)] + + return count(n, k) +``` + +```java +public class Solution { + private static final int MOD = 1_000_000_007; + private int[][] dp; + + public int kInversePairs(int n, int k) { + dp = new int[n + 1][k + 1]; + for (int i = 0; i <= n; i++) { + for (int j = 0; j <= k; j++) { + dp[i][j] = -1; + } + } + return count(n, k); + } + + private int count(int n, int k) { + if (n == 0) return k == 0 ? 1 : 0; + if (k < 0) return 0; + if (dp[n][k] != -1) return dp[n][k]; + + int res = 0; + for (int i = 0; i < n; i++) { + res = (res + count(n - 1, k - i)) % MOD; + } + + dp[n][k] = res; + return res; + } +} +``` + +```cpp +class Solution { +private: + static const int MOD = 1e9 + 7; + vector> dp; + + int count(int n, int k) { + if (n == 0) { + return k == 0 ? 1 : 0; + } + if (k < 0) { + return 0; + } + if (dp[n][k] != -1) { + return dp[n][k]; + } + + int res = 0; + for (int i = 0; i < n; ++i) { + res = (res + count(n - 1, k - i)) % MOD; + } + dp[n][k] = res; + return res; + } + +public: + int kInversePairs(int n, int k) { + dp.assign(n + 1, vector(k + 1, -1)); + return count(n, k); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number} k + * @return {number} + */ + kInversePairs(n, k) { + const MOD = 1e9 + 7; + const dp = Array.from({ length: n + 1 }, () => Array(k + 1).fill(-1)); + + const count = (n, k) => { + if (n === 0) return k === 0 ? 1 : 0; + if (k < 0) return 0; + if (dp[n][k] !== -1) return dp[n][k]; + + let res = 0; + for (let i = 0; i < n; i++) { + res = (res + count(n - 1, k - i)) % MOD; + } + + dp[n][k] = res; + return res; + }; + + return count(n, k); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2 * k)$ +* Space complexity: $O(n * k)$ + +> Where $n$ is the size of the permutation and $k$ is the number of inverse pairs in the permutation. + +--- + +## 2. Dynamic Programming (Top-Down Optimized) + +::tabs-start + +```python +class Solution: + def kInversePairs(self, n: int, k: int) -> int: + MOD = 10**9 + 7 + dp = [[-1] * (k + 1) for _ in range(n + 1)] + + def count(n, k): + if k == 0: + return 1 + if n == 1: + return 0 + if n * (n - 1) // 2 < k: + return 0 + if n * (n - 1) // 2 == k: + return 1 + if dp[n][k] != -1: + return dp[n][k] + + res = count(n, k - 1) + if k >= n: + res -= count(n - 1, k - n) + res = (res + count(n - 1, k)) % MOD + + dp[n][k] = res + return res + + return count(n, k) +``` + +```java +public class Solution { + private static final int MOD = 1_000_000_007; + private int[][] dp; + + public int kInversePairs(int n, int k) { + dp = new int[n + 1][k + 1]; + for (int[] row : dp) { + Arrays.fill(row, -1); + } + return count(n, k); + } + + private int count(int n, int k) { + if (k == 0) return 1; + if (n == 1) return 0; + if (n * (n - 1) / 2 < k) return 0; + if (n * (n - 1) / 2 == k) return 1; + if (dp[n][k] != -1) return dp[n][k]; + + long res = count(n, k - 1); + if (k >= n) { + res -= count(n - 1, k - n); + } + res = (res + count(n - 1, k)) % MOD; + + dp[n][k] = (int) (res + MOD) % MOD; + return dp[n][k]; + } +} +``` + +```cpp +class Solution { +private: + static const int MOD = 1e9 + 7; + vector> dp; + + int count(int n, int k) { + if (k == 0) return 1; + if (n == 1) return 0; + if (n * (n - 1) / 2 < k) return 0; + if (n * (n - 1) / 2 == k) return 1; + if (dp[n][k] != -1) return dp[n][k]; + + long long res = count(n, k - 1); + if (k >= n) { + res -= count(n - 1, k - n); + } + res = (res + count(n - 1, k) + MOD) % MOD; + + dp[n][k] = int(res); + return dp[n][k]; + } + +public: + int kInversePairs(int n, int k) { + dp.assign(n + 1, vector(k + 1, -1)); + return count(n, k); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number} k + * @return {number} + */ + kInversePairs(n, k) { + const MOD = 1e9 + 7; + const dp = Array.from({ length: n + 1 }, () => Array(k + 1).fill(-1)); + + const count = (n, k) => { + if (k === 0) return 1; + if (n === 1) return 0; + if ((n * (n - 1)) / 2 < k) return 0; + if ((n * (n - 1)) / 2 === k) return 1; + if (dp[n][k] !== -1) return dp[n][k]; + + let res = count(n, k - 1); + if (k >= n) { + res -= count(n - 1, k - n); + } + res = (res + count(n - 1, k) + MOD) % MOD; + + dp[n][k] = res; + return res; + }; + + return count(n, k); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * k)$ +* Space complexity: $O(n * k)$ + +> Where $n$ is the size of the permutation and $k$ is the number of inverse pairs in the permutation. + +--- + +## 3. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def kInversePairs(self, n: int, k: int) -> int: + MOD = 10**9 + 7 + dp = [[0] * (k + 1) for _ in range(n + 1)] + dp[0][0] = 1 + + for N in range(1, n + 1): + for K in range(k + 1): + for pairs in range(N): + if K - pairs >= 0: + dp[N][K] = (dp[N][K] + dp[N - 1][K - pairs]) % MOD + + return dp[n][k] +``` + +```java +public class Solution { + public int kInversePairs(int n, int k) { + final int MOD = 1000000007; + int[][] dp = new int[n + 1][k + 1]; + dp[0][0] = 1; + + for (int N = 1; N <= n; N++) { + for (int K = 0; K <= k; K++) { + for (int pairs = 0; pairs < N; pairs++) { + if (K - pairs >= 0) { + dp[N][K] = (dp[N][K] + dp[N - 1][K - pairs]) % MOD; + } + } + } + } + + return dp[n][k]; + } +} +``` + +```cpp +class Solution { +public: + int kInversePairs(int n, int k) { + const int MOD = 1e9 + 7; + vector> dp(n + 1, vector(k + 1, 0)); + dp[0][0] = 1; + + for (int N = 1; N <= n; N++) { + for (int K = 0; K <= k; K++) { + for (int pairs = 0; pairs < N; pairs++) { + if (K - pairs >= 0) { + dp[N][K] = (dp[N][K] + dp[N - 1][K - pairs]) % MOD; + } + } + } + } + + return dp[n][k]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number} k + * @return {number} + */ + kInversePairs(n, k) { + const MOD = 1e9 + 7; + const dp = Array.from({ length: n + 1 }, () => Array(k + 1).fill(0)); + dp[0][0] = 1; + + for (let N = 1; N <= n; N++) { + for (let K = 0; K <= k; K++) { + for (let pairs = 0; pairs < N; pairs++) { + if (K - pairs >= 0) { + dp[N][K] = (dp[N][K] + dp[N - 1][K - pairs]) % MOD; + } + } + } + } + + return dp[n][k]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2 * k)$ +* Space complexity: $O(n * k)$ + +> Where $n$ is the size of the permutation and $k$ is the number of inverse pairs in the permutation. + +--- + +## 4. Dynamic Programming (Bottom-Up Optimized) + +::tabs-start + +```python +class Solution: + def kInversePairs(self, n: int, k: int) -> int: + MOD = 10**9 + 7 + dp = [[0] * (k + 1) for _ in range(n + 1)] + dp[0][0] = 1 + + for N in range(1, n + 1): + for K in range(k + 1): + dp[N][K] = dp[N - 1][K] + if K > 0: + dp[N][K] = (dp[N][K] + dp[N][K - 1]) % MOD + if K >= N: + dp[N][K] = (dp[N][K] - dp[N - 1][K - N] + MOD) % MOD + + return dp[n][k] +``` + +```java +public class Solution { + public int kInversePairs(int n, int k) { + final int MOD = 1000000007; + int[][] dp = new int[n + 1][k + 1]; + dp[0][0] = 1; + + for (int N = 1; N <= n; N++) { + for (int K = 0; K <= k; K++) { + dp[N][K] = dp[N - 1][K]; + if (K > 0) { + dp[N][K] = (dp[N][K] + dp[N][K - 1]) % MOD; + } + if (K >= N) { + dp[N][K] = (dp[N][K] - dp[N - 1][K - N] + MOD) % MOD; + } + } + } + + return dp[n][k]; + } +} +``` + +```cpp +class Solution { +public: + int kInversePairs(int n, int k) { + const int MOD = 1e9 + 7; + vector> dp(n + 1, vector(k + 1, 0)); + dp[0][0] = 1; + + for (int N = 1; N <= n; N++) { + for (int K = 0; K <= k; K++) { + dp[N][K] = dp[N - 1][K]; + if (K > 0) { + dp[N][K] = (dp[N][K] + dp[N][K - 1]) % MOD; + } + if (K >= N) { + dp[N][K] = (dp[N][K] - dp[N - 1][K - N] + MOD) % MOD; + } + } + } + + return dp[n][k]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number} k + * @return {number} + */ + kInversePairs(n, k) { + const MOD = 1e9 + 7; + const dp = Array.from({ length: n + 1 }, () => Array(k + 1).fill(0)); + dp[0][0] = 1; + + for (let N = 1; N <= n; N++) { + for (let K = 0; K <= k; K++) { + dp[N][K] = dp[N - 1][K]; + if (K > 0) { + dp[N][K] = (dp[N][K] + dp[N][K - 1]) % MOD; + } + if (K >= N) { + dp[N][K] = (dp[N][K] - dp[N - 1][K - N] + MOD) % MOD; + } + } + } + + return dp[n][k]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * k)$ +* Space complexity: $O(n * k)$ + +> Where $n$ is the size of the permutation and $k$ is the number of inverse pairs in the permutation. + +--- + +## 5. Dynamic Programming (Space Optimized) + +::tabs-start + +```python +class Solution: + def kInversePairs(self, n: int, k: int) -> int: + MOD = 10**9 + 7 + prev = [0] * (k + 1) + prev[0] = 1 + + for N in range(1, n + 1): + cur = [0] * (k + 1) + total = 0 + for K in range(0, k + 1): + total = (total + prev[K]) % MOD + if K >= N: + total = (total - prev[K - N] + MOD) % MOD + cur[K] = total + prev = cur + + return prev[k] +``` + +```java +public class Solution { + public int kInversePairs(int n, int k) { + final int MOD = 1000000007; + int[] prev = new int[k + 1]; + prev[0] = 1; + + for (int N = 1; N <= n; N++) { + int[] cur = new int[k + 1]; + int total = 0; + for (int K = 0; K <= k; K++) { + total = (total + prev[K]) % MOD; + if (K >= N) { + total = (total - prev[K - N] + MOD) % MOD; + } + cur[K] = total; + } + prev = cur; + } + + return prev[k]; + } +} +``` + +```cpp +class Solution { +public: + int kInversePairs(int n, int k) { + const int MOD = 1e9 + 7; + vector prev(k + 1, 0); + prev[0] = 1; + + for (int N = 1; N <= n; N++) { + vector cur(k + 1, 0); + int total = 0; + for (int K = 0; K <= k; K++) { + total = (total + prev[K]) % MOD; + if (K >= N) { + total = (total - prev[K - N] + MOD) % MOD; + } + cur[K] = total; + } + prev = cur; + } + + return prev[k]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number} k + * @return {number} + */ + kInversePairs(n, k) { + const MOD = 1e9 + 7; + let prev = new Array(k + 1).fill(0); + prev[0] = 1; + + for (let N = 1; N <= n; N++) { + const cur = new Array(k + 1).fill(0); + let total = 0; + for (let K = 0; K <= k; K++) { + total = (total + prev[K]) % MOD; + if (K >= N) { + total = (total - prev[K - N] + MOD) % MOD; + } + cur[K] = total; + } + prev = cur; + } + + return prev[k]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * k)$ +* Space complexity: $O(k)$ + +> Where $n$ is the size of the permutation and $k$ is the number of inverse pairs in the permutation. \ No newline at end of file diff --git a/articles/minimum-difficulty-of-a-job-schedule.md b/articles/minimum-difficulty-of-a-job-schedule.md new file mode 100644 index 000000000..5738623eb --- /dev/null +++ b/articles/minimum-difficulty-of-a-job-schedule.md @@ -0,0 +1,682 @@ +## 1. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def minDifficulty(self, jobDifficulty: List[int], d: int) -> int: + if len(jobDifficulty) < d: + return -1 + + n = len(jobDifficulty) + dp = {} + + def dfs(i, d, cur_max): + if i == n: + return 0 if d == 0 else float("inf") + if d == 0: + return float("inf") + if (i, d, cur_max) in dp: + return dp[(i, d, cur_max)] + + cur_max = max(cur_max, jobDifficulty[i]) + res = min( + dfs(i + 1, d, cur_max), + cur_max + dfs(i + 1, d - 1, -1) + ) + dp[(i, d, cur_max)] = res + return res + + return dfs(0, d, -1) +``` + +```java +public class Solution { + private int[][][] dp; + + public int minDifficulty(int[] jobDifficulty, int d) { + int n = jobDifficulty.length; + if (n < d) return -1; + int m = 0; + for (int i = 0; i < n; i++) { + m = Math.max(m, jobDifficulty[i]); + } + + dp = new int[n][d + 1][m + 5]; + for (int[][] layer : dp) { + for (int[] row : layer) { + Arrays.fill(row, -1); + } + } + + return dfs(0, d, -1, jobDifficulty); + } + + private int dfs(int i, int d, int curMax, int[] jobDifficulty) { + if (i == jobDifficulty.length) return d == 0 ? 0 : Integer.MAX_VALUE / 2; + if (d == 0) return Integer.MAX_VALUE / 2; + if (dp[i][d][curMax + 1] != -1) return dp[i][d][curMax + 1]; + + int maxSoFar = Math.max(curMax, jobDifficulty[i]); + int res = Math.min( + dfs(i + 1, d, maxSoFar, jobDifficulty), + maxSoFar + dfs(i + 1, d - 1, -1, jobDifficulty) + ); + + dp[i][d][curMax + 1] = res; + return res; + } +} +``` + +```cpp +class Solution { + vector>> dp; + + int dfs(int i, int d, int curMax, const vector& jobDifficulty) { + if (i == jobDifficulty.size()) return d == 0 ? 0 : INT_MAX / 2; + if (d == 0) return INT_MAX / 2; + if (dp[i][d][curMax + 1] != -1) return dp[i][d][curMax + 1]; + + int maxSoFar = max(curMax, jobDifficulty[i]); + int res = min( + dfs(i + 1, d, maxSoFar, jobDifficulty), + maxSoFar + dfs(i + 1, d - 1, -1, jobDifficulty) + ); + + dp[i][d][curMax + 1] = res; + return res; + } + +public: + int minDifficulty(vector& jobDifficulty, int d) { + int n = jobDifficulty.size(); + if (n < d) return -1; + + int m = *max_element(jobDifficulty.begin(), jobDifficulty.end()); + dp = vector>>(n, vector>(d + 1, vector(m + 5, -1))); + return dfs(0, d, -1, jobDifficulty); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} jobDifficulty + * @param {number} d + * @return {number} + */ + minDifficulty(jobDifficulty, d) { + const n = jobDifficulty.length; + if (n < d) return -1; + let m = 0; + for (let it of jobDifficulty) { + m = Math.max(m, it); + } + + const dp = Array.from({ length: n }, () => + Array.from({ length: d + 1 }, () => Array(m + 5).fill(-1)) + ); + + const dfs = (i, d, curMax) => { + if (i === n) return d === 0 ? 0 : Infinity; + if (d === 0) return Infinity; + if (dp[i][d][curMax + 1] !== -1) return dp[i][d][curMax + 1]; + + const maxSoFar = Math.max(curMax, jobDifficulty[i]); + const res = Math.min( + dfs(i + 1, d, maxSoFar), + maxSoFar + dfs(i + 1, d - 1, -1) + ); + + dp[i][d][curMax + 1] = res; + return res; + }; + + return dfs(0, d, -1); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * d * m)$ +* Space complexity: $O(n * d * m)$ + +> Where $n$ is the number of jobs, $d$ is the number of days, and $m$ is the maximum difficulty value among all the job difficulties. + +--- + +## 2. Dynamic Programming (Top-Down Optimized) + +::tabs-start + +```python +class Solution: + def minDifficulty(self, jobDifficulty: List[int], d: int) -> int: + if len(jobDifficulty) < d: + return -1 + + n = len(jobDifficulty) + dp = {} + + def dfs(i, d): + if (i, d) in dp: + return dp[(i, d)] + + maxi = jobDifficulty[i] + if d == 1: + idx = i + while i < n: + maxi = max(maxi, jobDifficulty[i]) + i += 1 + dp[(idx, d)] = maxi + return maxi + + res = float("inf") + for j in range(i + 1, n): + res = min(res, maxi + dfs(j, d - 1)) + maxi = max(maxi, jobDifficulty[j]) + dp[(i, d)] = res + return res + + return dfs(0, d) +``` + +```java +public class Solution { + private int[][] dp; + + public int minDifficulty(int[] jobDifficulty, int d) { + int n = jobDifficulty.length; + if (n < d) return -1; + + dp = new int[n][d + 1]; + for (int[] row : dp) Arrays.fill(row, -1); + + return dfs(0, d, jobDifficulty); + } + + private int dfs(int i, int d, int[] jobDifficulty) { + if (dp[i][d] != -1) return dp[i][d]; + + int n = jobDifficulty.length; + int maxi = jobDifficulty[i]; + if (d == 1) { + for (int j = i; j < n; j++) { + maxi = Math.max(maxi, jobDifficulty[j]); + } + dp[i][d] = maxi; + return maxi; + } + + int res = Integer.MAX_VALUE / 2; + for (int j = i + 1; j < n; j++) { + res = Math.min(res, maxi + dfs(j, d - 1, jobDifficulty)); + maxi = Math.max(maxi, jobDifficulty[j]); + } + dp[i][d] = res; + return res; + } +} +``` + +```cpp +class Solution { + vector> dp; + +public: + int minDifficulty(vector& jobDifficulty, int d) { + int n = jobDifficulty.size(); + if (n < d) return -1; + + dp.assign(n, vector(d + 1, -1)); + return dfs(0, d, jobDifficulty); + } + +private: + int dfs(int i, int d, vector& jobDifficulty) { + if (dp[i][d] != -1) return dp[i][d]; + + int n = jobDifficulty.size(); + int maxi = jobDifficulty[i]; + if (d == 1) { + for (int j = i; j < n; j++) { + maxi = max(maxi, jobDifficulty[j]); + } + dp[i][d] = maxi; + return maxi; + } + + int res = INT_MAX / 2; + for (int j = i + 1; j < n; j++) { + res = min(res, maxi + dfs(j, d - 1, jobDifficulty)); + maxi = max(maxi, jobDifficulty[j]); + } + dp[i][d] = res; + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} jobDifficulty + * @param {number} d + * @return {number} + */ + minDifficulty(jobDifficulty, d) { + const n = jobDifficulty.length; + if (n < d) return -1; + + const dp = Array.from({ length: n }, () => Array(d + 1).fill(-1)); + + const dfs = (i, d) => { + if (dp[i][d] !== -1) return dp[i][d]; + + let maxi = jobDifficulty[i]; + if (d === 1) { + for (let j = i; j < n; j++) { + maxi = Math.max(maxi, jobDifficulty[j]); + } + dp[i][d] = maxi; + return maxi; + } + + let res = Infinity; + for (let j = i + 1; j < n; j++) { + res = Math.min(res, maxi + dfs(j, d - 1)); + maxi = Math.max(maxi, jobDifficulty[j]); + } + dp[i][d] = res; + return res; + }; + + return dfs(0, d); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2 * d)$ +* Space complexity: $O(n * d)$ + +> Where $n$ is the number of jobs and $d$ is the number of days. + +--- + +## 3. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def minDifficulty(self, jobDifficulty: List[int], d: int) -> int: + if len(jobDifficulty) < d: + return -1 + + n = len(jobDifficulty) + dp = [[float("inf")] * (d + 1) for _ in range(n + 1)] + dp[n][0] = 0 + + for day in range(1, d + 1): + for i in range(n - 1, -1, -1): + maxi = 0 + for j in range(i, n - day + 1): + maxi = max(maxi, jobDifficulty[j]) + dp[i][day] = min(dp[i][day], maxi + dp[j + 1][day - 1]) + + return dp[0][d] +``` + +```java +public class Solution { + public int minDifficulty(int[] jobDifficulty, int d) { + int n = jobDifficulty.length; + if (n < d) return -1; + + int[][] dp = new int[n + 1][d + 1]; + for (int[] row : dp) Arrays.fill(row, Integer.MAX_VALUE / 2); + dp[n][0] = 0; + + for (int day = 1; day <= d; day++) { + for (int i = n - 1; i >= 0; i--) { + int maxi = 0; + for (int j = i; j <= n - day; j++) { + maxi = Math.max(maxi, jobDifficulty[j]); + dp[i][day] = Math.min(dp[i][day], maxi + dp[j + 1][day - 1]); + } + } + } + + return dp[0][d]; + } +} +``` + +```cpp +class Solution { +public: + int minDifficulty(vector& jobDifficulty, int d) { + int n = jobDifficulty.size(); + if (n < d) return -1; + + vector> dp(n + 1, vector(d + 1, INT_MAX / 2)); + dp[n][0] = 0; + + for (int day = 1; day <= d; day++) { + for (int i = n - 1; i >= 0; i--) { + int maxi = 0; + for (int j = i; j <= n - day; j++) { + maxi = max(maxi, jobDifficulty[j]); + dp[i][day] = min(dp[i][day], maxi + dp[j + 1][day - 1]); + } + } + } + + return dp[0][d]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} jobDifficulty + * @param {number} d + * @return {number} + */ + minDifficulty(jobDifficulty, d) { + const n = jobDifficulty.length; + if (n < d) return -1; + + const dp = Array.from({ length: n + 1 }, () => Array(d + 1).fill(Infinity)); + dp[n][0] = 0; + + for (let day = 1; day <= d; day++) { + for (let i = n - 1; i >= 0; i--) { + let maxi = 0; + for (let j = i; j <= n - day; j++) { + maxi = Math.max(maxi, jobDifficulty[j]); + dp[i][day] = Math.min(dp[i][day], maxi + dp[j + 1][day - 1]); + } + } + } + + return dp[0][d]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2 * d)$ +* Space complexity: $O(n * d)$ + +> Where $n$ is the number of jobs and $d$ is the number of days. + +--- + +## 4. Dynamic Programming (Space Optimized) + +::tabs-start + +```python +class Solution: + def minDifficulty(self, jobDifficulty: List[int], d: int) -> int: + if len(jobDifficulty) < d: + return -1 + + n = len(jobDifficulty) + dp = [float("inf")] * (n + 1) + dp[n] = 0 + + for day in range(1, d + 1): + for i in range(n - day + 1): + maxi = 0 + dp[i] = float("inf") + for j in range(i, n - day + 1): + maxi = max(maxi, jobDifficulty[j]) + dp[i] = min(dp[i], maxi + dp[j + 1]) + + return dp[0] +``` + +```java +public class Solution { + public int minDifficulty(int[] jobDifficulty, int d) { + int n = jobDifficulty.length; + if (n < d) return -1; + + int INF = Integer.MAX_VALUE / 2; + int[] dp = new int[n + 1]; + Arrays.fill(dp, INF); + dp[n] = 0; + + for (int day = 1; day <= d; day++) { + for (int i = 0; i <= n - day; i++) { + int maxi = 0; + dp[i] = INF; + for (int j = i; j <= n - day; j++) { + maxi = Math.max(maxi, jobDifficulty[j]); + dp[i] = Math.min(dp[i], maxi + dp[j + 1]); + } + } + } + + return dp[0]; + } +} +``` + +```cpp +class Solution { +public: + int minDifficulty(vector& jobDifficulty, int d) { + int n = jobDifficulty.size(); + if (n < d) return -1; + + int INF = INT_MAX / 2; + vector dp(n + 1, INF); + dp[n] = 0; + + for (int day = 1; day <= d; day++) { + for (int i = 0; i <= n - day; i++) { + int maxi = 0; + dp[i] = INF; + for (int j = i; j <= n - day; j++) { + maxi = max(maxi, jobDifficulty[j]); + dp[i] = min(dp[i], maxi + dp[j + 1]); + } + } + } + + return dp[0]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} jobDifficulty + * @param {number} d + * @return {number} + */ + minDifficulty(jobDifficulty, d) { + const n = jobDifficulty.length; + if (n < d) return -1; + + let dp = new Array(n + 1).fill(Infinity); + dp[n] = 0; + + for (let day = 1; day <= d; day++) { + for (let i = 0; i <= n - day; i++) { + let maxi = 0; + dp[i] = Infinity; + for (let j = i; j <= n - day; j++) { + maxi = Math.max(maxi, jobDifficulty[j]); + dp[i] = Math.min(dp[i], maxi + dp[j + 1]); + } + } + } + + return dp[0]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2 * d)$ +* Space complexity: $O(n)$ + +> Where $n$ is the number of jobs and $d$ is the number of days. + +--- + +## 5. Monotonic Decreasing Stack + +::tabs-start + +```python +class Solution: + def minDifficulty(self, jobDifficulty: List[int], d: int) -> int: + n = len(jobDifficulty) + if n < d: + return -1 + + dp = [float("inf")] * n + for day in range(1, d + 1): + next_dp = [float("inf")] * n + stack = [] + for i in range(day - 1, n): + next_dp[i] = dp[i - 1] + jobDifficulty[i] if i > 0 else jobDifficulty[i] + while stack and jobDifficulty[stack[-1]] <= jobDifficulty[i]: + j = stack.pop() + next_dp[i] = min(next_dp[i], next_dp[j] - jobDifficulty[j] + jobDifficulty[i]) + if stack: + next_dp[i] = min(next_dp[i], next_dp[stack[-1]]) + stack.append(i) + dp = next_dp + + return dp[-1] +``` + +```java +public class Solution { + public int minDifficulty(int[] jobDifficulty, int d) { + int n = jobDifficulty.length; + if (n < d) return -1; + + int[] dp = new int[n]; + Arrays.fill(dp, Integer.MAX_VALUE / 2); + + for (int day = 1; day <= d; day++) { + int[] nextDp = new int[n]; + Arrays.fill(nextDp, Integer.MAX_VALUE / 2); + Stack stack = new Stack<>(); + for (int i = day - 1; i < n; i++) { + nextDp[i] = (i > 0 ? dp[i - 1] : 0) + jobDifficulty[i]; + while (!stack.isEmpty() && jobDifficulty[stack.peek()] <= jobDifficulty[i]) { + int j = stack.pop(); + nextDp[i] = Math.min(nextDp[i], nextDp[j] - jobDifficulty[j] + jobDifficulty[i]); + } + if (!stack.isEmpty()) { + nextDp[i] = Math.min(nextDp[i], nextDp[stack.peek()]); + } + stack.add(i); + } + dp = nextDp; + } + + return dp[n - 1]; + } +} +``` + +```cpp +class Solution { +public: + int minDifficulty(vector& jobDifficulty, int d) { + int n = jobDifficulty.size(); + if (n < d) return -1; + + vector dp(n, INT_MAX / 2); + + for (int day = 1; day <= d; day++) { + vector nextDp(n, INT_MAX / 2); + stack st; + for (int i = day - 1; i < n; i++) { + nextDp[i] = (i > 0 ? dp[i - 1] : 0) + jobDifficulty[i]; + while (!st.empty() && jobDifficulty[st.top()] <= jobDifficulty[i]) { + int j = st.top(); st.pop(); + nextDp[i] = min(nextDp[i], nextDp[j] - jobDifficulty[j] + jobDifficulty[i]); + } + if (!st.empty()) { + nextDp[i] = min(nextDp[i], nextDp[st.top()]); + } + st.push(i); + } + dp = nextDp; + } + + return dp[n - 1]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} jobDifficulty + * @param {number} d + * @return {number} + */ + minDifficulty(jobDifficulty, d) { + const n = jobDifficulty.length; + if (n < d) return -1; + + let dp = Array(n).fill(Infinity); + + for (let day = 1; day <= d; day++) { + const nextDp = Array(n).fill(Infinity); + const stack = []; + for (let i = day - 1; i < n; i++) { + nextDp[i] = (i > 0 ? dp[i - 1] : 0) + jobDifficulty[i]; + while (stack.length > 0 && jobDifficulty[stack[stack.length - 1]] <= jobDifficulty[i]) { + const j = stack.pop(); + nextDp[i] = Math.min(nextDp[i], nextDp[j] - jobDifficulty[j] + jobDifficulty[i]); + } + if (stack.length > 0) { + nextDp[i] = Math.min(nextDp[i], nextDp[stack[stack.length - 1]]); + } + stack.push(i); + } + dp = nextDp; + } + + return dp[n - 1]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * d)$ +* Space complexity: $O(n)$ + +> Where $n$ is the number of jobs and $d$ is the number of days. \ No newline at end of file diff --git a/articles/minimum-falling-path-sum-ii.md b/articles/minimum-falling-path-sum-ii.md new file mode 100644 index 000000000..b43db319c --- /dev/null +++ b/articles/minimum-falling-path-sum-ii.md @@ -0,0 +1,854 @@ +## 1. Recursion + +::tabs-start + +```python +class Solution: + def minFallingPathSum(self, grid: List[List[int]]) -> int: + N = len(grid) + + def helper(r, c): + if r == N - 1: + return grid[r][c] + res = float("inf") + for next_col in range(N): + if c != next_col: + res = min(res, grid[r][c] + helper(r + 1, next_col)) + return res + + res = float("inf") + for c in range(N): + res = min(res, helper(0, c)) + return res +``` + +```java +public class Solution { + private int helper(int[][] grid, int r, int c) { + int N = grid.length; + if (r == N - 1) { + return grid[r][c]; + } + int res = Integer.MAX_VALUE; + for (int nextCol = 0; nextCol < N; nextCol++) { + if (c != nextCol) { + res = Math.min(res, grid[r][c] + helper(grid, r + 1, nextCol)); + } + } + return res; + } + + public int minFallingPathSum(int[][] grid) { + int N = grid.length; + int res = Integer.MAX_VALUE; + for (int c = 0; c < N; c++) { + res = Math.min(res, helper(grid, 0, c)); + } + return res; + } +} +``` + +```cpp +class Solution { + int helper(vector>& grid, int r, int c) { + int N = grid.size(); + if (r == N - 1) { + return grid[r][c]; + } + int res = INT_MAX; + for (int nextCol = 0; nextCol < N; nextCol++) { + if (c != nextCol) { + res = min(res, grid[r][c] + helper(grid, r + 1, nextCol)); + } + } + return res; + } + +public: + int minFallingPathSum(vector>& grid) { + int N = grid.size(); + int res = INT_MAX; + for (int c = 0; c < N; c++) { + res = min(res, helper(grid, 0, c)); + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} grid + * @return {number} + */ + minFallingPathSum(grid) { + const N = grid.length; + + const helper = (r, c) => { + if (r === N - 1) return grid[r][c]; + let res = Infinity; + for (let nextCol = 0; nextCol < N; nextCol++) { + if (c !== nextCol) { + res = Math.min(res, grid[r][c] + helper(r + 1, nextCol)); + } + } + return res; + }; + + let res = Infinity; + for (let c = 0; c < N; c++) { + res = Math.min(res, helper(0, c)); + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ n)$ +* Space complexity: $O(n)$ for recursion stack. + +--- + +## 2. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def minFallingPathSum(self, grid: List[List[int]]) -> int: + N = len(grid) + cache = {} + + def helper(r, c): + if r == N - 1: + return grid[r][c] + if (r, c) in cache: + return cache[(r, c)] + + res = float("inf") + for next_col in range(N): + if c != next_col: + res = min(res, grid[r][c] + helper(r + 1, next_col)) + cache[(r, c)] = res + return res + + res = float("inf") + for c in range(N): + res = min(res, helper(0, c)) + return res +``` + +```java +public class Solution { + private int[][] memo; + + private int helper(int[][] grid, int r, int c) { + int N = grid.length; + if (r == N - 1) { + return grid[r][c]; + } + if (memo[r][c] != Integer.MIN_VALUE) { + return memo[r][c]; + } + + int res = Integer.MAX_VALUE; + for (int nextCol = 0; nextCol < N; nextCol++) { + if (c != nextCol) { + res = Math.min(res, grid[r][c] + helper(grid, r + 1, nextCol)); + } + } + memo[r][c] = res; + return res; + } + + public int minFallingPathSum(int[][] grid) { + int N = grid.length; + memo = new int[N][N]; + for (int[] row : memo) { + Arrays.fill(row, Integer.MIN_VALUE); + } + int res = Integer.MAX_VALUE; + for (int c = 0; c < N; c++) { + res = Math.min(res, helper(grid, 0, c)); + } + return res; + } +} +``` + +```cpp +class Solution { + vector> memo; + + int helper(vector>& grid, int r, int c) { + int N = grid.size(); + if (r == N - 1) { + return grid[r][c]; + } + if (memo[r][c] != INT_MIN) { + return memo[r][c]; + } + int res = INT_MAX; + for (int nextCol = 0; nextCol < N; nextCol++) { + if (c != nextCol) { + res = min(res, grid[r][c] + helper(grid, r + 1, nextCol)); + } + } + memo[r][c] = res; + return res; + } + +public: + int minFallingPathSum(vector>& grid) { + int N = grid.size(); + memo.assign(N, vector(N, INT_MIN)); + int res = INT_MAX; + for (int c = 0; c < N; c++) { + res = min(res, helper(grid, 0, c)); + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} grid + * @return {number} + */ + minFallingPathSum(grid) { + const N = grid.length; + const memo = Array.from({ length: N }, () => Array(N).fill(-Infinity)); + + const helper = (r, c) => { + if (r === N - 1) return grid[r][c]; + if (memo[r][c] !== -Infinity) return memo[r][c]; + let res = Infinity; + for (let nextCol = 0; nextCol < N; nextCol++) { + if (c !== nextCol) { + res = Math.min(res, grid[r][c] + helper(r + 1, nextCol)); + } + } + memo[r][c] = res; + return res; + }; + + let res = Infinity; + for (let c = 0; c < N; c++) { + res = Math.min(res, helper(0, c)); + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 3)$ +* Space complexity: $O(n ^ 2)$ + +--- + +## 3. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def minFallingPathSum(self, grid: List[List[int]]) -> int: + N = len(grid) + dp = [[float("inf")] * N for _ in range(N)] + + for c in range(N): + dp[N - 1][c] = grid[N - 1][c] + + for r in range(N - 2, -1, -1): + for c in range(N): + for next_col in range(N): + if c != next_col: + dp[r][c] = min(dp[r][c], grid[r][c] + dp[r + 1][next_col]) + + return min(dp[0]) +``` + +```java +public class Solution { + public int minFallingPathSum(int[][] grid) { + int N = grid.length; + int[][] dp = new int[N][N]; + + for (int c = 0; c < N; c++) { + dp[N - 1][c] = grid[N - 1][c]; + } + + for (int r = N - 2; r >= 0; r--) { + for (int c = 0; c < N; c++) { + dp[r][c] = Integer.MAX_VALUE; + for (int nextCol = 0; nextCol < N; nextCol++) { + if (c != nextCol) { + dp[r][c] = Math.min(dp[r][c], grid[r][c] + dp[r + 1][nextCol]); + } + } + } + } + + int res = Integer.MAX_VALUE; + for (int c = 0; c < N; c++) { + res = Math.min(res, dp[0][c]); + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int minFallingPathSum(vector>& grid) { + int N = grid.size(); + vector> dp(N, vector(N, INT_MAX)); + + for (int c = 0; c < N; c++) { + dp[N - 1][c] = grid[N - 1][c]; + } + + for (int r = N - 2; r >= 0; r--) { + for (int c = 0; c < N; c++) { + dp[r][c] = INT_MAX; + for (int nextCol = 0; nextCol < N; nextCol++) { + if (c != nextCol) { + dp[r][c] = min(dp[r][c], grid[r][c] + dp[r + 1][nextCol]); + } + } + } + } + + int res = INT_MAX; + for (int c = 0; c < N; c++) { + res = min(res, dp[0][c]); + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} grid + * @return {number} + */ + minFallingPathSum(grid) { + const N = grid.length; + const dp = Array.from({ length: N }, () => Array(N).fill(Infinity)); + + for (let c = 0; c < N; c++) { + dp[N - 1][c] = grid[N - 1][c]; + } + + for (let r = N - 2; r >= 0; r--) { + for (let c = 0; c < N; c++) { + for (let nextCol = 0; nextCol < N; nextCol++) { + if (c !== nextCol) { + dp[r][c] = Math.min(dp[r][c], grid[r][c] + dp[r + 1][nextCol]); + } + } + } + } + + return Math.min(...dp[0]); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 3)$ +* Space complexity: $O(n ^ 2)$ + +--- + +## 4. Dynamic Programming (Space Optimized) + +::tabs-start + +```python +class Solution: + def minFallingPathSum(self, grid: List[List[int]]) -> int: + N = len(grid) + dp = grid[0] + + for r in range(1, N): + next_dp = [float("inf")] * N + for curr_c in range(N): + for prev_c in range(N): + if prev_c != curr_c: + next_dp[curr_c] = min( + next_dp[curr_c], + grid[r][curr_c] + dp[prev_c] + ) + dp = next_dp + + return min(dp) +``` + +```java +public class Solution { + public int minFallingPathSum(int[][] grid) { + int N = grid.length; + int[] dp = grid[0]; + + for (int r = 1; r < N; r++) { + int[] nextDp = new int[N]; + Arrays.fill(nextDp, Integer.MAX_VALUE); + + for (int currC = 0; currC < N; currC++) { + for (int prevC = 0; prevC < N; prevC++) { + if (prevC != currC) { + nextDp[currC] = Math.min( + nextDp[currC], + grid[r][currC] + dp[prevC] + ); + } + } + } + dp = nextDp; + } + + int res = Integer.MAX_VALUE; + for (int i : dp) res = Math.min(res, i); + return res; + } +} +``` + +```cpp +class Solution { +public: + int minFallingPathSum(vector>& grid) { + int N = grid.size(); + vector dp = grid[0]; + + for (int r = 1; r < N; r++) { + vector nextDp(N, INT_MAX); + for (int currC = 0; currC < N; currC++) { + for (int prevC = 0; prevC < N; prevC++) { + if (prevC != currC) { + nextDp[currC] = min( + nextDp[currC], + grid[r][currC] + dp[prevC] + ); + } + } + } + dp = nextDp; + } + + return *min_element(dp.begin(), dp.end()); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} grid + * @return {number} + */ + minFallingPathSum(grid) { + const N = grid.length; + let dp = grid[0]; + + for (let r = 1; r < N; r++) { + const nextDp = Array(N).fill(Infinity); + for (let currC = 0; currC < N; currC++) { + for (let prevC = 0; prevC < N; prevC++) { + if (prevC !== currC) { + nextDp[currC] = Math.min( + nextDp[currC], + grid[r][currC] + dp[prevC] + ); + } + } + } + dp = nextDp; + } + + return Math.min(...dp); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 3)$ +* Space complexity: $O(n)$ + +--- + +## 5. Dynamic Programming (Time Optimized) + +::tabs-start + +```python +class Solution: + def minFallingPathSum(self, grid: List[List[int]]) -> int: + def get_min_two(row): + two_smallest = [] + for val, idx in row: + if len(two_smallest) < 2: + two_smallest.append((val, idx)) + elif two_smallest[1][0] > val: + two_smallest.pop() + two_smallest.append((val, idx)) + two_smallest.sort() + return two_smallest + + N = len(grid) + first_row = [(val, idx) for idx, val in enumerate(grid[0])] + dp = get_min_two(first_row) + + for r in range(1, N): + next_dp = [] + for curr_c in range(N): + curr_val = grid[r][curr_c] + min_val = float("inf") + for prev_val, prev_c in dp: + if curr_c != prev_c: + min_val = min(min_val, curr_val + prev_val) + next_dp.append((min_val, curr_c)) + dp = get_min_two(next_dp) + + return min(val for val, idx in dp) +``` + +```java +public class Solution { + public List getMinTwo(List row) { + List twoSmallest = new ArrayList<>(); + for (int[] entry : row) { + if (twoSmallest.size() < 2) { + twoSmallest.add(entry); + } else if (twoSmallest.get(1)[0] > entry[0]) { + twoSmallest.remove(1); + twoSmallest.add(entry); + } + twoSmallest.sort((a, b) -> a[0] - b[0]); + } + return twoSmallest; + } + + public int minFallingPathSum(int[][] grid) { + int N = grid.length; + + List firstRow = new ArrayList<>(); + for (int i = 0; i < grid[0].length; i++) { + firstRow.add(new int[]{grid[0][i], i}); + } + + List dp = getMinTwo(firstRow); + + for (int r = 1; r < N; r++) { + List nextDp = new ArrayList<>(); + for (int c = 0; c < grid[0].length; c++) { + int currVal = grid[r][c]; + int minVal = Integer.MAX_VALUE; + for (int[] prev : dp) { + if (prev[1] != c) { + minVal = Math.min(minVal, currVal + prev[0]); + } + } + nextDp.add(new int[]{minVal, c}); + } + dp = getMinTwo(nextDp); + } + + return dp.stream().mapToInt(a -> a[0]).min().getAsInt(); + } +} +``` + +```cpp +class Solution { +public: + int minFallingPathSum(vector>& grid) { + int N = grid.size(); + + auto getMinTwo = [](vector>& row) { + vector> twoSmallest; + for (auto& entry : row) { + if (twoSmallest.size() < 2) { + twoSmallest.push_back(entry); + } else if (twoSmallest[1].first > entry.first) { + twoSmallest.pop_back(); + twoSmallest.push_back(entry); + } + sort(twoSmallest.begin(), twoSmallest.end()); + } + return twoSmallest; + }; + + vector> firstRow; + for (int i = 0; i < grid[0].size(); i++) { + firstRow.push_back({grid[0][i], i}); + } + + vector> dp = getMinTwo(firstRow); + + for (int r = 1; r < N; r++) { + vector> nextDp; + for (int c = 0; c < grid[0].size(); c++) { + int currVal = grid[r][c]; + int minVal = INT_MAX; + for (auto& prev : dp) { + if (prev.second != c) { + minVal = min(minVal, currVal + prev.first); + } + } + nextDp.push_back({minVal, c}); + } + dp = getMinTwo(nextDp); + } + + int result = INT_MAX; + for (auto& entry : dp) { + result = min(result, entry.first); + } + return result; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} grid + * @return {number} + */ + minFallingPathSum(grid) { + const N = grid.length; + + const getMinTwo = (row) => { + const twoSmallest = []; + for (const [val, idx] of row) { + if (twoSmallest.length < 2) { + twoSmallest.push([val, idx]); + } else if (twoSmallest[1][0] > val) { + twoSmallest.pop(); + twoSmallest.push([val, idx]); + } + twoSmallest.sort((a, b) => a[0] - b[0]); + } + return twoSmallest; + }; + + const firstRow = grid[0].map((val, idx) => [val, idx]); + let dp = getMinTwo(firstRow); + + for (let r = 1; r < N; r++) { + const nextDp = []; + for (let c = 0; c < grid[0].length; c++) { + const currVal = grid[r][c]; + let minVal = Infinity; + for (const [prevVal, prevC] of dp) { + if (c !== prevC) { + minVal = Math.min(minVal, currVal + prevVal); + } + } + nextDp.push([minVal, c]); + } + dp = getMinTwo(nextDp); + } + + return Math.min(...dp.map(([val]) => val)); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n)$ + +--- + +## 6. Dynamic Programming (Optimal) + +::tabs-start + +```python +class Solution: + def minFallingPathSum(self, grid: List[List[int]]) -> int: + n = len(grid) + if n == 1: + return grid[0][0] + + dp_idx1 = dp_idx2 = -1 + dp_val1 = dp_val2 = 0 + + for i in range(n): + nextDp_idx1 = nextDp_idx2 = -1 + nextDp_val1 = nextDp_val2 = float("inf") + + for j in range(n): + cur = dp_val1 if j != dp_idx1 else dp_val2 + cur += grid[i][j] + + if nextDp_idx1 == -1 or cur < nextDp_val1: + nextDp_idx2, nextDp_val2 = nextDp_idx1, nextDp_val1 + nextDp_idx1, nextDp_val1 = j, cur + elif nextDp_idx2 == -1 or cur < nextDp_val2: + nextDp_idx2, nextDp_val2 = j, cur + + dp_idx1, dp_idx2, dp_val1, dp_val2 = nextDp_idx1, nextDp_idx2, nextDp_val1, nextDp_val2 + + return dp_val1 +``` + +```java +public class Solution { + public int minFallingPathSum(int[][] grid) { + int n = grid.length; + if (n == 1) { + return grid[0][0]; + } + + int dpIdx1 = -1, dpIdx2 = -1; + int dpVal1 = 0, dpVal2 = 0; + + for (int i = 0; i < n; i++) { + int nextDpIdx1 = -1, nextDpIdx2 = -1; + int nextDpVal1 = Integer.MAX_VALUE, nextDpVal2 = Integer.MAX_VALUE; + + for (int j = 0; j < n; j++) { + int cur = (j != dpIdx1) ? dpVal1 : dpVal2; + cur += grid[i][j]; + + if (nextDpIdx1 == -1 || cur < nextDpVal1) { + nextDpIdx2 = nextDpIdx1; + nextDpVal2 = nextDpVal1; + nextDpIdx1 = j; + nextDpVal1 = cur; + } else if (nextDpIdx2 == -1 || cur < nextDpVal2) { + nextDpIdx2 = j; + nextDpVal2 = cur; + } + } + + dpIdx1 = nextDpIdx1; + dpIdx2 = nextDpIdx2; + dpVal1 = nextDpVal1; + dpVal2 = nextDpVal2; + } + + return dpVal1; + } +} +``` + +```cpp +class Solution { +public: + int minFallingPathSum(vector>& grid) { + int n = grid.size(); + if (n == 1) { + return grid[0][0]; + } + + int dpIdx1 = -1, dpIdx2 = -1; + int dpVal1 = 0, dpVal2 = 0; + + for (int i = 1; i < n; i++) { + int nextDpIdx1 = -1, nextDpIdx2 = -1; + int nextDpVal1 = INT_MAX, nextDpVal2 = INT_MAX; + + for (int j = 0; j < n; j++) { + int cur = (j != dpIdx1) ? dpVal1 : dpVal2; + cur += grid[i][j]; + + if (nextDpIdx1 == -1 || cur < nextDpVal1) { + nextDpIdx2 = nextDpIdx1; + nextDpVal2 = nextDpVal1; + nextDpIdx1 = j; + nextDpVal1 = cur; + } else if (nextDpIdx2 == -1 || cur < nextDpVal2) { + nextDpIdx2 = j; + nextDpVal2 = cur; + } + } + + dpIdx1 = nextDpIdx1; + dpIdx2 = nextDpIdx2; + dpVal1 = nextDpVal1; + dpVal2 = nextDpVal2; + } + + return dpVal1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} grid + * @return {number} + */ + minFallingPathSum(grid) { + const n = grid.length; + if (n === 1) return grid[0][0]; + + let dpIdx1 = -1, dpIdx2 = -1; + let dpVal1 = 0, dpVal2 = 0; + + for (let i = 0; i < n; i++) { + let nextDpIdx1 = -1, nextDpIdx2 = -1; + let nextDpVal1 = Infinity, nextDpVal2 = Infinity; + + for (let j = 0; j < n; j++) { + let cur = (j !== dpIdx1) ? dpVal1 : dpVal2; + cur += grid[i][j]; + + if (nextDpIdx1 === -1 || cur < nextDpVal1) { + nextDpIdx2 = nextDpIdx1; + nextDpVal2 = nextDpVal1; + nextDpIdx1 = j; + nextDpVal1 = cur; + } else if (nextDpIdx2 === -1 || cur < nextDpVal2) { + nextDpIdx2 = j; + nextDpVal2 = cur; + } + } + + dpIdx1 = nextDpIdx1; + dpIdx2 = nextDpIdx2; + dpVal1 = nextDpVal1; + dpVal2 = nextDpVal2; + } + + return dpVal1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(1)$ extra space. \ No newline at end of file diff --git a/articles/rotate-array.md b/articles/rotate-array.md new file mode 100644 index 000000000..72dfcb037 --- /dev/null +++ b/articles/rotate-array.md @@ -0,0 +1,433 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def rotate(self, nums: List[int], k: int) -> None: + """ + Do not return anything, modify nums in-place instead. + """ + n = len(nums) + k %= n + while k: + tmp = nums[n - 1] + for i in range(n - 1, 0, -1): + nums[i] = nums[i - 1] + nums[0] = tmp + k -= 1 +``` + +```java +public class Solution { + public void rotate(int[] nums, int k) { + int n = nums.length; + k %= n; + while (k > 0) { + int tmp = nums[n - 1]; + for (int i = n - 1; i > 0; i--) { + nums[i] = nums[i - 1]; + } + nums[0] = tmp; + k--; + } + } +} +``` + +```cpp +class Solution { +public: + void rotate(vector& nums, int k) { + int n = nums.size(); + k %= n; + while (k > 0) { + int tmp = nums[n - 1]; + for (int i = n - 1; i > 0; i--) { + nums[i] = nums[i - 1]; + } + nums[0] = tmp; + k--; + } + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {void} Do not return anything, modify nums in-place instead. + */ + rotate(nums, k) { + const n = nums.length; + k %= n; + while (k > 0) { + const tmp = nums[n - 1]; + for (let i = n - 1; i > 0; i--) { + nums[i] = nums[i - 1]; + } + nums[0] = tmp; + k--; + } + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * k)$ +* Space complexity: $O(1)$ extra space. + +--- + +## 2. Extra Space + +::tabs-start + +```python +class Solution: + def rotate(self, nums: List[int], k: int) -> None: + """ + Do not return anything, modify nums in-place instead. + """ + n = len(nums) + tmp = [0] * n + for i in range(n): + tmp[(i + k) % n] = nums[i] + + nums[:] = tmp +``` + +```java +public class Solution { + public void rotate(int[] nums, int k) { + int n = nums.length; + int[] tmp = new int[n]; + for (int i = 0; i < n; i++) { + tmp[(i + k) % n] = nums[i]; + } + for (int i = 0; i < n; i++) { + nums[i] = tmp[i]; + } + } +} +``` + +```cpp +class Solution { +public: + void rotate(vector& nums, int k) { + int n = nums.size(); + vector tmp(n); + for (int i = 0; i < n; i++) { + tmp[(i + k) % n] = nums[i]; + } + for (int i = 0; i < n; i++) { + nums[i] = tmp[i]; + } + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {void} Do not return anything, modify nums in-place instead. + */ + rotate(nums, k) { + const n = nums.length; + const tmp = new Array(n); + for (let i = 0; i < n; i++) { + tmp[(i + k) % n] = nums[i]; + } + for (let i = 0; i < n; i++) { + nums[i] = tmp[i]; + } + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ extra space. + +--- + +## 3. Cyclic Traversal + +::tabs-start + +```python +class Solution: + def rotate(self, nums: List[int], k: int) -> None: + """ + Do not return anything, modify nums in-place instead. + """ + n = len(nums) + k %= n + count = start = 0 + + while count < n: + current = start + prev = nums[start] + while True: + next_idx = (current + k) % n + nums[next_idx], prev = prev, nums[next_idx] + current = next_idx + count += 1 + + if start == current: + break + start += 1 +``` + +```java +public class Solution { + public void rotate(int[] nums, int k) { + int n = nums.length; + k %= n; + int count = 0; + + for (int start = 0; count < n; start++) { + int current = start; + int prev = nums[start]; + do { + int nextIdx = (current + k) % n; + int temp = nums[nextIdx]; + nums[nextIdx] = prev; + prev = temp; + current = nextIdx; + count++; + } while (start != current); + } + } +} +``` + +```cpp +class Solution { +public: + void rotate(vector& nums, int k) { + int n = nums.size(); + k %= n; + int count = 0; + + for (int start = 0; count < n; start++) { + int current = start; + int prev = nums[start]; + do { + int nextIdx = (current + k) % n; + int temp = nums[nextIdx]; + nums[nextIdx] = prev; + prev = temp; + current = nextIdx; + count++; + } while (start != current); + } + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {void} Do not return anything, modify nums in-place instead. + */ + rotate(nums, k) { + const n = nums.length; + k %= n; + let count = 0; + + for (let start = 0; count < n; start++) { + let current = start; + let prev = nums[start]; + do { + const nextIdx = (current + k) % n; + const temp = nums[nextIdx]; + nums[nextIdx] = prev; + prev = temp; + current = nextIdx; + count++; + } while (start !== current); + } + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. + +--- + +## 4. Using Reverse + +::tabs-start + +```python +class Solution: + def rotate(self, nums: List[int], k: int) -> None: + """ + Do not return anything, modify nums in-place instead. + """ + n = len(nums) + k %= n + + def reverse(l: int, r: int) -> None: + while l < r: + nums[l], nums[r] = nums[r], nums[l] + l, r = l + 1, r - 1 + + reverse(0, n - 1) + reverse(0, k - 1) + reverse(k, n - 1) +``` + +```java +public class Solution { + public void rotate(int[] nums, int k) { + int n = nums.length; + k %= n; + + reverse(nums, 0, n - 1); + reverse(nums, 0, k - 1); + reverse(nums, k, n - 1); + } + + private void reverse(int[] nums, int l, int r) { + while (l < r) { + int temp = nums[l]; + nums[l] = nums[r]; + nums[r] = temp; + l++; + r--; + } + } +} +``` + +```cpp +class Solution { +public: + void rotate(vector& nums, int k) { + int n = nums.size(); + k %= n; + + reverse(nums, 0, n - 1); + reverse(nums, 0, k - 1); + reverse(nums, k, n - 1); + } + +private: + void reverse(vector& nums, int l, int r) { + while (l < r) { + swap(nums[l], nums[r]); + l++; + r--; + } + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {void} Do not return anything, modify nums in-place instead. + */ + rotate(nums, k) { + const n = nums.length; + k %= n; + + const reverse = (l, r) => { + while (l < r) { + [nums[l], nums[r]] = [nums[r], nums[l]]; + l++; + r--; + } + }; + + reverse(0, n - 1); + reverse(0, k - 1); + reverse(k, n - 1); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. + +--- + +## 5. One Liner + +::tabs-start + +```python +class Solution: + def rotate(self, nums: List[int], k: int) -> None: + """ + Do not return anything, modify nums in-place instead. + """ + nums[:] = nums[-k % len(nums):] + nums[:-k % len(nums)] +``` + +```java +public class Solution { + public void rotate(int[] nums, int k) { + int n = nums.length; + int[] rotated = Arrays.copyOfRange(nums, n - k % n, n); + System.arraycopy(nums, 0, nums, k % n, n - k % n); + System.arraycopy(rotated, 0, nums, 0, rotated.length); + } +} +``` + +```cpp + +class Solution { +public: + void rotate(vector& nums, int k) { + std::rotate(nums.begin(), nums.end() - (k % nums.size()), nums.end()); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {void} Do not return anything, modify nums in-place instead. + */ + rotate(nums, k) { + nums.splice(0, 0, ...nums.splice(nums.length - (k % nums.length))); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ or $O(n)$ depending on the language. \ No newline at end of file From e52182736035e2492681c1acd88087bf7deff8b8 Mon Sep 17 00:00:00 2001 From: Sri Hari <94112314+Srihari2222@users.noreply.github.com> Date: Tue, 31 Dec 2024 23:45:33 +0530 Subject: [PATCH 23/45] Sri Hari: Batch-4/Neetcode-250/Added-articles (#3781) * Batch-4/Neetcode-250/Added-articles * Batch-4/Neetcode-250/Added-articles * Batch-4/Neetcode-250/Added-articles * Batch-4/Neetcode-250/Added-articles * Batch-4/Neetcode-250/Added-articles --- articles/asteroid-collision.md | 239 +++++ ...capacity-to-ship-packages-within-d-days.md | 266 +++++ articles/count-ways-to-build-good-strings.md | 2 +- articles/decode-string.md | 446 ++++++++ articles/design-circular-queue.md | 990 ++++++++++++++++++ articles/find-in-mountain-array.md | 684 ++++++++++++ articles/implement-queue-using-stacks.md | 351 +++++++ articles/implement-stack-using-queues.md | 438 ++++++++ ...greatest-common-divisors-in-linked-list.md | 155 +++ articles/lemonade-change.md | 241 +++++ articles/lfu-cache.md | 715 +++++++++++++ articles/longest-turbulent-subarray.md | 638 +++++++++++ articles/matchsticks-to-square.md | 474 +++++++++ articles/maximum-frequency-stack.md | 525 ++++++++++ articles/maximum-sum-circular-subarray.md | 316 ++++++ articles/minimum-cost-for-tickets.md | 23 +- articles/n-queens-ii.md | 672 ++++++++++++ articles/n-queens.md | 10 +- articles/online-stock-span.md | 173 +++ articles/partition-to-k-equal-sum-subsets.md | 772 ++++++++++++++ articles/reverse-linked-list-ii.md | 707 +++++++++++++ articles/search-in-rotated-sorted-array-ii.md | 207 ++++ articles/simplify-path.md | 213 ++++ articles/split-array-largest-sum.md | 862 +++++++++++++++ articles/sqrtx.md | 378 +++++++ articles/transpose-matrix.md | 208 ++++ 26 files changed, 10686 insertions(+), 19 deletions(-) create mode 100644 articles/asteroid-collision.md create mode 100644 articles/capacity-to-ship-packages-within-d-days.md create mode 100644 articles/decode-string.md create mode 100644 articles/design-circular-queue.md create mode 100644 articles/find-in-mountain-array.md create mode 100644 articles/implement-queue-using-stacks.md create mode 100644 articles/implement-stack-using-queues.md create mode 100644 articles/insert-greatest-common-divisors-in-linked-list.md create mode 100644 articles/lemonade-change.md create mode 100644 articles/lfu-cache.md create mode 100644 articles/longest-turbulent-subarray.md create mode 100644 articles/matchsticks-to-square.md create mode 100644 articles/maximum-frequency-stack.md create mode 100644 articles/maximum-sum-circular-subarray.md create mode 100644 articles/n-queens-ii.md create mode 100644 articles/online-stock-span.md create mode 100644 articles/partition-to-k-equal-sum-subsets.md create mode 100644 articles/reverse-linked-list-ii.md create mode 100644 articles/search-in-rotated-sorted-array-ii.md create mode 100644 articles/simplify-path.md create mode 100644 articles/split-array-largest-sum.md create mode 100644 articles/sqrtx.md create mode 100644 articles/transpose-matrix.md diff --git a/articles/asteroid-collision.md b/articles/asteroid-collision.md new file mode 100644 index 000000000..e9634e9e8 --- /dev/null +++ b/articles/asteroid-collision.md @@ -0,0 +1,239 @@ +## 1. Stack + +::tabs-start + +```python +class Solution: + def asteroidCollision(self, asteroids: List[int]) -> List[int]: + stack = [] + for a in asteroids: + while stack and a < 0 and stack[-1] > 0: + diff = a + stack[-1] + if diff < 0: + stack.pop() + elif diff > 0: + a = 0 + else: + a = 0 + stack.pop() + if a: + stack.append(a) + return stack +``` + +```java +public class Solution { + public int[] asteroidCollision(int[] asteroids) { + Stack stack = new Stack<>(); + for (int a : asteroids) { + while (!stack.isEmpty() && a < 0 && stack.peek() > 0) { + int diff = a + stack.peek(); + if (diff < 0) { + stack.pop(); + } else if (diff > 0) { + a = 0; + } else { + a = 0; + stack.pop(); + } + } + if (a != 0) { + stack.add(a); + } + } + return stack.stream().mapToInt(i -> i).toArray(); + } +} +``` + +```cpp +class Solution { +public: + vector asteroidCollision(vector& asteroids) { + vector stack; + for (int& a : asteroids) { + while (!stack.empty() && a < 0 && stack.back() > 0) { + int diff = a + stack.back(); + if (diff < 0) { + stack.pop_back(); + } else if (diff > 0) { + a = 0; + } else { + a = 0; + stack.pop_back(); + } + } + if (a != 0) { + stack.push_back(a); + } + } + return stack; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} asteroids + * @return {number[]} + */ + asteroidCollision(asteroids) { + const stack = []; + for (let a of asteroids) { + while (stack.length && a < 0 && stack[stack.length - 1] > 0) { + const diff = a + stack[stack.length - 1]; + if (diff < 0) { + stack.pop(); + } else if (diff > 0) { + a = 0; + } else { + a = 0; + stack.pop(); + } + } + if (a !== 0) { + stack.push(a); + } + } + return stack; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Without Stack + +::tabs-start + +```python +class Solution: + def asteroidCollision(self, asteroids: List[int]) -> List[int]: + n = len(asteroids) + j = -1 + + for a in asteroids: + while j >= 0 and asteroids[j] > 0 and a < 0: + if asteroids[j] > abs(a): + a = 0 + break + elif asteroids[j] == abs(a): + j -= 1 + a = 0 + break + else: + j -= 1 + if a: + j += 1 + asteroids[j] = a + + return asteroids[:j + 1] +``` + +```java +public class Solution { + public int[] asteroidCollision(int[] asteroids) { + int n = asteroids.length; + int j = -1; + + for (int a : asteroids) { + while (j >= 0 && asteroids[j] > 0 && a < 0) { + if (asteroids[j] > Math.abs(a)) { + a = 0; + break; + } else if (asteroids[j] == Math.abs(a)) { + j--; + a = 0; + break; + } else { + j--; + } + } + if (a != 0) { + asteroids[++j] = a; + } + } + + return Arrays.copyOfRange(asteroids, 0, j + 1); + } +} +``` + +```cpp +class Solution { +public: + vector asteroidCollision(vector& asteroids) { + int n = asteroids.size(); + int j = -1; + + for (int& a : asteroids) { + while (j >= 0 && asteroids[j] > 0 && a < 0) { + if (asteroids[j] > abs(a)) { + a = 0; + break; + } else if (asteroids[j] == abs(a)) { + j--; + a = 0; + break; + } else { + j--; + } + } + if (a != 0) { + asteroids[++j] = a; + } + } + + asteroids.resize(j + 1); + return asteroids; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} asteroids + * @return {number[]} + */ + asteroidCollision(asteroids) { + let n = asteroids.length; + let j = -1; + + for (let a of asteroids) { + while (j >= 0 && asteroids[j] > 0 && a < 0) { + if (asteroids[j] > Math.abs(a)) { + a = 0; + break; + } else if (asteroids[j] === Math.abs(a)) { + j--; + a = 0; + break; + } else { + j--; + } + } + if (a !== 0) { + asteroids[++j] = a; + } + } + + return asteroids.slice(0, j + 1); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ for the output array. \ No newline at end of file diff --git a/articles/capacity-to-ship-packages-within-d-days.md b/articles/capacity-to-ship-packages-within-d-days.md new file mode 100644 index 000000000..9a5d4e777 --- /dev/null +++ b/articles/capacity-to-ship-packages-within-d-days.md @@ -0,0 +1,266 @@ +## 1. Linear Search + +::tabs-start + +```python +class Solution: + def shipWithinDays(self, weights: List[int], days: int) -> int: + res = max(weights) + while True: + ships = 1 + cap = res + for w in weights: + if cap - w < 0: + ships += 1 + cap = res + cap -= w + + if ships <= days: + return res + + res += 1 +``` + +```java +public class Solution { + public int shipWithinDays(int[] weights, int days) { + int res = 0; + for (int weight : weights) { + res = Math.max(res, weight); + } + while (true) { + int ships = 1; + int cap = res; + for (int weight : weights) { + if (cap - weight < 0) { + ships++; + cap = res; + } + cap -= weight; + } + if (ships <= days) { + return res; + } + res++; + } + } +} +``` + +```cpp +class Solution { +public: + int shipWithinDays(vector& weights, int days) { + int res = *max_element(weights.begin(), weights.end()); + while (true) { + int ships = 1, cap = res; + for (int w : weights) { + if (cap - w < 0) { + ships++; + cap = res; + } + cap -= w; + } + if (ships <= days) { + return res; + } + res++; + } + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} weights + * @param {number} days + * @return {number} + */ + shipWithinDays(weights, days) { + let res = Math.max(...weights); + while (true) { + let ships = 1, cap = res; + for (let w of weights) { + if (cap - w < 0) { + ships++; + cap = res; + } + cap -= w; + } + if (ships <= days) { + return res; + } + res++; + } + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(1)$ + +--- + +## 2. Binary Search + +::tabs-start + +```python +class Solution: + def shipWithinDays(self, weights: List[int], days: int) -> int: + l, r = max(weights), sum(weights) + res = r + + def canShip(cap): + ships, currCap = 1, cap + for w in weights: + if currCap - w < 0: + ships += 1 + if ships > days: + return False + currCap = cap + + currCap -= w + return True + + while l <= r: + cap = (l + r) // 2 + if canShip(cap): + res = min(res, cap) + r = cap - 1 + else: + l = cap + 1 + + return res +``` + +```java +public class Solution { + public int shipWithinDays(int[] weights, int days) { + int l = 0, r = 0; + for (int w : weights) { + l = Math.max(l, w); + r += w; + } + int res = r; + + while (l <= r) { + int cap = (l + r) / 2; + if (canShip(weights, days, cap)) { + res = Math.min(res, cap); + r = cap - 1; + } else { + l = cap + 1; + } + } + return res; + } + + private boolean canShip(int[] weights, int days, int cap) { + int ships = 1, currCap = cap; + for (int w : weights) { + if (currCap - w < 0) { + ships++; + if (ships > days) { + return false; + } + currCap = cap; + } + currCap -= w; + } + return true; + } +} +``` + +```cpp +class Solution { +public: + int shipWithinDays(vector& weights, int days) { + int l = *max_element(weights.begin(), weights.end()); + int r = accumulate(weights.begin(), weights.end(), 0); + int res = r; + + while (l <= r) { + int cap = (l + r) / 2; + if (canShip(weights, days, cap)) { + res = min(res, cap); + r = cap - 1; + } else { + l = cap + 1; + } + } + return res; + } + +private: + bool canShip(const vector& weights, int days, int cap) { + int ships = 1, currCap = cap; + for (int w : weights) { + if (currCap - w < 0) { + ships++; + if (ships > days) { + return false; + } + currCap = cap; + } + currCap -= w; + } + return true; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} weights + * @param {number} days + * @return {number} + */ + shipWithinDays(weights, days) { + let l = Math.max(...weights); + let r = weights.reduce((a, b) => a + b, 0); + let res = r; + + const canShip = (cap) => { + let ships = 1, currCap = cap; + for (const w of weights) { + if (currCap - w < 0) { + ships++; + if (ships > days) { + return false; + } + currCap = cap; + } + currCap -= w; + } + return true; + }; + + while (l <= r) { + const cap = Math.floor((l + r) / 2); + if (canShip(cap)) { + res = Math.min(res, cap); + r = cap - 1; + } else { + l = cap + 1; + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/count-ways-to-build-good-strings.md b/articles/count-ways-to-build-good-strings.md index d1808c1e9..a18b90526 100644 --- a/articles/count-ways-to-build-good-strings.md +++ b/articles/count-ways-to-build-good-strings.md @@ -84,7 +84,7 @@ class Solution { ### Time & Space Complexity * Time complexity: $O(2 ^ n)$ -* Space complexity: $O(n)$ +* Space complexity: $O(n)$ for recursion stack. > Where $n$ is equal to the given $high$ value. diff --git a/articles/decode-string.md b/articles/decode-string.md new file mode 100644 index 000000000..c5363f71b --- /dev/null +++ b/articles/decode-string.md @@ -0,0 +1,446 @@ +## 1. Recursion + +::tabs-start + +```python +class Solution: + def decodeString(self, s: str) -> str: + self.i = 0 + + def helper(): + res = "" + k = 0 + + while self.i < len(s): + c = s[self.i] + + if c.isdigit(): + k = k * 10 + int(c) + elif c == "[": + self.i += 1 + res += k * helper() + k = 0 + elif c == "]": + return res + else: + res += c + + self.i += 1 + return res + + return helper() +``` + +```java +public class Solution { + private int i = 0; + + public String decodeString(String s) { + return helper(s); + } + + private String helper(String s) { + StringBuilder res = new StringBuilder(); + int k = 0; + + while (i < s.length()) { + char c = s.charAt(i); + + if (Character.isDigit(c)) { + k = k * 10 + (c - '0'); + } else if (c == '[') { + i++; + String subRes = helper(s); + while (k-- > 0) res.append(subRes); + k = 0; + } else if (c == ']') { + return res.toString(); + } else { + res.append(c); + } + + i++; + } + + return res.toString(); + } +} +``` + +```cpp +class Solution { +private: + string helper(int& i, string& s) { + string res; + int k = 0; + + while (i < s.size()) { + char c = s[i]; + + if (isdigit(c)) { + k = k * 10 + (c - '0'); + } else if (c == '[') { + i++; + string subRes = helper(i, s); + while (k-- > 0) res += subRes; + k = 0; + } else if (c == ']') { + return res; + } else { + res += c; + } + + i++; + } + + return res; + } + +public: + string decodeString(string s) { + int i = 0; + return helper(i, s); + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {string} + */ + decodeString(s) { + let i = 0; + + const helper = () => { + let res = ""; + let k = 0; + + while (i < s.length) { + const c = s[i]; + + if (!isNaN(c)) { + k = k * 10 + parseInt(c, 10); + } else if (c === "[") { + i++; + res += helper().repeat(k); + k = 0; + } else if (c === "]") { + return res; + } else { + res += c; + } + + i++; + } + + return res; + }; + + return helper(); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n + N)$ +* Space complexity: $O(n + N)$ + +> Where $n$ is the length of the input string and $N$ is the length of the output string. + +--- + +## 2. One Stack + +::tabs-start + +```python +class Solution: + def decodeString(self, s: str) -> str: + stack = [] + + for i in range(len(s)): + if s[i] != "]": + stack.append(s[i]) + else: + substr = "" + while stack[-1] != "[": + substr = stack.pop() + substr + stack.pop() + + k = "" + while stack and stack[-1].isdigit(): + k = stack.pop() + k + stack.append(int(k) * substr) + + return "".join(stack) +``` + +```java +public class Solution { + public String decodeString(String s) { + Stack stack = new Stack<>(); + + for (int i = 0; i < s.length(); i++) { + if (s.charAt(i) != ']') { + stack.push(String.valueOf(s.charAt(i))); + } else { + StringBuilder substr = new StringBuilder(); + while (!stack.peek().equals("[")) { + substr.insert(0, stack.pop()); + } + stack.pop(); + + StringBuilder k = new StringBuilder(); + while (!stack.isEmpty() && Character.isDigit(stack.peek().charAt(0))) { + k.insert(0, stack.pop()); + } + int count = Integer.parseInt(k.toString()); + String repeatedStr = substr.toString().repeat(count); + stack.push(repeatedStr); + } + } + + StringBuilder res = new StringBuilder(); + while (!stack.isEmpty()) { + res.insert(0, stack.pop()); + } + return res.toString(); + } +} +``` + +```cpp +class Solution { +public: + string decodeString(string s) { + vector stack; + + for (char& c : s) { + if (c != ']') { + stack.push_back(string(1, c)); + } else { + string substr = ""; + while (stack.back() != "[") { + substr = stack.back() + substr; + stack.pop_back(); + } + stack.pop_back(); + + string k = ""; + while (!stack.empty() && isdigit(stack.back()[0])) { + k = stack.back() + k; + stack.pop_back(); + } + int repeatCount = stoi(k); + + string repeated = ""; + for (int i = 0; i < repeatCount; ++i) { + repeated += substr; + } + stack.push_back(repeated); + } + } + + string res = ""; + for (const string& part : stack) { + res += part; + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {string} + */ + decodeString(s) { + const stack = []; + + for (let i = 0; i < s.length; i++) { + const char = s[i]; + + if (char !== "]") { + stack.push(char); + } else { + let substr = ""; + while (stack[stack.length - 1] !== "[") { + substr = stack.pop() + substr; + } + stack.pop(); + + let k = ""; + while (stack.length > 0 && !isNaN(stack[stack.length - 1])) { + k = stack.pop() + k; + } + stack.push(substr.repeat(parseInt(k, 10))); + } + } + + return stack.join(""); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n + N ^ 2)$ +* Space complexity: $O(n + N)$ + +> Where $n$ is the length of the input string and $N$ is the length of the output string. + +--- + +## 3. Two Stacks + +::tabs-start + +```python +class Solution: + def decodeString(self, s: str) -> str: + string_stack = [] + count_stack = [] + cur = "" + k = 0 + + for c in s: + if c.isdigit(): + k = k * 10 + int(c) + elif c == "[": + string_stack.append(cur) + count_stack.append(k) + cur = "" + k = 0 + elif c == "]": + temp = cur + cur = string_stack.pop() + count = count_stack.pop() + cur += temp * count + else: + cur += c + + return cur +``` + +```java +public class Solution { + public String decodeString(String s) { + Stack stringStack = new Stack<>(); + Stack countStack = new Stack<>(); + StringBuilder cur = new StringBuilder(); + int k = 0; + + for (char c : s.toCharArray()) { + if (Character.isDigit(c)) { + k = k * 10 + (c - '0'); + } else if (c == '[') { + stringStack.push(cur.toString()); + countStack.push(k); + cur = new StringBuilder(); + k = 0; + } else if (c == ']') { + String temp = cur.toString(); + cur = new StringBuilder(stringStack.pop()); + int count = countStack.pop(); + for (int i = 0; i < count; i++) { + cur.append(temp); + } + } else { + cur.append(c); + } + } + + return cur.toString(); + } +} +``` + +```cpp +class Solution { +public: + string decodeString(string s) { + vector stringStack; + vector countStack; + string cur = ""; + int k = 0; + + for (char c : s) { + if (isdigit(c)) { + k = k * 10 + (c - '0'); + } else if (c == '[') { + stringStack.push_back(cur); + countStack.push_back(k); + cur = ""; + k = 0; + } else if (c == ']') { + string temp = cur; + cur = stringStack.back(); + stringStack.pop_back(); + int count = countStack.back(); + countStack.pop_back(); + for (int i = 0; i < count; i++) { + cur += temp; + } + } else { + cur += c; + } + } + + return cur; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {string} + */ + decodeString(s) { + const stringStack = []; + const countStack = []; + let cur = ""; + let k = 0; + + for (const c of s) { + if (!isNaN(c)) { + k = k * 10 + parseInt(c, 10); + } else if (c === "[") { + stringStack.push(cur); + countStack.push(k); + cur = ""; + k = 0; + } else if (c === "]") { + const temp = cur; + cur = stringStack.pop(); + const count = countStack.pop(); + cur += temp.repeat(count); + } else { + cur += c; + } + } + + return cur; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n + N)$ +* Space complexity: $O(n + N)$ + +> Where $n$ is the length of the input string and $N$ is the length of the output string. \ No newline at end of file diff --git a/articles/design-circular-queue.md b/articles/design-circular-queue.md new file mode 100644 index 000000000..033e2cfd6 --- /dev/null +++ b/articles/design-circular-queue.md @@ -0,0 +1,990 @@ +## 1. Brute Force + +::tabs-start + +```python +class MyCircularQueue: + + def __init__(self, k: int): + self.q = [] + self.k = k + + def enQueue(self, value: int) -> bool: + if len(self.q) == self.k: + return False + self.q.append(value) + return True + + def deQueue(self) -> bool: + if not self.q: + return False + self.q.pop(0) + return True + + def Front(self) -> int: + if self.q: + return self.q[0] + return -1 + + def Rear(self) -> int: + if self.q: + return self.q[-1] + return -1 + + def isEmpty(self) -> bool: + return len(self.q) == 0 + + def isFull(self) -> bool: + return len(self.q) == self.k +``` + +```java +public class MyCircularQueue { + private List queue; + private int capacity; + + public MyCircularQueue(int k) { + queue = new ArrayList<>(); + capacity = k; + } + + public boolean enQueue(int value) { + if (queue.size() == capacity) { + return false; + } + queue.add(value); + return true; + } + + public boolean deQueue() { + if (queue.isEmpty()) { + return false; + } + queue.remove(0); + return true; + } + + public int Front() { + return queue.isEmpty() ? -1 : queue.get(0); + } + + public int Rear() { + return queue.isEmpty() ? -1 : queue.get(queue.size() - 1); + } + + public boolean isEmpty() { + return queue.isEmpty(); + } + + public boolean isFull() { + return queue.size() == capacity; + } +} +``` + +```cpp +class MyCircularQueue { +private: + vector queue; + int capacity; + +public: + MyCircularQueue(int k) { + capacity = k; + } + + bool enQueue(int value) { + if (queue.size() == capacity) { + return false; + } + queue.push_back(value); + return true; + } + + bool deQueue() { + if (queue.empty()) { + return false; + } + queue.erase(queue.begin()); + return true; + } + + int Front() { + return queue.empty() ? -1 : queue.front(); + } + + int Rear() { + return queue.empty() ? -1 : queue.back(); + } + + bool isEmpty() { + return queue.empty(); + } + + bool isFull() { + return queue.size() == capacity; + } +}; +``` + +```javascript +class MyCircularQueue { + /** + * @param {number} k + */ + constructor(k) { + this.queue = []; + this.capacity = k; + } + + /** + * @param {number} value + * @return {boolean} + */ + enQueue(value) { + if (this.queue.length === this.capacity) { + return false; + } + this.queue.push(value); + return true; + } + + /** + * @return {boolean} + */ + deQueue() { + if (this.queue.length === 0) { + return false; + } + this.queue.shift(); + return true; + } + + /** + * @return {number} + */ + Front() { + return this.queue.length === 0 ? -1 : this.queue[0]; + } + + /** + * @return {number} + */ + Rear() { + return this.queue.length === 0 ? -1 : this.queue[this.queue.length - 1]; + } + + /** + * @return {boolean} + */ + isEmpty() { + return this.queue.length === 0; + } + + /** + * @return {boolean} + */ + isFull() { + return this.queue.length === this.capacity; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: + * $O(1)$ time for initialization. + * $O(1)$ time for each $enQueue()$, $Front()$, $Rear()$, $isEmpty()$ and $isFull()$ function calls. + * $O(n)$ time for each $deQueue()$ function call. +* Space complexity: $O(n)$ + +> Where $n$ is the size of the queue. + +--- + +## 2. Array + +::tabs-start + +```python +class MyCircularQueue: + + def __init__(self, k: int): + self.q = [0] * k + self.k = k + self.front = 0 + self.rear = -1 + self.size = 0 + + def enQueue(self, value: int) -> bool: + if self.isFull(): + return False + self.rear = (self.rear + 1) % self.k + self.q[self.rear] = value + self.size += 1 + return True + + def deQueue(self) -> bool: + if self.isEmpty(): + return False + self.front = (self.front + 1) % self.k + self.size -= 1 + return True + + def Front(self) -> int: + if self.isEmpty(): + return -1 + return self.q[self.front] + + def Rear(self) -> int: + if self.isEmpty(): + return -1 + return self.q[self.rear] + + def isEmpty(self) -> bool: + return self.size == 0 + + def isFull(self) -> bool: + return self.size == self.k +``` + +```java +public class MyCircularQueue { + private int[] queue; + private int size; + private int front; + private int rear; + + public MyCircularQueue(int k) { + queue = new int[k]; + size = 0; + front = 0; + rear = -1; + } + + public boolean enQueue(int value) { + if (isFull()) { + return false; + } + rear = (rear + 1) % queue.length; + queue[rear] = value; + size++; + return true; + } + + public boolean deQueue() { + if (isEmpty()) { + return false; + } + front = (front + 1) % queue.length; + size--; + return true; + } + + public int Front() { + return isEmpty() ? -1 : queue[front]; + } + + public int Rear() { + return isEmpty() ? -1 : queue[rear]; + } + + public boolean isEmpty() { + return size == 0; + } + + public boolean isFull() { + return size == queue.length; + } +} +``` + +```cpp +class MyCircularQueue { +private: + vector queue; + int size; + int front; + int rear; + int capacity; + +public: + MyCircularQueue(int k) { + queue = vector(k); + size = 0; + front = 0; + rear = -1; + capacity = k; + } + + bool enQueue(int value) { + if (isFull()) { + return false; + } + rear = (rear + 1) % capacity; + queue[rear] = value; + size++; + return true; + } + + bool deQueue() { + if (isEmpty()) { + return false; + } + front = (front + 1) % capacity; + size--; + return true; + } + + int Front() { + return isEmpty() ? -1 : queue[front]; + } + + int Rear() { + return isEmpty() ? -1 : queue[rear]; + } + + bool isEmpty() { + return size == 0; + } + + bool isFull() { + return size == capacity; + } +}; +``` + +```javascript +class MyCircularQueue { + /** + * @param {number} k + */ + constructor(k) { + this.queue = new Array(k); + this.size = 0; + this.front = 0; + this.rear = -1; + this.capacity = k; + } + + /** + * @param {number} value + * @return {boolean} + */ + enQueue(value) { + if (this.isFull()) { + return false; + } + this.rear = (this.rear + 1) % this.capacity; + this.queue[this.rear] = value; + this.size++; + return true; + } + + /** + * @return {boolean} + */ + deQueue() { + if (this.isEmpty()) { + return false; + } + this.front = (this.front + 1) % this.capacity; + this.size--; + return true; + } + + /** + * @return {number} + */ + Front() { + return this.isEmpty() ? -1 : this.queue[this.front]; + } + + /** + * @return {number} + */ + Rear() { + return this.isEmpty() ? -1 : this.queue[this.rear]; + } + + /** + * @return {boolean} + */ + isEmpty() { + return this.size === 0; + } + + /** + * @return {boolean} + */ + isFull() { + return this.size === this.capacity; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: + * $O(n)$ time for initialization. + * $O(1)$ time for each $enQueue()$, $deQueue()$, $Front()$, $Rear()$, $isEmpty()$ and $isFull()$ function calls. +* Space complexity: $O(n)$ + +> Where $n$ is the size of the queue. + +--- + +## 3. Doubly Linked List + +::tabs-start + +```python +class ListNode: + + def __init__(self, val, nxt, prev): + self.val, self.next, self.prev = val, nxt, prev + +class MyCircularQueue: + + def __init__(self, k: int): + self.space = k + self.left = ListNode(0, None, None) + self.right = ListNode(0, None, self.left) + self.left.next = self.right + + def enQueue(self, value: int) -> bool: + if self.isFull(): return False + cur = ListNode(value, self.right, self.right.prev) + self.right.prev.next = cur + self.right.prev = cur + self.space -= 1 + return True + + def deQueue(self) -> bool: + if self.isEmpty(): return False + self.left.next = self.left.next.next + self.left.next.prev = self.left + self.space += 1 + return True + + def Front(self) -> int: + if self.isEmpty(): return -1 + return self.left.next.val + + def Rear(self) -> int: + if self.isEmpty(): return -1 + return self.right.prev.val + + def isEmpty(self) -> bool: + return self.left.next == self.right + + def isFull(self) -> bool: + return self.space == 0 +``` + +```java +class ListNode { + int val; + ListNode next, prev; + + ListNode(int val, ListNode next, ListNode prev) { + this.val = val; + this.next = next; + this.prev = prev; + } +} + +public class MyCircularQueue { + private int space; + private ListNode left, right; + + public MyCircularQueue(int k) { + space = k; + left = new ListNode(0, null, null); + right = new ListNode(0, null, left); + left.next = right; + } + + public boolean enQueue(int value) { + if (isFull()) { + return false; + } + ListNode cur = new ListNode(value, right, right.prev); + right.prev.next = cur; + right.prev = cur; + space--; + return true; + } + + public boolean deQueue() { + if (isEmpty()) { + return false; + } + left.next = left.next.next; + left.next.prev = left; + space++; + return true; + } + + public int Front() { + return isEmpty() ? -1 : left.next.val; + } + + public int Rear() { + return isEmpty() ? -1 : right.prev.val; + } + + public boolean isEmpty() { + return left.next == right; + } + + public boolean isFull() { + return space == 0; + } +} +``` + +```cpp +class MyCircularQueue { +private: + struct ListNode { + int val; + ListNode* next; + ListNode* prev; + ListNode(int v, ListNode* n = nullptr, ListNode* p = nullptr) + : val(v), next(n), prev(p) {} + }; + + int space; + ListNode* left; + ListNode* right; + +public: + MyCircularQueue(int k) { + space = k; + left = new ListNode(0); + right = new ListNode(0, nullptr, left); + left->next = right; + } + + bool enQueue(int value) { + if (isFull()) return false; + ListNode* cur = new ListNode(value, right, right->prev); + right->prev->next = cur; + right->prev = cur; + space--; + return true; + } + + bool deQueue() { + if (isEmpty()) return false; + ListNode* tmp = left->next; + left->next = left->next->next; + left->next->prev = left; + delete tmp; + space++; + return true; + } + + int Front() { + return isEmpty() ? -1 : left->next->val; + } + + int Rear() { + return isEmpty() ? -1 : right->prev->val; + } + + bool isEmpty() { + return left->next == right; + } + + bool isFull() { + return space == 0; + } +}; +``` + +```javascript +class ListNode { + /** + * @param {number} val + * @param {ListNode} next + * @param {ListNode} prev + */ + constructor(val, next = null, prev = null) { + this.val = val; + this.next = next; + this.prev = prev; + } +} + +class MyCircularQueue { + /** + * @param {number} k + */ + constructor(k) { + this.space = k; + this.left = new ListNode(0); + this.right = new ListNode(0, null, this.left); + this.left.next = this.right; + } + + /** + * @param {number} value + * @return {boolean} + */ + enQueue(value) { + if (this.isFull()) return false; + const cur = new ListNode(value, this.right, this.right.prev); + this.right.prev.next = cur; + this.right.prev = cur; + this.space--; + return true; + } + + /** + * @return {boolean} + */ + deQueue() { + if (this.isEmpty()) return false; + this.left.next = this.left.next.next; + this.left.next.prev = this.left; + this.space++; + return true; + } + + /** + * @return {number} + */ + Front() { + return this.isEmpty() ? -1 : this.left.next.val; + } + + /** + * @return {number} + */ + Rear() { + return this.isEmpty() ? -1 : this.right.prev.val; + } + + /** + * @return {boolean} + */ + isEmpty() { + return this.left.next === this.right; + } + + /** + * @return {boolean} + */ + isFull() { + return this.space === 0; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: + * $O(n)$ time for initialization. + * $O(1)$ time for each $enQueue()$, $deQueue()$, $Front()$, $Rear()$, $isEmpty()$ and $isFull()$ function calls. +* Space complexity: $O(n)$ + +> Where $n$ is the size of the queue. + +--- + +## 4. Singly Linked List + +::tabs-start + +```python +class ListNode: + def __init__(self, val, nxt=None): + self.val = val + self.next = nxt + +class MyCircularQueue: + def __init__(self, k: int): + self.space = k + self.left = ListNode(0) + self.right = self.left + + def enQueue(self, value: int) -> bool: + if self.isFull(): return False + + cur = ListNode(value) + if self.isEmpty(): + self.left.next = cur + self.right = cur + else: + self.right.next = cur + self.right = cur + + self.space -= 1 + return True + + def deQueue(self) -> bool: + if self.isEmpty(): return False + + self.left.next = self.left.next.next + if self.left.next is None: + self.right = self.left + + self.space += 1 + return True + + def Front(self) -> int: + if self.isEmpty(): return -1 + return self.left.next.val + + def Rear(self) -> int: + if self.isEmpty(): return -1 + return self.right.val + + def isEmpty(self) -> bool: + return self.left.next is None + + def isFull(self) -> bool: + return self.space == 0 +``` + +```java +class ListNode { + int val; + ListNode next; + + ListNode(int val) { + this.val = val; + this.next = null; + } +} + +public class MyCircularQueue { + private int space; + private ListNode left; + private ListNode right; + + public MyCircularQueue(int k) { + this.space = k; + this.left = new ListNode(0); + this.right = this.left; + } + + public boolean enQueue(int value) { + if (isFull()) return false; + + ListNode cur = new ListNode(value); + if (isEmpty()) { + this.left.next = cur; + this.right = cur; + } else { + this.right.next = cur; + this.right = cur; + } + + this.space--; + return true; + } + + public boolean deQueue() { + if (isEmpty()) return false; + + this.left.next = this.left.next.next; + if (this.left.next == null) { + this.right = this.left; + } + + this.space++; + return true; + } + + public int Front() { + return isEmpty() ? -1 : this.left.next.val; + } + + public int Rear() { + return isEmpty() ? -1 : this.right.val; + } + + public boolean isEmpty() { + return this.left.next == null; + } + + public boolean isFull() { + return this.space == 0; + } +} +``` + +```cpp +class MyCircularQueue { +private: + struct ListNode { + int val; + ListNode* next; + ListNode(int v) : val(v), next(nullptr) {} + }; + + int space; + ListNode* left; + ListNode* right; + +public: + MyCircularQueue(int k) { + space = k; + left = new ListNode(0); + right = left; + } + + bool enQueue(int value) { + if (isFull()) return false; + + ListNode* cur = new ListNode(value); + if (isEmpty()) { + left->next = cur; + right = cur; + } else { + right->next = cur; + right = cur; + } + + space--; + return true; + } + + bool deQueue() { + if (isEmpty()) return false; + + ListNode* tmp = left->next; + left->next = left->next->next; + delete tmp; + if (!left->next) { + right = left; + } + + space++; + return true; + } + + int Front() { + return isEmpty() ? -1 : left->next->val; + } + + int Rear() { + return isEmpty() ? -1 : right->val; + } + + bool isEmpty() { + return left->next == nullptr; + } + + bool isFull() { + return space == 0; + } +}; +``` + +```javascript +class ListNode { + /** + * @param {number} val + * @param {ListNode} next + */ + constructor(val, next = null) { + this.val = val; + this.next = next; + } +} + +class MyCircularQueue { + /** + * @param {number} k + */ + constructor(k) { + this.space = k; + this.left = new ListNode(0); + this.right = this.left; + } + + /** + * @param {number} value + * @return {boolean} + */ + enQueue(value) { + if (this.isFull()) return false; + + const cur = new ListNode(value); + if (this.isEmpty()) { + this.left.next = cur; + this.right = cur; + } else { + this.right.next = cur; + this.right = cur; + } + + this.space--; + return true; + } + + /** + * @return {boolean} + */ + deQueue() { + if (this.isEmpty()) return false; + + this.left.next = this.left.next.next; + if (!this.left.next) { + this.right = this.left; + } + + this.space++; + return true; + } + + /** + * @return {number} + */ + Front() { + return this.isEmpty() ? -1 : this.left.next.val; + } + + /** + * @return {number} + */ + Rear() { + return this.isEmpty() ? -1 : this.right.val; + } + + /** + * @return {boolean} + */ + isEmpty() { + return this.left.next === null; + } + + /** + * @return {boolean} + */ + isFull() { + return this.space === 0; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: + * $O(n)$ time for initialization. + * $O(1)$ time for each $enQueue()$, $deQueue()$, $Front()$, $Rear()$, $isEmpty()$ and $isFull()$ function calls. +* Space complexity: $O(n)$ + +> Where $n$ is the size of the queue. \ No newline at end of file diff --git a/articles/find-in-mountain-array.md b/articles/find-in-mountain-array.md new file mode 100644 index 000000000..f687ba271 --- /dev/null +++ b/articles/find-in-mountain-array.md @@ -0,0 +1,684 @@ +## 1. Brute Force + +::tabs-start + +```python +# """ +# This is MountainArray's API interface. +# You should not implement it, or speculate about its implementation +# """ +#class MountainArray: +# def get(self, index: int) -> int: +# def length(self) -> int: + +class Solution: + def findInMountainArray(self, target: int, mountainArr: 'MountainArray') -> int: + n = mountainArr.length() + + for i in range(n): + if mountainArr.get(i) == target: + return i + + return -1 +``` + +```java +/** + * // This is MountainArray's API interface. + * // You should not implement it, or speculate about its implementation + * interface MountainArray { + * public int get(int index) {} + * public int length() {} + * } + */ + +public class Solution { + public int findInMountainArray(int target, MountainArray mountainArr) { + int n = mountainArr.length(); + + for (int i = 0; i < n; i++) { + if (mountainArr.get(i) == target) { + return i; + } + } + + return -1; + } +} +``` + +```cpp +/** + * // This is the MountainArray's API interface. + * // You should not implement it, or speculate about its implementation + * class MountainArray { + * public: + * int get(int index); + * int length(); + * }; + */ + +class Solution { +public: + int findInMountainArray(int target, MountainArray &mountainArr) { + int n = mountainArr.length(); + + for (int i = 0; i < n; i++) { + if (mountainArr.get(i) == target) { + return i; + } + } + + return -1; + } +}; +``` + +```javascript +/** + * // This is the MountainArray's API interface. + * // You should not implement it, or speculate about its implementation + * function MountainArray() { + * @param {number} index + * @return {number} + * this.get = function(index) { + * ... + * }; + * + * @return {number} + * this.length = function() { + * ... + * }; + * }; + */ + +class Solution { + /** + * @param {number} target + * @param {MountainArray} mountainArr + * @return {number} + */ + findInMountainArray(target, mountainArr) { + let n = mountainArr.length(); + + for (let i = 0; i < n; i++) { + if (mountainArr.get(i) === target) { + return i; + } + } + + return -1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ + +--- + +## 2. Binary Search + +::tabs-start + +```python +# """ +# This is MountainArray's API interface. +# You should not implement it, or speculate about its implementation +# """ +#class MountainArray: +# def get(self, index: int) -> int: +# def length(self) -> int: + +class Solution: + def findInMountainArray(self, target: int, mountainArr: 'MountainArray') -> int: + length = mountainArr.length() + + # Find Peak + l, r = 1, length - 2 + while l <= r: + m = (l + r) // 2 + left, mid, right = mountainArr.get(m - 1), mountainArr.get(m), mountainArr.get(m + 1) + if left < mid < right: + l = m + 1 + elif left > mid > right: + r = m - 1 + else: + break + peak = m + + # Search left portion + l, r = 0, peak - 1 + while l <= r: + m = (l + r) // 2 + val = mountainArr.get(m) + if val < target: + l = m + 1 + elif val > target: + r = m - 1 + else: + return m + + # Search right portion + l, r = peak, length - 1 + while l <= r: + m = (l + r) // 2 + val = mountainArr.get(m) + if val > target: + l = m + 1 + elif val < target: + r = m - 1 + else: + return m + + return -1 +``` + +```java +/** + * // This is MountainArray's API interface. + * // You should not implement it, or speculate about its implementation + * interface MountainArray { + * public int get(int index) {} + * public int length() {} + * } + */ + +public class Solution { + public int findInMountainArray(int target, MountainArray mountainArr) { + int length = mountainArr.length(); + + // Find Peak + int l = 1, r = length - 2, peak = 0; + while (l <= r) { + int m = (l + r) / 2; + int left = mountainArr.get(m - 1); + int mid = mountainArr.get(m); + int right = mountainArr.get(m + 1); + if (left < mid && mid < right) { + l = m + 1; + } else if (left > mid && mid > right) { + r = m - 1; + } else { + peak = m; + break; + } + } + + // Search left portion + l = 0; + r = peak - 1; + while (l <= r) { + int m = (l + r) / 2; + int val = mountainArr.get(m); + if (val < target) { + l = m + 1; + } else if (val > target) { + r = m - 1; + } else { + return m; + } + } + + // Search right portion + l = peak; + r = length - 1; + while (l <= r) { + int m = (l + r) / 2; + int val = mountainArr.get(m); + if (val > target) { + l = m + 1; + } else if (val < target) { + r = m - 1; + } else { + return m; + } + } + + return -1; + } +} +``` + +```cpp +/** + * // This is the MountainArray's API interface. + * // You should not implement it, or speculate about its implementation + * class MountainArray { + * public: + * int get(int index); + * int length(); + * }; + */ + +class Solution { +public: + int findInMountainArray(int target, MountainArray &mountainArr) { + int length = mountainArr.length(); + + // Find Peak + int l = 1, r = length - 2, peak = 0; + while (l <= r) { + int m = (l + r) / 2; + int left = mountainArr.get(m - 1); + int mid = mountainArr.get(m); + int right = mountainArr.get(m + 1); + if (left < mid && mid < right) { + l = m + 1; + } else if (left > mid && mid > right) { + r = m - 1; + } else { + peak = m; + break; + } + } + + // Search left portion + l = 0; + r = peak - 1; + while (l <= r) { + int m = (l + r) / 2; + int val = mountainArr.get(m); + if (val < target) { + l = m + 1; + } else if (val > target) { + r = m - 1; + } else { + return m; + } + } + + // Search right portion + l = peak; + r = length - 1; + while (l <= r) { + int m = (l + r) / 2; + int val = mountainArr.get(m); + if (val > target) { + l = m + 1; + } else if (val < target) { + r = m - 1; + } else { + return m; + } + } + + return -1; + } +}; +``` + +```javascript +/** + * // This is the MountainArray's API interface. + * // You should not implement it, or speculate about its implementation + * function MountainArray() { + * @param {number} index + * @return {number} + * this.get = function(index) { + * ... + * }; + * + * @return {number} + * this.length = function() { + * ... + * }; + * }; + */ + +class Solution { + /** + * @param {number} target + * @param {MountainArray} mountainArr + * @return {number} + */ + findInMountainArray(target, mountainArr) { + const length = mountainArr.length(); + + // Find Peak + let l = 1, r = length - 2, peak = 0; + while (l <= r) { + const m = Math.floor((l + r) / 2); + const left = mountainArr.get(m - 1); + const mid = mountainArr.get(m); + const right = mountainArr.get(m + 1); + if (left < mid && mid < right) { + l = m + 1; + } else if (left > mid && mid > right) { + r = m - 1; + } else { + peak = m; + break; + } + } + + // Search left portion + l = 0; + r = peak - 1; + while (l <= r) { + const m = Math.floor((l + r) / 2); + const val = mountainArr.get(m); + if (val < target) { + l = m + 1; + } else if (val > target) { + r = m - 1; + } else { + return m; + } + } + + // Search right portion + l = peak; + r = length - 1; + while (l <= r) { + const m = Math.floor((l + r) / 2); + const val = mountainArr.get(m); + if (val > target) { + l = m + 1; + } else if (val < target) { + r = m - 1; + } else { + return m; + } + } + + return -1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(\log n)$ +* Space complexity: $O(1)$ + +--- + +## 3. Binary Search + Caching + +::tabs-start + +```python +# """ +# This is MountainArray's API interface. +# You should not implement it, or speculate about its implementation +# """ +#class MountainArray: +# def get(self, index: int) -> int: +# def length(self) -> int: + +class Solution: + def findInMountainArray(self, target: int, mountainArr: 'MountainArray') -> int: + length = mountainArr.length() + cache = {} + + def get(i): + if i not in cache: + cache[i] = mountainArr.get(i) + return cache[i] + + # Find Peak + l, r = 1, length - 2 + while l <= r: + m = (l + r) >> 1 + left, mid, right = get(m - 1), get(m), get(m + 1) + if left < mid < right: + l = m + 1 + elif left > mid > right: + r = m - 1 + else: + break + peak = m + + def binary_search(l, r, ascending): + while l <= r: + m = (l + r) >> 1 + val = get(m) + if val == target: + return m + if ascending == (val < target): + l = m + 1 + else: + r = m - 1 + return -1 + + # Search left portion + res = binary_search(0, peak, True) + if res != -1: + return res + + # Search right portion + return binary_search(peak, length - 1, False) +``` + +```java +/** + * // This is MountainArray's API interface. + * // You should not implement it, or speculate about its implementation + * interface MountainArray { + * public int get(int index) {} + * public int length() {} + * } + */ + +public class Solution { + private Map cache = new HashMap<>(); + + private int get(int index, MountainArray mountainArr) { + if (!cache.containsKey(index)) { + cache.put(index, mountainArr.get(index)); + } + return cache.get(index); + } + + private int binarySearch(int l, int r, boolean ascending, MountainArray mountainArr, int target) { + while (l <= r) { + int m = (l + r) >> 1; + int val = get(m, mountainArr); + if (val == target) { + return m; + } + if (ascending == (val < target)) { + l = m + 1; + } else { + r = m - 1; + } + } + return -1; + } + + public int findInMountainArray(int target, MountainArray mountainArr) { + int length = mountainArr.length(); + + // Find Peak + int l = 1, r = length - 2, peak = 0; + while (l <= r) { + int m = (l + r) >> 1; + int left = get(m - 1, mountainArr); + int mid = get(m, mountainArr); + int right = get(m + 1, mountainArr); + if (left < mid && mid < right) { + l = m + 1; + } else if (left > mid && mid > right) { + r = m - 1; + } else { + peak = m; + break; + } + } + + // Search left portion + int res = binarySearch(0, peak, true, mountainArr, target); + if (res != -1) { + return res; + } + + // Search right portion + return binarySearch(peak, length - 1, false, mountainArr, target); + } +} +``` + +```cpp +/** + * // This is the MountainArray's API interface. + * // You should not implement it, or speculate about its implementation + * class MountainArray { + * public: + * int get(int index); + * int length(); + * }; + */ + +class Solution { +private: + unordered_map cache; + + int get(int index, MountainArray &mountainArr) { + if (cache.find(index) == cache.end()) { + cache[index] = mountainArr.get(index); + } + return cache[index]; + } + + int binarySearch(int l, int r, bool ascending, int target, MountainArray &mountainArr) { + while (l <= r) { + int m = (l + r) >> 1; + int val = get(m, mountainArr); + if (val == target) { + return m; + } + if (ascending == (val < target)) { + l = m + 1; + } else { + r = m - 1; + } + } + return -1; + } + +public: + int findInMountainArray(int target, MountainArray &mountainArr) { + int length = mountainArr.length(); + + // Find Peak + int l = 1, r = length - 2, peak = 0; + while (l <= r) { + int m = (l + r) >> 1; + int left = get(m - 1, mountainArr); + int mid = get(m, mountainArr); + int right = get(m + 1, mountainArr); + if (left < mid && mid < right) { + l = m + 1; + } else if (left > mid && mid > right) { + r = m - 1; + } else { + peak = m; + break; + } + } + + // Search left portion + int res = binarySearch(0, peak, true, target, mountainArr); + if (res != -1) { + return res; + } + + // Search right portion + return binarySearch(peak, length - 1, false, target, mountainArr); + } +}; +``` + +```javascript +/** + * // This is the MountainArray's API interface. + * // You should not implement it, or speculate about its implementation + * function MountainArray() { + * @param {number} index + * @return {number} + * this.get = function(index) { + * ... + * }; + * + * @return {number} + * this.length = function() { + * ... + * }; + * }; + */ + +class Solution { + /** + * @param {number} target + * @param {MountainArray} mountainArr + * @return {number} + */ + findInMountainArray(target, mountainArr) { + const cache = new Map(); + const get = (index) => { + if (!cache.has(index)) { + cache.set(index, mountainArr.get(index)); + } + return cache.get(index); + }; + + const binarySearch = (l, r, ascending) => { + while (l <= r) { + const m = Math.floor((l + r) / 2); + const val = get(m); + if (val === target) { + return m; + } + if (ascending === (val < target)) { + l = m + 1; + } else { + r = m - 1; + } + } + return -1; + }; + + const length = mountainArr.length(); + + // Find Peak + let l = 1, r = length - 2, peak = 0; + while (l <= r) { + const m = Math.floor((l + r) / 2); + const left = get(m - 1); + const mid = get(m); + const right = get(m + 1); + if (left < mid && mid < right) { + l = m + 1; + } else if (left > mid && mid > right) { + r = m - 1; + } else { + peak = m; + break; + } + } + + // Search left portion + let res = binarySearch(0, peak, true); + if (res !== -1) { + return res; + } + + // Search right portion + return binarySearch(peak, length - 1, false); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(\log n)$ +* Space complexity: $O(\log n)$ \ No newline at end of file diff --git a/articles/implement-queue-using-stacks.md b/articles/implement-queue-using-stacks.md new file mode 100644 index 000000000..9d51b2ad4 --- /dev/null +++ b/articles/implement-queue-using-stacks.md @@ -0,0 +1,351 @@ +## 1. Using Two Stacks (Brute Force) + +::tabs-start + +```python +class MyQueue: + + def __init__(self): + self.stack1 = [] + self.stack2 = [] + + def push(self, x: int) -> None: + self.stack1.append(x) + + def pop(self) -> int: + while len(self.stack1) > 1: + self.stack2.append(self.stack1.pop()) + res = self.stack1.pop() + while self.stack2: + self.stack1.append(self.stack2.pop()) + return res + + def peek(self) -> int: + while len(self.stack1) > 1: + self.stack2.append(self.stack1.pop()) + res = self.stack1[-1] + while self.stack2: + self.stack1.append(self.stack2.pop()) + return res + + def empty(self) -> bool: + return not self.stack1 +``` + +```java +public class MyQueue { + private Stack stack1; + private Stack stack2; + + public MyQueue() { + stack1 = new Stack<>(); + stack2 = new Stack<>(); + } + + public void push(int x) { + stack1.push(x); + } + + public int pop() { + while (stack1.size() > 1) { + stack2.push(stack1.pop()); + } + int res = stack1.pop(); + while (!stack2.isEmpty()) { + stack1.push(stack2.pop()); + } + return res; + } + + public int peek() { + while (stack1.size() > 1) { + stack2.push(stack1.pop()); + } + int res = stack1.peek(); + while (!stack2.isEmpty()) { + stack1.push(stack2.pop()); + } + return res; + } + + public boolean empty() { + return stack1.isEmpty(); + } +} +``` + +```cpp +class MyQueue { +private: + stack stack1; + stack stack2; + +public: + MyQueue() {} + + void push(int x) { + stack1.push(x); + } + + int pop() { + while (stack1.size() > 1) { + stack2.push(stack1.top()); + stack1.pop(); + } + int res = stack1.top(); + stack1.pop(); + while (!stack2.empty()) { + stack1.push(stack2.top()); + stack2.pop(); + } + return res; + } + + int peek() { + while (stack1.size() > 1) { + stack2.push(stack1.top()); + stack1.pop(); + } + int res = stack1.top(); + while (!stack2.empty()) { + stack1.push(stack2.top()); + stack2.pop(); + } + return res; + } + + bool empty() { + return stack1.empty(); + } +}; +``` + +```javascript +class MyQueue { + constructor() { + this.stack1 = []; + this.stack2 = []; + } + + /** + * @param {number} x + * @return {void} + */ + push(x) { + this.stack1.push(x); + } + + /** + * @return {number} + */ + pop() { + while (this.stack1.length > 1) { + this.stack2.push(this.stack1.pop()); + } + const res = this.stack1.pop(); + while (this.stack2.length) { + this.stack1.push(this.stack2.pop()); + } + return res; + } + + /** + * @return {number} + */ + peek() { + while (this.stack1.length > 1) { + this.stack2.push(this.stack1.pop()); + } + const res = this.stack1[this.stack1.length - 1]; + while (this.stack2.length) { + this.stack1.push(this.stack2.pop()); + } + return res; + } + + /** + * @return {boolean} + */ + empty() { + return this.stack1.length === 0; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: + * $O(1)$ time for initialization. + * $O(1)$ time for each $push()$ and $empty()$ function calls. + * $O(n)$ time for each $pop()$ and $peek()$ function calls. +* Space complexity: $O(n)$ + +--- + +## 2. Using Two Stacks (Amortized Complexity) + +::tabs-start + +```python +class MyQueue: + + def __init__(self): + self.s1 = [] + self.s2 = [] + + def push(self, x: int) -> None: + self.s1.append(x) + + def pop(self) -> int: + if not self.s2: + while self.s1: + self.s2.append(self.s1.pop()) + return self.s2.pop() + + def peek(self) -> int: + if not self.s2: + while self.s1: + self.s2.append(self.s1.pop()) + return self.s2[-1] + + def empty(self) -> bool: + return max(len(self.s1), len(self.s2)) == 0 +``` + +```java +public class MyQueue { + private Stack s1; + private Stack s2; + + public MyQueue() { + s1 = new Stack<>(); + s2 = new Stack<>(); + } + + public void push(int x) { + s1.push(x); + } + + public int pop() { + if (s2.isEmpty()) { + while (!s1.isEmpty()) { + s2.push(s1.pop()); + } + } + return s2.pop(); + } + + public int peek() { + if (s2.isEmpty()) { + while (!s1.isEmpty()) { + s2.push(s1.pop()); + } + } + return s2.peek(); + } + + public boolean empty() { + return s1.isEmpty() && s2.isEmpty(); + } +} +``` + +```cpp +class MyQueue { +private: + stack s1, s2; + +public: + MyQueue() {} + + void push(int x) { + s1.push(x); + } + + int pop() { + if (s2.empty()) { + while (!s1.empty()) { + s2.push(s1.top()); + s1.pop(); + } + } + int res = s2.top(); + s2.pop(); + return res; + } + + int peek() { + if (s2.empty()) { + while (!s1.empty()) { + s2.push(s1.top()); + s1.pop(); + } + } + return s2.top(); + } + + bool empty() { + return s1.empty() && s2.empty(); + } +}; +``` + +```javascript +class MyQueue { + constructor() { + this.s1 = []; + this.s2 = []; + } + + /** + * @param {number} x + * @return {void} + */ + push(x) { + this.s1.push(x); + } + + /** + * @return {number} + */ + pop() { + if (this.s2.length === 0) { + while (this.s1.length > 0) { + this.s2.push(this.s1.pop()); + } + } + return this.s2.pop(); + } + + /** + * @return {number} + */ + peek() { + if (this.s2.length === 0) { + while (this.s1.length > 0) { + this.s2.push(this.s1.pop()); + } + } + return this.s2[this.s2.length - 1]; + } + + /** + * @return {boolean} + */ + empty() { + return this.s1.length === 0 && this.s2.length === 0; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: + * $O(1)$ time for initialization. + * $O(1)$ time for each $push()$ and $empty()$ function calls. + * $O(1)$ amortized time for each $pop()$ and $peek()$ function calls. +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/implement-stack-using-queues.md b/articles/implement-stack-using-queues.md new file mode 100644 index 000000000..ee6199220 --- /dev/null +++ b/articles/implement-stack-using-queues.md @@ -0,0 +1,438 @@ +## 1. Using Two Queues + +::tabs-start + +```python +class MyStack: + + def __init__(self): + self.q1 = deque() + self.q2 = deque() + + def push(self, x: int) -> None: + self.q2.append(x) + while self.q1: + self.q2.append(self.q1.popleft()) + + self.q1, self.q2 = self.q2, self.q1 + + def pop(self) -> int: + return self.q1.popleft() + + def top(self) -> int: + return self.q1[0] + + def empty(self) -> bool: + return len(self.q1) == 0 +``` + +```java +public class MyStack { + private Queue q1; + private Queue q2; + + public MyStack() { + q1 = new LinkedList<>(); + q2 = new LinkedList<>(); + } + + public void push(int x) { + q2.offer(x); + while (!q1.isEmpty()) { + q2.offer(q1.poll()); + } + Queue temp = q1; + q1 = q2; + q2 = temp; + } + + public int pop() { + return q1.poll(); + } + + public int top() { + return q1.peek(); + } + + public boolean empty() { + return q1.isEmpty(); + } +} +``` + +```cpp +class MyStack { +private: + queue q1; + queue q2; + +public: + MyStack() {} + + void push(int x) { + q2.push(x); + while (!q1.empty()) { + q2.push(q1.front()); + q1.pop(); + } + swap(q1, q2); + } + + int pop() { + int top = q1.front(); + q1.pop(); + return top; + } + + int top() { + return q1.front(); + } + + bool empty() { + return q1.empty(); + } +}; +``` + +```javascript +class MyStack { + constructor() { + this.q1 = new Queue(); + this.q2 = new Queue(); + } + + /** + * @param {number} x + * @return {void} + */ + push(x) { + this.q2.push(x); + + while (!this.q1.isEmpty()) { + this.q2.push(this.q1.pop()); + } + + [this.q1, this.q2] = [this.q2, this.q1]; + } + + /** + * @return {number} + */ + pop() { + return this.q1.pop(); + } + + /** + * @return {number} + */ + top() { + return this.q1.front(); + } + + /** + * @return {boolean} + */ + empty() { + return this.q1.isEmpty(); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: + * $O(1)$ time for initialization. + * $O(n)$ time for each $push()$ function call. + * $O(1)$ time for each $pop()$ function call. +* Space complexity: $O(n)$ + +--- + +## 2. Using One Queue + +::tabs-start + +```python +class MyStack: + + def __init__(self): + self.q = deque() + + def push(self, x: int) -> None: + self.q.append(x) + for _ in range(len(self.q) - 1): + self.q.append(self.q.popleft()) + + def pop(self) -> int: + return self.q.popleft() + + def top(self) -> int: + return self.q[0] + + def empty(self) -> bool: + return len(self.q) == 0 +``` + +```java +public class MyStack { + private Queue q; + + public MyStack() { + q = new LinkedList<>(); + } + + public void push(int x) { + q.offer(x); + for (int i = q.size() - 1; i > 0; i--) { + q.offer(q.poll()); + } + } + + public int pop() { + return q.poll(); + } + + public int top() { + return q.peek(); + } + + public boolean empty() { + return q.isEmpty(); + } +} +``` + +```cpp +class MyStack { + queue q; + +public: + MyStack() {} + + void push(int x) { + q.push(x); + for (int i = q.size() - 1; i > 0; i--) { + q.push(q.front()); + q.pop(); + } + } + + int pop() { + int top = q.front(); + q.pop(); + return top; + } + + int top() { + return q.front(); + } + + bool empty() { + return q.empty(); + } +}; +``` + +```javascript +class MyStack { + constructor() { + this.q = new Queue(); + } + + /** + * @param {number} x + * @return {void} + */ + push(x) { + this.q.push(x); + + for (let i = this.q.size() - 1; i > 0; i--) { + this.q.push(this.q.pop()); + } + } + + /** + * @return {number} + */ + pop() { + return this.q.pop(); + } + + /** + * @return {number} + */ + top() { + return this.q.front(); + } + + /** + * @return {boolean} + */ + empty() { + return this.q.isEmpty(); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: + * $O(1)$ time for initialization. + * $O(n)$ time for each $push()$ function call. + * $O(1)$ time for each $pop()$ function call. +* Space complexity: $O(n)$ + +--- + +## 3. Queue Of Queues + +::tabs-start + +```python +class MyStack: + + def __init__(self): + self.q = None + + def push(self, x: int) -> None: + self.q = deque([x, self.q]) + + def pop(self) -> int: + top = self.q.popleft() + self.q = self.q.popleft() + return top + + def top(self) -> int: + return self.q[0] + + def empty(self) -> bool: + return not self.q +``` + +```java +public class MyStack { + private Queue q; + + public MyStack() { + q = null; + } + + public void push(int x) { + Queue newQueue = new LinkedList<>(); + newQueue.add(x); + newQueue.add(q); + q = newQueue; + } + + public int pop() { + if (q == null) return -1; + + int top = (int) q.poll(); + q = (Queue) q.poll(); + return top; + } + + public int top() { + if (q == null) return -1; + return (int) q.peek(); + } + + public boolean empty() { + return q == null; + } +} +``` + +```cpp +class MyStack { +private: + struct Node { + int val; + shared_ptr next; + Node(int v, shared_ptr n) : val(v), next(n) {} + }; + shared_ptr q; + +public: + MyStack() : q(nullptr) {} + + void push(int x) { + q = make_shared(x, q); + } + + int pop() { + if (!q) return -1; + int top = q->val; + q = q->next; + return top; + } + + int top() { + if (!q) return -1; + return q->val; + } + + bool empty() { + return q == nullptr; + } +}; +``` + +```javascript +class MyStack { + constructor() { + this.q = null; + } + + /** + * @param {number} x + * @return {void} + */ + push(x) { + const newQueue = new Queue(); + newQueue.enqueue(x); + newQueue.enqueue(this.q); + this.q = newQueue; + } + + /** + * @return {number} + */ + pop() { + if (this.q === null) return -1; + + const top = this.q.dequeue(); + this.q = this.q.dequeue(); + return top; + } + + /** + * @return {number} + */ + top() { + if (this.q === null) return -1; + return this.q.front(); + } + + /** + * @return {boolean} + */ + empty() { + return this.q === null; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: + * $O(1)$ time for initialization. + * $O(1)$ time for each $push()$ function call. + * $O(1)$ time for each $pop()$ function call. +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/insert-greatest-common-divisors-in-linked-list.md b/articles/insert-greatest-common-divisors-in-linked-list.md new file mode 100644 index 000000000..ffa61c7ce --- /dev/null +++ b/articles/insert-greatest-common-divisors-in-linked-list.md @@ -0,0 +1,155 @@ +## 1. Simulation + +::tabs-start + +```python +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, val=0, next=None): +# self.val = val +# self.next = next +class Solution: + def insertGreatestCommonDivisors(self, head: Optional[ListNode]) -> Optional[ListNode]: + def gcd(a, b): + while b > 0: + a, b = b, a % b + return a + + cur = head + while cur.next: + n1, n2 = cur.val, cur.next.val + cur.next = ListNode(gcd(n1, n2), cur.next) + cur = cur.next.next + + return head +``` + +```java +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode() {} + * ListNode(int val) { this.val = val; } + * ListNode(int val, ListNode next) { this.val = val; this.next = next; } + * } + */ +public class Solution { + public ListNode insertGreatestCommonDivisors(ListNode head) { + if (head == null) return null; + + ListNode cur = head; + + while (cur.next != null) { + int n1 = cur.val, n2 = cur.next.val; + int gcdValue = gcd(n1, n2); + ListNode newNode = new ListNode(gcdValue, cur.next); + cur.next = newNode; + cur = newNode.next; + } + + return head; + } + + private int gcd(int a, int b) { + while (b > 0) { + int temp = b; + b = a % b; + a = temp; + } + return a; + } +} +``` + +```cpp +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode() : val(0), next(nullptr) {} + * ListNode(int x) : val(x), next(nullptr) {} + * ListNode(int x, ListNode *next) : val(x), next(next) {} + * }; + */ +class Solution { +public: + ListNode* insertGreatestCommonDivisors(ListNode* head) { + if (!head) return nullptr; + + ListNode* cur = head; + + while (cur->next) { + int n1 = cur->val, n2 = cur->next->val; + int gcdValue = gcd(n1, n2); + ListNode* newNode = new ListNode(gcdValue, cur->next); + cur->next = newNode; + cur = newNode->next; + } + + return head; + } + +private: + int gcd(int a, int b) { + while (b > 0) { + int temp = b; + b = a % b; + a = temp; + } + return a; + } +}; +``` + +```javascript +/** + * Definition for singly-linked list. + * class ListNode { + * constructor(val = 0, next = null) { + * this.val = val; + * this.next = next; + * } + * } + */ +class Solution { + /** + * @param {ListNode} head + * @return {ListNode} + */ + insertGreatestCommonDivisors(head) { + if (!head) return null; + const gcd = (a, b) => { + while (b > 0) { + [a, b] = [b, a % b]; + } + return a; + }; + + let cur = head; + + while (cur.next) { + const n1 = cur.val, n2 = cur.next.val; + const gcdValue = gcd(n1, n2); + const newNode = new ListNode(gcdValue, cur.next); + cur.next = newNode; + cur = newNode.next; + } + + return head; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * \log (min(a, b)))$ +* Space complexity: + * $O(n)$ space for the gcd ListNodes. + * $O(1)$ extra space. + +> Where $n$ is the length of the given list, and $a$ and $b$ are two numbers passed to the $gcd()$ function. \ No newline at end of file diff --git a/articles/lemonade-change.md b/articles/lemonade-change.md new file mode 100644 index 000000000..662b4dc91 --- /dev/null +++ b/articles/lemonade-change.md @@ -0,0 +1,241 @@ +## 1. Iteration - I + +::tabs-start + +```python +class Solution: + def lemonadeChange(self, bills: List[int]) -> bool: + five, ten = 0, 0 + for b in bills: + if b == 5: + five += 1 + elif b == 10: + ten += 1 + if five > 0: + five -= 1 + else: + return False + else: + change = b - 5 + if change == 15 and five > 0 and ten > 0: + five -= 1 + ten -= 1 + elif change == 15 and five >= 3: + five -= 3 + else: + return False + return True +``` + +```java +public class Solution { + public boolean lemonadeChange(int[] bills) { + int five = 0, ten = 0; + for (int b : bills) { + if (b == 5) { + five++; + } else if (b == 10) { + ten++; + if (five > 0) { + five--; + } else { + return false; + } + } else { + if (five > 0 && ten > 0) { + five--; + ten--; + } else if (five >= 3) { + five -= 3; + } else { + return false; + } + } + } + return true; + } +} +``` + +```cpp +class Solution { +public: + bool lemonadeChange(vector& bills) { + int five = 0, ten = 0; + for (int b : bills) { + if (b == 5) { + five++; + } else if (b == 10) { + ten++; + if (five > 0) { + five--; + } else { + return false; + } + } else { + if (five > 0 && ten > 0) { + five--; + ten--; + } else if (five >= 3) { + five -= 3; + } else { + return false; + } + } + } + return true; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} bills + * @return {boolean} + */ + lemonadeChange(bills) { + let five = 0, ten = 0; + for (let b of bills) { + if (b === 5) { + five++; + } else if (b === 10) { + ten++; + if (five > 0) { + five--; + } else { + return false; + } + } else { + if (five > 0 && ten > 0) { + five--; + ten--; + } else if (five >= 3) { + five -= 3; + } else { + return false; + } + } + } + return true; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. + +--- + +## 2. Iteration - II + +::tabs-start + +```python +class Solution: + def lemonadeChange(self, bills: List[int]) -> bool: + five, ten = 0, 0 + for b in bills: + if b == 5: + five += 1 + elif b == 10: + five, ten = five - 1, ten + 1 + elif ten > 0: + five, ten = five - 1, ten - 1 + else: + five -= 3 + if five < 0: + return False + return True +``` + +```java +public class Solution { + public boolean lemonadeChange(int[] bills) { + int five = 0, ten = 0; + for (int b : bills) { + if (b == 5) { + five++; + } else if (b == 10) { + five--; + ten++; + } else if (ten > 0) { + five--; + ten--; + } else { + five -= 3; + } + if (five < 0) { + return false; + } + } + return true; + } +} +``` + +```cpp +class Solution { +public: + bool lemonadeChange(vector& bills) { + int five = 0, ten = 0; + for (int b : bills) { + if (b == 5) { + five++; + } else if (b == 10) { + five--; + ten++; + } else if (ten > 0) { + five--; + ten--; + } else { + five -= 3; + } + if (five < 0) { + return false; + } + } + return true; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} bills + * @return {boolean} + */ + lemonadeChange(bills) { + let five = 0, ten = 0; + for (let b of bills) { + if (b === 5) { + five++; + } else if (b === 10) { + five--; + ten++; + } else if (ten > 0) { + five--; + ten--; + } else { + five -= 3; + } + if (five < 0) { + return false; + } + } + return true; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. \ No newline at end of file diff --git a/articles/lfu-cache.md b/articles/lfu-cache.md new file mode 100644 index 000000000..ec31ef4fb --- /dev/null +++ b/articles/lfu-cache.md @@ -0,0 +1,715 @@ +## 1. Brute Force + +::tabs-start + +```python +class LFUCache: + + def __init__(self, capacity: int): + self.capacity = capacity + self.cache = {} # key -> [value, frequency, timestamp] + self.timestamp = 0 + + def get(self, key: int) -> int: + if key not in self.cache: + return -1 + + self.cache[key][1] += 1 + self.timestamp += 1 + self.cache[key][2] = self.timestamp + return self.cache[key][0] + + def put(self, key: int, value: int) -> None: + if self.capacity <= 0: + return + + self.timestamp += 1 + if key in self.cache: + self.cache[key][0] = value + self.cache[key][1] += 1 + self.cache[key][2] = self.timestamp + return + + if len(self.cache) >= self.capacity: + min_freq = float('inf') + min_timestamp = float('inf') + lfu_key = None + + for k, (_, freq, ts) in self.cache.items(): + if freq < min_freq or (freq == min_freq and ts < min_timestamp): + min_freq = freq + min_timestamp = ts + lfu_key = k + if lfu_key is not None: + del self.cache[lfu_key] + + self.cache[key] = [value, 1, self.timestamp] +``` + +```java +class Node { + int value, freq, timestamp; + + Node(int value, int freq, int timestamp) { + this.value = value; + this.freq = freq; + this.timestamp = timestamp; + } +} + +public class LFUCache { + + private int capacity, timestamp; + private Map cache; + + public LFUCache(int capacity) { + this.capacity = capacity; + this.timestamp = 0; + this.cache = new HashMap<>(); + } + + public int get(int key) { + if (!cache.containsKey(key)) return -1; + + Node node = cache.get(key); + node.freq++; + node.timestamp = ++timestamp; + return node.value; + } + + public void put(int key, int value) { + if (capacity <= 0) return; + + timestamp++; + if (cache.containsKey(key)) { + Node node = cache.get(key); + node.value = value; + node.freq++; + node.timestamp = timestamp; + return; + } + + if (cache.size() >= capacity) { + int minFreq = Integer.MAX_VALUE, minTimestamp = Integer.MAX_VALUE, lfuKey = -1; + + for (Map.Entry entry : cache.entrySet()) { + Node node = entry.getValue(); + if (node.freq < minFreq || (node.freq == minFreq && node.timestamp < minTimestamp)) { + minFreq = node.freq; + minTimestamp = node.timestamp; + lfuKey = entry.getKey(); + } + } + + cache.remove(lfuKey); + } + + cache.put(key, new Node(value, 1, timestamp)); + } +} +``` + +```cpp +class LFUCache { + struct Node { + int value, freq, timestamp; + Node(int v, int f, int t) : value(v), freq(f), timestamp(t) {} + }; + + int capacity, timestamp; + unordered_map cache; + +public: + LFUCache(int capacity) : capacity(capacity), timestamp(0) {} + + int get(int key) { + if (cache.find(key) == cache.end()) return -1; + + cache[key]->freq++; + cache[key]->timestamp = ++timestamp; + return cache[key]->value; + } + + void put(int key, int value) { + if (capacity <= 0) return; + + timestamp++; + if (cache.find(key) != cache.end()) { + cache[key]->value = value; + cache[key]->freq++; + cache[key]->timestamp = timestamp; + return; + } + + if (cache.size() >= capacity) { + int minFreq = INT_MAX, minTimestamp = INT_MAX, lfuKey = -1; + + for (const auto& [k, node] : cache) { + if (node->freq < minFreq || (node->freq == minFreq && node->timestamp < minTimestamp)) { + minFreq = node->freq; + minTimestamp = node->timestamp; + lfuKey = k; + } + } + delete cache[lfuKey]; + cache.erase(lfuKey); + } + + cache[key] = new Node(value, 1, timestamp); + } +}; +``` + +```javascript +class LFUCache { + /** + * @param {number} capacity + */ + constructor(capacity) { + this.capacity = capacity; + this.timestamp = 0; + this.cache = new Map(); + } + + /** + * @param {number} key + * @return {number} + */ + get(key) { + if (!this.cache.has(key)) return -1; + + const node = this.cache.get(key); + node.freq++; + node.timestamp = ++this.timestamp; + return node.value; + } + + /** + * @param {number} key + * @param {number} value + * @return {void} + */ + put(key, value) { + if (this.capacity <= 0) return; + + this.timestamp++; + if (this.cache.has(key)) { + const node = this.cache.get(key); + node.value = value; + node.freq++; + node.timestamp = this.timestamp; + return; + } + + if (this.cache.size >= this.capacity) { + let minFreq = Infinity, minTimestamp = Infinity, lfuKey = null; + + for (const [k, node] of this.cache.entries()) { + if (node.freq < minFreq || (node.freq === minFreq && node.timestamp < minTimestamp)) { + minFreq = node.freq; + minTimestamp = node.timestamp; + lfuKey = k; + } + } + + if (lfuKey !== null) this.cache.delete(lfuKey); + } + + this.cache.set(key, { value, freq: 1, timestamp: this.timestamp }); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: + * $O(1)$ time for initialization. + * $O(1)$ time for each $get()$ function call. + * $O(n)$ time for each $put()$ function call. +* Space complexity: $O(n)$ + +--- + +## 2. Doubly Linked List + +::tabs-start + +```python +class ListNode: + + def __init__(self, val, prev=None, next=None): + self.val = val + self.prev = prev + self.next = next + +class LinkedList: + + def __init__(self): + self.left = ListNode(0) + self.right = ListNode(0, self.left) + self.left.next = self.right + self.map = {} + + def length(self): + return len(self.map) + + def pushRight(self, val): + node = ListNode(val, self.right.prev, self.right) + self.map[val] = node + self.right.prev = node + node.prev.next = node + + def pop(self, val): + if val in self.map: + node = self.map[val] + next, prev = node.next, node.prev + next.prev = prev + prev.next = next + self.map.pop(val, None) + + def popLeft(self): + res = self.left.next.val + self.pop(self.left.next.val) + return res + + def update(self, val): + self.pop(val) + self.pushRight(val) + +class LFUCache: + + def __init__(self, capacity: int): + self.cap = capacity + self.lfuCnt = 0 + self.valMap = {} # Map key -> val + self.countMap = defaultdict(int) # Map key -> count + # Map count of key -> linkedlist + self.listMap = defaultdict(LinkedList) + + def counter(self, key): + cnt = self.countMap[key] + self.countMap[key] += 1 + self.listMap[cnt].pop(key) + self.listMap[cnt + 1].pushRight(key) + + if cnt == self.lfuCnt and self.listMap[cnt].length() == 0: + self.lfuCnt += 1 + + + def get(self, key: int) -> int: + if key not in self.valMap: + return -1 + self.counter(key) + return self.valMap[key] + + def put(self, key: int, value: int) -> None: + if self.cap == 0: + return + + if key not in self.valMap and len(self.valMap) == self.cap: + res = self.listMap[self.lfuCnt].popLeft() + self.valMap.pop(res) + self.countMap.pop(res) + + self.valMap[key] = value + self.counter(key) + self.lfuCnt = min(self.lfuCnt, self.countMap[key]) +``` + +```java +class ListNode { + int val; + ListNode prev, next; + + ListNode(int val) { + this.val = val; + } + + ListNode(int val, ListNode prev, ListNode next) { + this.val = val; + this.prev = prev; + this.next = next; + } +} + +class DoublyLinkedList { + private ListNode left, right; + private Map map; + + DoublyLinkedList() { + this.left = new ListNode(0); + this.right = new ListNode(0, this.left, null); + this.left.next = this.right; + this.map = new HashMap<>(); + } + + public int length() { + return map.size(); + } + + public void pushRight(int val) { + ListNode node = new ListNode(val, this.right.prev, this.right); + this.map.put(val, node); + this.right.prev.next = node; + this.right.prev = node; + } + + public void pop(int val) { + if (this.map.containsKey(val)) { + ListNode node = this.map.get(val); + ListNode prev = node.prev, next = node.next; + prev.next = next; + next.prev = prev; + this.map.remove(val); + } + } + + public int popLeft() { + int res = this.left.next.val; + pop(res); + return res; + } + + public void update(int val) { + pop(val); + pushRight(val); + } +} + +public class LFUCache { + private int capacity; + private int lfuCount; + private Map valMap; + private Map countMap; + private Map listMap; + + public LFUCache(int capacity) { + this.capacity = capacity; + this.lfuCount = 0; + this.valMap = new HashMap<>(); + this.countMap = new HashMap<>(); + this.listMap = new HashMap<>(); + } + + private void counter(int key) { + int count = countMap.get(key); + countMap.put(key, count + 1); + listMap.putIfAbsent(count, new DoublyLinkedList()); + listMap.get(count).pop(key); + + listMap.putIfAbsent(count + 1, new DoublyLinkedList()); + listMap.get(count + 1).pushRight(key); + + if (count == lfuCount && listMap.get(count).length() == 0) { + lfuCount++; + } + } + + public int get(int key) { + if (!valMap.containsKey(key)) { + return -1; + } + counter(key); + return valMap.get(key); + } + + public void put(int key, int value) { + if (capacity == 0) { + return; + } + + if (!valMap.containsKey(key) && valMap.size() == capacity) { + int toRemove = listMap.get(lfuCount).popLeft(); + valMap.remove(toRemove); + countMap.remove(toRemove); + } + + valMap.put(key, value); + countMap.putIfAbsent(key, 0); + counter(key); + lfuCount = Math.min(lfuCount, countMap.get(key)); + } +} +``` + +```cpp +class LFUCache { + struct ListNode { + int val; + ListNode* prev; + ListNode* next; + + ListNode(int val) : val(val), prev(nullptr), next(nullptr) {} + ListNode(int val, ListNode* prev, ListNode* next) : val(val), prev(prev), next(next) {} + }; + + struct LinkedList { + ListNode* left; + ListNode* right; + unordered_map map; + + LinkedList() { + left = new ListNode(0); + right = new ListNode(0); + left->next = right; + right->prev = left; + } + + ~LinkedList() { + while (left->next != right) { + ListNode* temp = left->next; + left->next = temp->next; + delete temp; + } + delete left; + delete right; + } + + int length() { + return map.size(); + } + + void pushRight(int val) { + ListNode* node = new ListNode(val, right->prev, right); + map[val] = node; + right->prev->next = node; + right->prev = node; + } + + void pop(int val) { + if (map.find(val) != map.end()) { + ListNode* node = map[val]; + ListNode* prev = node->prev; + ListNode* next = node->next; + prev->next = next; + next->prev = prev; + map.erase(val); + delete node; + } + } + + int popLeft() { + int res = left->next->val; + pop(res); + return res; + } + + void update(int val) { + pop(val); + pushRight(val); + } + }; + + int capacity; + int lfuCount; + unordered_map valMap; // Map key -> value + unordered_map countMap; // Map key -> count + unordered_map listMap; // Map count -> linked list + + void counter(int key) { + int count = countMap[key]; + countMap[key] = count + 1; + + listMap[count]->pop(key); + + if (!listMap.count(count + 1)) { + listMap[count + 1] = new LinkedList(); + } + listMap[count + 1]->pushRight(key); + + if (count == lfuCount && listMap[count]->length() == 0) { + lfuCount++; + } + } + +public: + LFUCache(int capacity) : capacity(capacity), lfuCount(0) { + listMap[0] = new LinkedList(); + } + + ~LFUCache() { + for (auto& pair : listMap) { + delete pair.second; + } + } + + int get(int key) { + if (valMap.find(key) == valMap.end()) { + return -1; + } + counter(key); + return valMap[key]; + } + + void put(int key, int value) { + if (capacity == 0) { + return; + } + + if (valMap.find(key) == valMap.end() && valMap.size() == capacity) { + int toRemove = listMap[lfuCount]->popLeft(); + valMap.erase(toRemove); + countMap.erase(toRemove); + } + + valMap[key] = value; + if (countMap.find(key) == countMap.end()) { + countMap[key] = 0; + listMap[0]->pushRight(key); + lfuCount = 0; + } + counter(key); + } +}; +``` + +```javascript +class ListNode { + /** + * @param {number} val + * @param {ListNode} prev + * @param {ListNode} next + */ + constructor(val, prev = null, next = null) { + this.val = val; + this.prev = prev; + this.next = next; + } +} + +class LinkedList { + constructor() { + this.left = new ListNode(0); + this.right = new ListNode(0); + this.left.next = this.right; + this.right.prev = this.left; + this.map = new Map(); + } + + /** + * @return {number} + */ + length() { + return this.map.size; + } + + /** + * @param {number} val + */ + pushRight(val) { + const node = new ListNode(val, this.right.prev, this.right); + this.map.set(val, node); + this.right.prev.next = node; + this.right.prev = node; + } + + /** + * @param {number} val + */ + pop(val) { + if (this.map.has(val)) { + const node = this.map.get(val); + const prev = node.prev; + const next = node.next; + prev.next = next; + next.prev = prev; + this.map.delete(val); + } + } + + /** + * @return {number} + */ + popLeft() { + const res = this.left.next.val; + this.pop(res); + return res; + } + + /** + * @param {number} val + */ + update(val) { + this.pop(val); + this.pushRight(val); + } +} + +class LFUCache { + /** + * @param {number} capacity + */ + constructor(capacity) { + this.capacity = capacity; + this.lfuCount = 0; + this.valMap = new Map(); + this.countMap = new Map(); + this.listMap = new Map(); + this.listMap.set(0, new LinkedList()); + } + + /** + * @param {number} key + */ + counter(key) { + const count = this.countMap.get(key); + this.countMap.set(key, count + 1); + + this.listMap.get(count).pop(key); + + if (!this.listMap.has(count + 1)) { + this.listMap.set(count + 1, new LinkedList()); + } + this.listMap.get(count + 1).pushRight(key); + + if (count === this.lfuCount && this.listMap.get(count).length() === 0) { + this.lfuCount++; + } + } + + /** + * @param {number} key + * @return {number} + */ + get(key) { + if (!this.valMap.has(key)) { + return -1; + } + this.counter(key); + return this.valMap.get(key); + } + + /** + * @param {number} key + * @param {number} value + */ + put(key, value) { + if (this.capacity === 0) return; + + if (!this.valMap.has(key) && this.valMap.size === this.capacity) { + const toRemove = this.listMap.get(this.lfuCount).popLeft(); + this.valMap.delete(toRemove); + this.countMap.delete(toRemove); + } + + this.valMap.set(key, value); + if (!this.countMap.has(key)) { + this.countMap.set(key, 0); + this.listMap.get(0).pushRight(key); + this.lfuCount = 0; + } + this.counter(key); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: + * $O(1)$ time for initialization. + * $O(1)$ time for each $get()$ and $put()$ function calls. +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/longest-turbulent-subarray.md b/articles/longest-turbulent-subarray.md new file mode 100644 index 000000000..a7876d5c0 --- /dev/null +++ b/articles/longest-turbulent-subarray.md @@ -0,0 +1,638 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def maxTurbulenceSize(self, arr: List[int]) -> int: + n = len(arr) + res = 1 + + for i in range(n - 1): + if arr[i] == arr[i + 1]: + continue + + sign = 1 if arr[i] > arr[i + 1] else 0 + j = i + 1 + while j < n - 1: + if arr[j] == arr[j + 1]: + break + curSign = 1 if arr[j] > arr[j + 1] else 0 + if sign == curSign: + break + sign = curSign + j += 1 + + res = max(res, j - i + 1) + + return res +``` + +```java +public class Solution { + public int maxTurbulenceSize(int[] arr) { + int n = arr.length; + int res = 1; + + for (int i = 0; i < n - 1; i++) { + if (arr[i] == arr[i + 1]) continue; + + int sign = arr[i] > arr[i + 1] ? 1 : 0; + int j = i + 1; + + while (j < n - 1) { + if (arr[j] == arr[j + 1]) break; + + int curSign = arr[j] > arr[j + 1] ? 1 : 0; + if (sign == curSign) break; + + sign = curSign; + j++; + } + + res = Math.max(res, j - i + 1); + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int maxTurbulenceSize(vector& arr) { + int n = arr.size(); + int res = 1; + + for (int i = 0; i < n - 1; i++) { + if (arr[i] == arr[i + 1]) continue; + + int sign = arr[i] > arr[i + 1] ? 1 : 0; + int j = i + 1; + + while (j < n - 1) { + if (arr[j] == arr[j + 1]) break; + + int curSign = arr[j] > arr[j + 1] ? 1 : 0; + if (sign == curSign) break; + + sign = curSign; + j++; + } + + res = max(res, j - i + 1); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} arr + * @return {number} + */ + maxTurbulenceSize(arr) { + const n = arr.length; + let res = 1; + + for (let i = 0; i < n - 1; i++) { + if (arr[i] === arr[i + 1]) continue; + + let sign = arr[i] > arr[i + 1] ? 1 : 0; + let j = i + 1; + + while (j < n - 1) { + if (arr[j] === arr[j + 1]) break; + + let curSign = arr[j] > arr[j + 1] ? 1 : 0; + if (sign === curSign) break; + + sign = curSign; + j++; + } + + res = Math.max(res, j - i + 1); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(1)$ extra space. + +--- + +## 2. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def maxTurbulenceSize(self, arr: List[int]) -> int: + n = len(arr) + memo = {} + + def dfs(i, sign): + if i == n - 1: + return 1 + if (i, sign) in memo: + return memo[(i, sign)] + + res = 1 + if ((sign and arr[i] > arr[i + 1]) or + (not sign and arr[i] < arr[i + 1]) + ): + res = 1 + dfs(i + 1, not sign) + + memo[(i, sign)] = res + return res + + max_len = 1 + for i in range(n): + max_len = max(max_len, dfs(i, True), dfs(i, False)) + + return max_len +``` + +```java +public class Solution { + private int[][] memo; + + public int maxTurbulenceSize(int[] arr) { + int n = arr.length; + memo = new int[n][2]; + + for (int i = 0; i < n; i++) { + memo[i][0] = -1; + memo[i][1] = -1; + } + + int maxLen = 1; + for (int i = 0; i < n; i++) { + maxLen = Math.max(maxLen, dfs(i, true, arr)); + maxLen = Math.max(maxLen, dfs(i, false, arr)); + } + + return maxLen; + } + + private int dfs(int i, boolean sign, int[] arr) { + int signIndex = sign ? 1 : 0; + if (i == arr.length - 1) return 1; + if (memo[i][signIndex] != -1) { + return memo[i][signIndex]; + } + + int res = 1; + if ((sign && arr[i] > arr[i + 1]) || + (!sign && arr[i] < arr[i + 1])) { + res = 1 + dfs(i + 1, !sign, arr); + } + + memo[i][signIndex] = res; + return res; + } +} +``` + +```cpp +class Solution { + vector> memo; + +public: + int maxTurbulenceSize(vector& arr) { + int n = arr.size(); + memo.assign(n, vector(2, -1)); + + int maxLen = 1; + for (int i = 0; i < n; i++) { + maxLen = max(maxLen, dfs(i, true, arr)); + maxLen = max(maxLen, dfs(i, false, arr)); + } + + return maxLen; + } + + int dfs(int i, bool sign, vector& arr) { + int signIndex = sign ? 1 : 0; + if (i == arr.size() - 1) return 1; + if (memo[i][signIndex] != -1) { + return memo[i][signIndex]; + } + + int res = 1; + if ((sign && arr[i] > arr[i + 1]) || + (!sign && arr[i] < arr[i + 1])) { + res = 1 + dfs(i + 1, !sign, arr); + } + + memo[i][signIndex] = res; + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} arr + * @return {number} + */ + maxTurbulenceSize(arr) { + const n = arr.length; + const memo = Array.from({ length: n }, () => [-1, -1]); + + const dfs = (i, sign) => { + const signIndex = sign ? 1 : 0; + if (i === n - 1) return 1; + if (memo[i][signIndex] !== -1) { + return memo[i][signIndex]; + } + + let res = 1; + if ((sign && arr[i] > arr[i + 1]) || + (!sign && arr[i] < arr[i + 1])) { + res = 1 + dfs(i + 1, !sign); + } + + memo[i][signIndex] = res; + return res; + }; + + let maxLen = 1; + for (let i = 0; i < n; i++) { + maxLen = Math.max(maxLen, dfs(i, true), dfs(i, false)); + } + + return maxLen; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def maxTurbulenceSize(self, arr: List[int]) -> int: + n = len(arr) + if n == 1: + return 1 + + dp = [[1] * 2 for _ in range(n)] + + max_len = 1 + for i in range(1, n): + if arr[i] > arr[i - 1]: + dp[i][1] = dp[i - 1][0] + 1 + elif arr[i] < arr[i - 1]: + dp[i][0] = dp[i - 1][1] + 1 + + max_len = max(max_len, dp[i][0], dp[i][1]) + + return max_len +``` + +```java +public class Solution { + public int maxTurbulenceSize(int[] arr) { + int n = arr.length; + if (n == 1) return 1; + + int[][] dp = new int[n][2]; + for (int i = 0; i < n; i++) { + dp[i][0] = dp[i][1] = 1; + } + + int maxLen = 1; + for (int i = 1; i < n; i++) { + if (arr[i] > arr[i - 1]) { + dp[i][1] = dp[i - 1][0] + 1; + } else if (arr[i] < arr[i - 1]) { + dp[i][0] = dp[i - 1][1] + 1; + } + maxLen = Math.max(maxLen, dp[i][0]); + maxLen = Math.max(maxLen, dp[i][1]); + } + + return maxLen; + } +} +``` + +```cpp +class Solution { +public: + int maxTurbulenceSize(vector& arr) { + int n = arr.size(); + if (n == 1) return 1; + + vector> dp(n, vector(2, 1)); + int maxLen = 1; + + for (int i = 1; i < n; ++i) { + if (arr[i] > arr[i - 1]) { + dp[i][1] = dp[i - 1][0] + 1; + } else if (arr[i] < arr[i - 1]) { + dp[i][0] = dp[i - 1][1] + 1; + } + maxLen = max(maxLen, max(dp[i][0], dp[i][1])); + } + + return maxLen; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} arr + * @return {number} + */ + maxTurbulenceSize(arr) { + const n = arr.length; + if (n === 1) return 1; + + const dp = Array.from({ length: n }, () => [1, 1]); + + let maxLen = 1; + for (let i = 1; i < n; i++) { + if (arr[i] > arr[i - 1]) { + dp[i][1] = dp[i - 1][0] + 1; + } else if (arr[i] < arr[i - 1]) { + dp[i][0] = dp[i - 1][1] + 1; + } + maxLen = Math.max(maxLen, dp[i][0], dp[i][1]); + } + + return maxLen; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 4. Sliding Window + +::tabs-start + +```python +class Solution: + def maxTurbulenceSize(self, arr: List[int]) -> int: + l, r, res, prev = 0, 1, 1, "" + + while r < len(arr): + if arr[r - 1] > arr[r] and prev != ">": + res = max(res, r - l + 1) + r += 1 + prev = ">" + elif arr[r - 1] < arr[r] and prev != "<": + res = max(res, r - l + 1) + r += 1 + prev = "<" + else: + r = r + 1 if arr[r] == arr[r - 1] else r + l = r - 1 + prev = "" + + return res +``` + +```java +public class Solution { + public int maxTurbulenceSize(int[] arr) { + int l = 0, r = 1, res = 1; + String prev = ""; + + while (r < arr.length) { + if (arr[r - 1] > arr[r] && !">".equals(prev)) { + res = Math.max(res, r - l + 1); + r++; + prev = ">"; + } else if (arr[r - 1] < arr[r] && !"<".equals(prev)) { + res = Math.max(res, r - l + 1); + r++; + prev = "<"; + } else { + r = (arr[r] == arr[r - 1]) ? r + 1 : r; + l = r - 1; + prev = ""; + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int maxTurbulenceSize(vector& arr) { + int l = 0, r = 1, res = 1; + string prev = ""; + + while (r < arr.size()) { + if (arr[r - 1] > arr[r] && prev != ">") { + res = max(res, r - l + 1); + r++; + prev = ">"; + } else if (arr[r - 1] < arr[r] && prev != "<") { + res = max(res, r - l + 1); + r++; + prev = "<"; + } else { + r = (arr[r] == arr[r - 1]) ? r + 1 : r; + l = r - 1; + prev = ""; + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} arr + * @return {number} + */ + maxTurbulenceSize(arr) { + let l = 0, r = 1, res = 1, prev = ""; + + while (r < arr.length) { + if (arr[r - 1] > arr[r] && prev !== ">") { + res = Math.max(res, r - l + 1); + r++; + prev = ">"; + } else if (arr[r - 1] < arr[r] && prev !== "<") { + res = Math.max(res, r - l + 1); + r++; + prev = "<"; + } else { + r = (arr[r] === arr[r - 1]) ? r + 1 : r; + l = r - 1; + prev = ""; + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. + +--- + +## 5. Iteration + +::tabs-start + +```python +class Solution: + def maxTurbulenceSize(self, arr: List[int]) -> int: + n = len(arr) + res = cnt = 0 + sign = -1 + + for i in range(n - 1): + if arr[i] > arr[i + 1]: + cnt = cnt + 1 if sign == 0 else 1 + sign = 1 + elif arr[i] < arr[i + 1]: + cnt = cnt + 1 if sign == 1 else 1 + sign = 0 + else: + cnt = 0 + sign = -1 + + res = max(res, cnt) + + return res + 1 +``` + +```java +public class Solution { + public int maxTurbulenceSize(int[] arr) { + int n = arr.length; + int res = 0, cnt = 0, sign = -1; + + for (int i = 0; i < n - 1; i++) { + if (arr[i] > arr[i + 1]) { + cnt = (sign == 0) ? cnt + 1 : 1; + sign = 1; + } else if (arr[i] < arr[i + 1]) { + cnt = (sign == 1) ? cnt + 1 : 1; + sign = 0; + } else { + cnt = 0; + sign = -1; + } + + res = Math.max(res, cnt); + } + + return res + 1; + } +} +``` + +```cpp +class Solution { +public: + int maxTurbulenceSize(vector& arr) { + int n = arr.size(); + int res = 0, cnt = 0, sign = -1; + + for (int i = 0; i < n - 1; i++) { + if (arr[i] > arr[i + 1]) { + cnt = (sign == 0) ? cnt + 1 : 1; + sign = 1; + } else if (arr[i] < arr[i + 1]) { + cnt = (sign == 1) ? cnt + 1 : 1; + sign = 0; + } else { + cnt = 0; + sign = -1; + } + + res = max(res, cnt); + } + + return res + 1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} arr + * @return {number} + */ + maxTurbulenceSize(arr) { + const n = arr.length; + let res = 0, cnt = 0, sign = -1; + + for (let i = 0; i < n - 1; i++) { + if (arr[i] > arr[i + 1]) { + cnt = (sign === 0) ? cnt + 1 : 1; + sign = 1; + } else if (arr[i] < arr[i + 1]) { + cnt = (sign === 1) ? cnt + 1 : 1; + sign = 0; + } else { + cnt = 0; + sign = -1; + } + + res = Math.max(res, cnt); + } + + return res + 1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. \ No newline at end of file diff --git a/articles/matchsticks-to-square.md b/articles/matchsticks-to-square.md new file mode 100644 index 000000000..c05819570 --- /dev/null +++ b/articles/matchsticks-to-square.md @@ -0,0 +1,474 @@ +## 1. Backtracking (Brute Force) + +::tabs-start + +```python +class Solution: + def makesquare(self, matchsticks: List[int]) -> bool: + if sum(matchsticks) % 4 != 0: + return False + + sides = [0] * 4 + + def dfs(i): + if i == len(matchsticks): + return sides[0] == sides[1] == sides[2] == sides[3] + + for side in range(4): + sides[side] += matchsticks[i] + if dfs(i + 1): + return True + sides[side] -= matchsticks[i] + + return False + + return dfs(0) +``` + +```java +public class Solution { + public boolean makesquare(int[] matchsticks) { + int sum = Arrays.stream(matchsticks).sum(); + if (sum % 4 != 0) return false; + + int[] sides = new int[4]; + return dfs(matchsticks, sides, 0); + } + + private boolean dfs(int[] matchsticks, int[] sides, int i) { + if (i == matchsticks.length) { + return sides[0] == sides[1] && sides[1] == sides[2] && sides[2] == sides[3]; + } + + for (int j = 0; j < 4; j++) { + sides[j] += matchsticks[i]; + if (dfs(matchsticks, sides, i + 1)) return true; + sides[j] -= matchsticks[i]; + } + + return false; + } +} +``` + +```cpp +class Solution { +public: + bool makesquare(vector& matchsticks) { + int sum = accumulate(matchsticks.begin(), matchsticks.end(), 0); + if (sum % 4 != 0) return false; + + vector sides(4, 0); + return dfs(matchsticks, sides, 0); + } + +private: + bool dfs(vector& matchsticks, vector& sides, int i) { + if (i == matchsticks.size()) { + return sides[0] == sides[1] && sides[1] == sides[2] && sides[2] == sides[3]; + } + + for (int j = 0; j < 4; j++) { + sides[j] += matchsticks[i]; + if (dfs(matchsticks, sides, i + 1)) return true; + sides[j] -= matchsticks[i]; + } + + return false; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} matchsticks + * @return {boolean} + */ + makesquare(matchsticks) { + const sum = matchsticks.reduce((a, b) => a + b, 0); + if (sum % 4 !== 0) return false; + + const sides = Array(4).fill(0); + const dfs = (i) => { + if (i === matchsticks.length) { + return sides[0] === sides[1] && sides[1] === sides[2] && sides[2] === sides[3]; + } + + for (let j = 0; j < 4; j++) { + sides[j] += matchsticks[i]; + if (dfs(i + 1)) return true; + sides[j] -= matchsticks[i]; + } + + return false; + }; + + return dfs(0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(4 ^ n)$ +* Space complexity: $O(n)$ for recursion stack. + +--- + +## 2. Backtracking (Pruning) + +::tabs-start + +```python +class Solution: + def makesquare(self, matchsticks: List[int]) -> bool: + total_length = sum(matchsticks) + if total_length % 4 != 0: + return False + + length = total_length // 4 + sides = [0] * 4 + matchsticks.sort(reverse=True) + + def dfs(i): + if i == len(matchsticks): + return True + + for side in range(4): + if sides[side] + matchsticks[i] <= length: + sides[side] += matchsticks[i] + if dfs(i + 1): + return True + sides[side] -= matchsticks[i] + + if sides[side] == 0: + break + + return False + + return dfs(0) +``` + +```java +public class Solution { + public boolean makesquare(int[] matchsticks) { + int totalLength = Arrays.stream(matchsticks).sum(); + if (totalLength % 4 != 0) return false; + + int length = totalLength / 4; + int[] sides = new int[4]; + Arrays.sort(matchsticks); + reverse(matchsticks); + + return dfs(matchsticks, sides, 0, length); + } + + private boolean dfs(int[] matchsticks, int[] sides, int index, int length) { + if (index == matchsticks.length) { + return true; + } + + for (int i = 0; i < 4; i++) { + if (sides[i] + matchsticks[index] <= length) { + sides[i] += matchsticks[index]; + if (dfs(matchsticks, sides, index + 1, length)) return true; + sides[i] -= matchsticks[index]; + } + + if (sides[i] == 0) break; + } + + return false; + } + + private void reverse(int[] matchsticks) { + for (int i = 0, j = matchsticks.length - 1; i < j; i++, j--) { + int temp = matchsticks[i]; + matchsticks[i] = matchsticks[j]; + matchsticks[j] = temp; + } + } +} +``` + +```cpp +class Solution { +public: + bool makesquare(vector& matchsticks) { + int totalLength = accumulate(matchsticks.begin(), matchsticks.end(), 0); + if (totalLength % 4 != 0) return false; + + int length = totalLength / 4; + vector sides(4, 0); + sort(matchsticks.rbegin(), matchsticks.rend()); + + return dfs(matchsticks, sides, 0, length); + } + +private: + bool dfs(vector& matchsticks, vector& sides, int index, int length) { + if (index == matchsticks.size()) { + return true; + } + + for (int i = 0; i < 4; i++) { + if (sides[i] + matchsticks[index] <= length) { + sides[i] += matchsticks[index]; + if (dfs(matchsticks, sides, index + 1, length)) return true; + sides[i] -= matchsticks[index]; + } + + if (sides[i] == 0) break; + } + + return false; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} matchsticks + * @return {boolean} + */ + makesquare(matchsticks) { + const totalLength = matchsticks.reduce((a, b) => a + b, 0); + if (totalLength % 4 !== 0) return false; + + const length = totalLength / 4; + const sides = Array(4).fill(0); + matchsticks.sort((a, b) => b - a); + + const dfs = (index) => { + if (index === matchsticks.length) { + return true; + } + + for (let i = 0; i < 4; i++) { + if (sides[i] + matchsticks[index] <= length) { + sides[i] += matchsticks[index]; + if (dfs(index + 1)) return true; + sides[i] -= matchsticks[index]; + } + + if (sides[i] === 0) break; + } + + return false; + }; + + return dfs(0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(4 ^ n)$ +* Space complexity: $O(n)$ for recursion stack. + +--- + +## 3. Dynamic Programming (Bit Mask) + +::tabs-start + +```python +class Solution: + def makesquare(self, matchsticks: List[int]) -> bool: + total_length = sum(matchsticks) + if total_length % 4 != 0: + return False + + length = total_length // 4 + if max(matchsticks) > length: + return False + + n = len(matchsticks) + dp = [float("-inf")] * (1 << n) + matchsticks.sort(reverse=True) + + def dfs(mask): + if mask == 0: + return 0 + if dp[mask] != float("-inf"): + return dp[mask] + + for i in range(n): + if mask & (1 << i): + res = dfs(mask ^ (1 << i)) + if res >= 0 and res + matchsticks[i] <= length: + dp[mask] = (res + matchsticks[i]) % length + return dp[mask] + if mask == (1 << n) - 1: + dp[mask] = -1 + return -1 + + dp[mask] = -1 + return -1 + + return not dfs((1 << n) - 1) +``` + +```java +public class Solution { + private int[] dp; + private int length; + private int n; + + public boolean makesquare(int[] matchsticks) { + int totalLength = Arrays.stream(matchsticks).sum(); + if (totalLength % 4 != 0) return false; + + length = totalLength / 4; + if (Arrays.stream(matchsticks).max().getAsInt() > length) { + return false; + } + + Arrays.sort(matchsticks); + reverse(matchsticks); + this.n = matchsticks.length; + this.dp = new int[1 << n]; + Arrays.fill(dp, Integer.MIN_VALUE); + + return dfs((1 << n) - 1, matchsticks) == 0; + } + + private int dfs(int mask, int[] matchsticks) { + if (mask == 0) return 0; + if (dp[mask] != Integer.MIN_VALUE) return dp[mask]; + + for (int i = 0; i < n; i++) { + if ((mask & (1 << i)) != 0) { + int res = dfs(mask ^ (1 << i), matchsticks); + if (res >= 0 && res + matchsticks[i] <= length) { + dp[mask] = (res + matchsticks[i]) % length; + return dp[mask]; + } + + if (mask == (1 << n) - 1) { + dp[mask] = -1; + return -1; + } + } + } + + dp[mask] = -1; + return dp[mask]; + } + + private void reverse(int[] matchsticks) { + for (int i = 0, j = matchsticks.length - 1; i < j; i++, j--) { + int temp = matchsticks[i]; + matchsticks[i] = matchsticks[j]; + matchsticks[j] = temp; + } + } +} +``` + +```cpp +class Solution { + vector dp; + int length, n; + +public: + bool makesquare(vector& matchsticks) { + int totalLength = accumulate(matchsticks.begin(), matchsticks.end(), 0); + if (totalLength % 4 != 0) return false; + + length = totalLength / 4; + if (*max_element(matchsticks.begin(), matchsticks.end()) > length) { + return false; + } + + sort(matchsticks.rbegin(), matchsticks.rend()); + n = matchsticks.size(); + dp.resize(1 << n, INT_MIN); + + return dfs((1 << n) - 1, matchsticks) == 0; + } + +private: + int dfs(int mask, vector& matchsticks) { + if (mask == 0) return 0; + if (dp[mask] != INT_MIN) return dp[mask]; + + for (int i = 0; i < n; i++) { + if (mask & (1 << i)) { + int res = dfs(mask ^ (1 << i), matchsticks); + if (res >= 0 && res + matchsticks[i] <= length) { + dp[mask] = (res + matchsticks[i]) % length; + return dp[mask]; + } + + if (mask == (1 << n) - 1) { + dp[mask] = -1; + return -1; + } + } + } + + dp[mask] = -1; + return dp[mask]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} matchsticks + * @return {boolean} + */ + makesquare(matchsticks) { + const totalLength = matchsticks.reduce((a, b) => a + b, 0); + if (totalLength % 4 !== 0) return false; + + const length = totalLength / 4; + if (Math.max(...matchsticks) > length) return false; + + matchsticks.sort((a, b) => b - a); + const n = matchsticks.length; + const dp = new Array(1 << n).fill(-Infinity); + + const dfs = (mask) => { + if (mask === 0) return 0; + if (dp[mask] !== -Infinity) return dp[mask]; + + for (let i = 0; i < n; i++) { + if (mask & (1 << i)) { + const res = dfs(mask ^ (1 << i)); + if (res >= 0 && res + matchsticks[i] <= length) { + dp[mask] = (res + matchsticks[i]) % length; + return dp[mask]; + } + + if (mask === (1 << n) - 1) { + dp[mask] = -1; + return -1; + } + } + } + + dp[mask] = -1; + return dp[mask]; + }; + + return dfs((1 << n) - 1) === 0; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * 2 ^ n)$ +* Space complexity: $O(n + 2 ^ n)$ \ No newline at end of file diff --git a/articles/maximum-frequency-stack.md b/articles/maximum-frequency-stack.md new file mode 100644 index 000000000..317e2546c --- /dev/null +++ b/articles/maximum-frequency-stack.md @@ -0,0 +1,525 @@ +## 1. Brute Force + +::tabs-start + +```python +class FreqStack: + + def __init__(self): + self.cnt = defaultdict(int) + self.stack = [] + + def push(self, val: int) -> None: + self.stack.append(val) + self.cnt[val] += 1 + + def pop(self) -> int: + maxCnt = max(self.cnt.values()) + i = len(self.stack) - 1 + while self.cnt[self.stack[i]] != maxCnt: + i -= 1 + self.cnt[self.stack[i]] -= 1 + return self.stack.pop(i) +``` + +```java +public class FreqStack { + private Map cnt; + private List stack; + + public FreqStack() { + cnt = new HashMap<>(); + stack = new ArrayList<>(); + } + + public void push(int val) { + stack.add(val); + cnt.put(val, cnt.getOrDefault(val, 0) + 1); + } + + public int pop() { + int maxCnt = Collections.max(cnt.values()); + int i = stack.size() - 1; + while (cnt.get(stack.get(i)) != maxCnt) { + i--; + } + int val = stack.remove(i); + cnt.put(val, cnt.get(val) - 1); + return val; + } +} +``` + +```cpp +class FreqStack { +private: + unordered_map cnt; + vector stack; + +public: + FreqStack() {} + + void push(int val) { + stack.push_back(val); + cnt[val]++; + } + + int pop() { + int maxCnt = 0; + for (auto& [_, frequency] : cnt) { + maxCnt = max(maxCnt, frequency); + } + int i = stack.size() - 1; + while (cnt[stack[i]] != maxCnt) { + i--; + } + int val = stack[i]; + stack.erase(stack.begin() + i); + cnt[val]--; + return val; + } +}; +``` + +```javascript +class FreqStack { + constructor() { + this.cnt = new Map(); + this.stack = []; + } + + /** + * @param {number} val + * @return {void} + */ + push(val) { + this.stack.push(val); + this.cnt.set(val, (this.cnt.get(val) || 0) + 1); + } + + /** + * @return {number} + */ + pop() { + const maxCnt = Math.max(...this.cnt.values()); + let i = this.stack.length - 1; + while (this.cnt.get(this.stack[i]) !== maxCnt) { + i--; + } + const val = this.stack.splice(i, 1)[0]; + this.cnt.set(val, this.cnt.get(val) - 1); + return val; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: + * $O(1)$ time for each $push()$ function call. + * $O(n)$ time for each $pop()$ function call. +* Space complexity: $O(n)$ + +> Where $n$ is the number of elements in the stack. + +--- + +## 2. Heap + +::tabs-start + +```python +class FreqStack: + + def __init__(self): + self.heap = [] + self.cnt = defaultdict(int) + self.index = 0 + + def push(self, val: int) -> None: + self.cnt[val] += 1 + heapq.heappush(self.heap, (-self.cnt[val], -self.index, val)) + self.index += 1 + + def pop(self) -> int: + _, _, val = heapq.heappop(self.heap) + self.cnt[val] -= 1 + return val +``` + +```java +public class FreqStack { + private PriorityQueue heap; + private Map cnt; + private int index; + + public FreqStack() { + heap = new PriorityQueue<>((a, b) -> + a[0] != b[0] ? Integer.compare(b[0], a[0]) : Integer.compare(b[1], a[1]) + ); + cnt = new HashMap<>(); + index = 0; + } + + public void push(int val) { + cnt.put(val, cnt.getOrDefault(val, 0) + 1); + heap.offer(new int[]{cnt.get(val), index++, val}); + } + + public int pop() { + int[] top = heap.poll(); + int val = top[2]; + cnt.put(val, cnt.get(val) - 1); + return val; + } +} +``` + +```cpp +class FreqStack { +private: + priority_queue> heap; // {frequency, index, value} + unordered_map cnt; + int index; + +public: + FreqStack() : index(0) {} + + void push(int val) { + cnt[val]++; + heap.push({cnt[val], index++, val}); + } + + int pop() { + auto top = heap.top(); + heap.pop(); + int val = top[2]; + cnt[val]--; + return val; + } +}; +``` + +```javascript +class FreqStack { + constructor() { + this.heap = new MaxPriorityQueue({ + priority: (element) => element[0] * 100000 + element[1], + }); + this.cnt = new Map(); + this.index = 0; + } + + /** + * @param {number} val + * @return {void} + */ + push(val) { + this.cnt.set(val, (this.cnt.get(val) || 0) + 1); + this.heap.enqueue([this.cnt.get(val), this.index++, val]); + } + + /** + * @return {number} + */ + pop() { + const [, , val] = this.heap.dequeue().element; + this.cnt.set(val, this.cnt.get(val) - 1); + return val; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: + * $O(\log n)$ time for each $push()$ function call. + * $O(\log n)$ time for each $pop()$ function call. +* Space complexity: $O(n)$ + +> Where $n$ is the number of elements in the stack. + +--- + +## 3. Stack Of Stacks (Hash Map) + +::tabs-start + +```python +class FreqStack: + + def __init__(self): + self.cnt = {} + self.maxCnt = 0 + self.stacks = {} + + def push(self, val: int) -> None: + valCnt = 1 + self.cnt.get(val, 0) + self.cnt[val] = valCnt + if valCnt > self.maxCnt: + self.maxCnt = valCnt + self.stacks[valCnt] = [] + self.stacks[valCnt].append(val) + + def pop(self) -> int: + res = self.stacks[self.maxCnt].pop() + self.cnt[res] -= 1 + if not self.stacks[self.maxCnt]: + self.maxCnt -= 1 + return res +``` + +```java +class FreqStack { + private Map cnt; + private Map> stacks; + private int maxCnt; + + public FreqStack() { + cnt = new HashMap<>(); + stacks = new HashMap<>(); + maxCnt = 0; + } + + public void push(int val) { + int valCnt = cnt.getOrDefault(val, 0) + 1; + cnt.put(val, valCnt); + if (valCnt > maxCnt) { + maxCnt = valCnt; + stacks.putIfAbsent(valCnt, new Stack<>()); + } + stacks.get(valCnt).push(val); + } + + public int pop() { + int res = stacks.get(maxCnt).pop(); + cnt.put(res, cnt.get(res) - 1); + if (stacks.get(maxCnt).isEmpty()) { + maxCnt--; + } + return res; + } +} +``` + +```cpp +class FreqStack { +public: + unordered_map cnt; + unordered_map> stacks; + int maxCnt; + + FreqStack() { + maxCnt = 0; + } + + void push(int val) { + int valCnt = ++cnt[val]; + if (valCnt > maxCnt) { + maxCnt = valCnt; + stacks[valCnt] = stack(); + } + stacks[valCnt].push(val); + } + + int pop() { + int res = stacks[maxCnt].top(); + stacks[maxCnt].pop(); + cnt[res]--; + if (stacks[maxCnt].empty()) { + maxCnt--; + } + return res; + } +}; +``` + +```javascript +class FreqStack { + constructor() { + this.cnt = new Map(); + this.stacks = new Map(); + this.maxCnt = 0; + } + + /** + * @param {number} val + * @return {void} + */ + push(val) { + const valCnt = (this.cnt.get(val) || 0) + 1; + this.cnt.set(val, valCnt); + if (valCnt > this.maxCnt) { + this.maxCnt = valCnt; + if (!this.stacks.has(valCnt)) { + this.stacks.set(valCnt, []); + } + } + this.stacks.get(valCnt).push(val); + } + + /** + * @return {number} + */ + pop() { + const res = this.stacks.get(this.maxCnt).pop(); + this.cnt.set(res, this.cnt.get(res) - 1); + if (this.stacks.get(this.maxCnt).length === 0) { + this.maxCnt--; + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: + * $O(1)$ time for each $push()$ function call. + * $O(1)$ time for each $pop()$ function call. +* Space complexity: $O(n)$ + +> Where $n$ is the number of elements in the stack. + +--- + +## 4. Stack Of Stacks (Dynamic Array) + +::tabs-start + +```python +class FreqStack: + + def __init__(self): + self.cnt = {} + self.stacks = [[]] + + def push(self, val: int) -> None: + valCnt = 1 + self.cnt.get(val, 0) + self.cnt[val] = valCnt + if valCnt == len(self.stacks): + self.stacks.append([]) + self.stacks[valCnt].append(val) + + def pop(self) -> int: + res = self.stacks[-1].pop() + self.cnt[res] -= 1 + if not self.stacks[-1]: + self.stacks.pop() + return res +``` + +```java +public class FreqStack { + private Map cnt; + private List> stacks; + + public FreqStack() { + cnt = new HashMap<>(); + stacks = new ArrayList<>(); + stacks.add(new Stack<>()); + } + + public void push(int val) { + int valCnt = cnt.getOrDefault(val, 0) + 1; + cnt.put(val, valCnt); + if (valCnt == stacks.size()) { + stacks.add(new Stack<>()); + } + stacks.get(valCnt).push(val); + } + + public int pop() { + Stack topStack = stacks.get(stacks.size() - 1); + int res = topStack.pop(); + cnt.put(res, cnt.get(res) - 1); + if (topStack.isEmpty()) { + stacks.remove(stacks.size() - 1); + } + return res; + } +} +``` + +```cpp +class FreqStack { +public: + unordered_map cnt; + vector> stacks; + + FreqStack() { + stacks.push_back(stack()); + } + + void push(int val) { + int valCnt = ++cnt[val]; + if (valCnt == stacks.size()) { + stacks.push_back(stack()); + } + stacks[valCnt].push(val); + } + + int pop() { + stack& topStack = stacks.back(); + int res = topStack.top(); + topStack.pop(); + if (topStack.empty()) { + stacks.pop_back(); + } + cnt[res]--; + return res; + } +}; +``` + +```javascript +class FreqStack { + constructor() { + this.cnt = new Map(); + this.stacks = [[]]; + } + + /** + * @param {number} val + * @return {void} + */ + push(val) { + const valCnt = (this.cnt.get(val) || 0) + 1; + this.cnt.set(val, valCnt); + if (valCnt === this.stacks.length) { + this.stacks.push([]); + } + this.stacks[valCnt].push(val); + } + + /** + * @return {number} + */ + pop() { + const topStack = this.stacks[this.stacks.length - 1]; + const res = topStack.pop(); + this.cnt.set(res, this.cnt.get(res) - 1); + if (topStack.length === 0) { + this.stacks.pop(); + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: + * $O(1)$ time for each $push()$ function call. + * $O(1)$ time for each $pop()$ function call. +* Space complexity: $O(n)$ + +> Where $n$ is the number of elements in the stack. \ No newline at end of file diff --git a/articles/maximum-sum-circular-subarray.md b/articles/maximum-sum-circular-subarray.md new file mode 100644 index 000000000..d1f717448 --- /dev/null +++ b/articles/maximum-sum-circular-subarray.md @@ -0,0 +1,316 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def maxSubarraySumCircular(self, nums: List[int]) -> int: + n = len(nums) + res = nums[0] + + for i in range(n): + cur_sum = 0 + for j in range(i, i + n): + cur_sum += nums[j % n] + res = max(res, cur_sum) + + return res +``` + +```java +public class Solution { + public int maxSubarraySumCircular(int[] nums) { + int n = nums.length; + int res = nums[0]; + + for (int i = 0; i < n; i++) { + int curSum = 0; + for (int j = i; j < i + n; j++) { + curSum += nums[j % n]; + res = Math.max(res, curSum); + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int maxSubarraySumCircular(vector& nums) { + int n = nums.size(); + int res = nums[0]; + + for (int i = 0; i < n; i++) { + int curSum = 0; + for (int j = i; j < i + n; j++) { + curSum += nums[j % n]; + res = max(res, curSum); + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + maxSubarraySumCircular(nums) { + const n = nums.length; + let res = nums[0]; + + for (let i = 0; i < n; i++) { + let curSum = 0; + for (let j = i; j < i + n; j++) { + curSum += nums[j % n]; + res = Math.max(res, curSum); + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(1)$ extra space. + +--- + +## 2. Prefix & Suffix Sums + +::tabs-start + +```python +class Solution: + def maxSubarraySumCircular(self, nums: List[int]) -> int: + n = len(nums) + right_max = [0] * n + right_max[-1] = nums[-1] + suffix_sum = nums[-1] + + for i in range(n - 2, -1, -1): + suffix_sum += nums[i] + right_max[i] = max(right_max[i + 1], suffix_sum) + + max_sum = nums[0] + cur_max = 0 + prefix_sum = 0 + + for i in range(n): + cur_max = max(cur_max, 0) + nums[i] + max_sum = max(max_sum, cur_max) + prefix_sum += nums[i] + if i + 1 < n: + max_sum = max(max_sum, prefix_sum + right_max[i + 1]) + + return max_sum +``` + +```java +public class Solution { + public int maxSubarraySumCircular(int[] nums) { + int n = nums.length; + int[] rightMax = new int[n]; + rightMax[n - 1] = nums[n - 1]; + int suffixSum = nums[n - 1]; + + for (int i = n - 2; i >= 0; i--) { + suffixSum += nums[i]; + rightMax[i] = Math.max(rightMax[i + 1], suffixSum); + } + + int maxSum = nums[0]; + int curMax = 0; + int prefixSum = 0; + + for (int i = 0; i < n; i++) { + curMax = Math.max(curMax, 0) + nums[i]; + maxSum = Math.max(maxSum, curMax); + prefixSum += nums[i]; + if (i + 1 < n) { + maxSum = Math.max(maxSum, prefixSum + rightMax[i + 1]); + } + } + + return maxSum; + } +} +``` + +```cpp +class Solution { +public: + int maxSubarraySumCircular(vector& nums) { + int n = nums.size(); + vector rightMax(n); + rightMax[n - 1] = nums[n - 1]; + int suffixSum = nums[n - 1]; + + for (int i = n - 2; i >= 0; --i) { + suffixSum += nums[i]; + rightMax[i] = max(rightMax[i + 1], suffixSum); + } + + int maxSum = nums[0]; + int curMax = 0; + int prefixSum = 0; + + for (int i = 0; i < n; ++i) { + curMax = max(curMax, 0) + nums[i]; + maxSum = max(maxSum, curMax); + prefixSum += nums[i]; + if (i + 1 < n) { + maxSum = max(maxSum, prefixSum + rightMax[i + 1]); + } + } + + return maxSum; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + maxSubarraySumCircular(nums) { + const n = nums.length; + const rightMax = new Array(n).fill(0); + rightMax[n - 1] = nums[n - 1]; + let suffixSum = nums[n - 1]; + + for (let i = n - 2; i >= 0; i--) { + suffixSum += nums[i]; + rightMax[i] = Math.max(rightMax[i + 1], suffixSum); + } + + let maxSum = nums[0]; + let curMax = 0; + let prefixSum = 0; + + for (let i = 0; i < n; i++) { + curMax = Math.max(curMax, 0) + nums[i]; + maxSum = Math.max(maxSum, curMax); + prefixSum += nums[i]; + if (i + 1 < n) { + maxSum = Math.max(maxSum, prefixSum + rightMax[i + 1]); + } + } + + return maxSum; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Kadane's Algorithm + +::tabs-start + +```python +class Solution: + def maxSubarraySumCircular(self, nums: List[int]) -> int: + globMax, globMin = nums[0], nums[0] + curMax, curMin = 0, 0 + total = 0 + + for num in nums: + curMax = max(curMax + num, num) + curMin = min(curMin + num, num) + total += num + globMax = max(globMax, curMax) + globMin = min(globMin, curMin) + + return max(globMax, total - globMin) if globMax > 0 else globMax +``` + +```java +public class Solution { + public int maxSubarraySumCircular(int[] nums) { + int globMax = nums[0], globMin = nums[0]; + int curMax = 0, curMin = 0, total = 0; + + for (int num : nums) { + curMax = Math.max(curMax + num, num); + curMin = Math.min(curMin + num, num); + total += num; + globMax = Math.max(globMax, curMax); + globMin = Math.min(globMin, curMin); + } + + return globMax > 0 ? Math.max(globMax, total - globMin) : globMax; + } +} +``` + +```cpp +class Solution { +public: + int maxSubarraySumCircular(vector& nums) { + int globMax = nums[0], globMin = nums[0]; + int curMax = 0, curMin = 0, total = 0; + + for (int& num : nums) { + curMax = max(curMax + num, num); + curMin = min(curMin + num, num); + total += num; + globMax = max(globMax, curMax); + globMin = min(globMin, curMin); + } + + return globMax > 0 ? max(globMax, total - globMin) : globMax; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + maxSubarraySumCircular(nums) { + let globMax = nums[0], globMin = nums[0]; + let curMax = 0, curMin = 0, total = 0; + + for (const num of nums) { + curMax = Math.max(curMax + num, num); + curMin = Math.min(curMin + num, num); + total += num; + globMax = Math.max(globMax, curMax); + globMin = Math.min(globMin, curMin); + } + + return globMax > 0 ? Math.max(globMax, total - globMin) : globMax; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. \ No newline at end of file diff --git a/articles/minimum-cost-for-tickets.md b/articles/minimum-cost-for-tickets.md index 158c186db..1b32c8980 100644 --- a/articles/minimum-cost-for-tickets.md +++ b/articles/minimum-cost-for-tickets.md @@ -145,8 +145,8 @@ class Solution: return dp[i] dp[i] = float("inf") + j = i for d, c in zip([1, 7, 30], costs): - j = i while j < len(days) and days[j] < days[i] + d: j += 1 dp[i] = min(dp[i], c + dfs(j)) @@ -175,9 +175,8 @@ public class Solution { } dp[i] = Integer.MAX_VALUE; - int idx = 0; + int idx = 0, j = i; for (int d : new int[]{1, 7, 30}) { - int j = i + 1; while (j < days.length && days[j] < days[i] + d) { j++; } @@ -199,9 +198,8 @@ private: if (dp[i] != -1) return dp[i]; dp[i] = INT_MAX; - int idx = 0; + int idx = 0, j = i; for (int d : {1, 7, 30}) { - int j = i + 1; while (j < days.size() && days[j] < days[i] + d) { j++; } @@ -234,8 +232,8 @@ class Solution { if (dp[i] !== -1) return dp[i]; dp[i] = Infinity; + let j = i; [1, 7, 30].forEach((d, idx) => { - let j = i + 1; while (j < days.length && days[j] < days[i] + d) { j++; } @@ -259,7 +257,7 @@ class Solution { --- -## 3. Dynamic Programming (Bottom-Up) - I +## 3. Dynamic Programming (Bottom-Up) ::tabs-start @@ -271,8 +269,8 @@ class Solution: for i in range(n - 1, -1, -1): dp[i] = float('inf') + j = i for d, c in zip([1, 7, 30], costs): - j = i + 1 while j < n and days[j] < days[i] + d: j += 1 dp[i] = min(dp[i], c + dp[j]) @@ -288,9 +286,8 @@ public class Solution { for (int i = n - 1; i >= 0; i--) { dp[i] = Integer.MAX_VALUE; - int idx = 0; + int idx = 0, j = i; for (int d : new int[]{1, 7, 30}) { - int j = i + 1; while (j < n && days[j] < days[i] + d) { j++; } @@ -313,8 +310,8 @@ public: for (int i = n - 1; i >= 0; i--) { dp[i] = INT_MAX; + int j = i; for (int k = 0; k < 3; ++k) { - int j = i + 1; while (j < n && days[j] < days[i] + (k == 0 ? 1 : k == 1 ? 7 : 30)) { j++; } @@ -340,8 +337,8 @@ class Solution { for (let i = n - 1; i >= 0; i--) { dp[i] = Infinity; + let j = i; [1, 7, 30].forEach((d, idx) => { - let j = i + 1; while (j < n && days[j] < days[i] + d) { j++; } @@ -363,7 +360,7 @@ class Solution { --- -## 4. Dynamic Programming (Bottom-Up) - II +## 4. Dynamic Programming (Bottom-Up) + Two Pointers ::tabs-start diff --git a/articles/n-queens-ii.md b/articles/n-queens-ii.md new file mode 100644 index 000000000..1e123c887 --- /dev/null +++ b/articles/n-queens-ii.md @@ -0,0 +1,672 @@ +## 1. Backtracking + +::tabs-start + +```python +class Solution: + def totalNQueens(self, n: int) -> int: + res = 0 + board = [["."] * n for i in range(n)] + + def backtrack(r): + nonlocal res + if r == n: + res += 1 + return + for c in range(n): + if self.isSafe(r, c, board): + board[r][c] = "Q" + backtrack(r + 1) + board[r][c] = "." + + backtrack(0) + return res + + def isSafe(self, r: int, c: int, board): + row = r - 1 + while row >= 0: + if board[row][c] == "Q": + return False + row -= 1 + + row, col = r - 1, c - 1 + while row >= 0 and col >= 0: + if board[row][col] == "Q": + return False + row -= 1 + col -= 1 + + row, col = r - 1, c + 1 + while row >= 0 and col < len(board): + if board[row][col] == "Q": + return False + row -= 1 + col += 1 + return True +``` + +```java +public class Solution { + private int res; + + public int totalNQueens(int n) { + res = 0; + char[][] board = new char[n][n]; + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { + board[i][j] = '.'; + } + } + backtrack(0, board); + return res; + } + + private void backtrack(int r, char[][] board) { + if (r == board.length) { + res++; + return; + } + for (int c = 0; c < board.length; c++) { + if (isSafe(r, c, board)) { + board[r][c] = 'Q'; + backtrack(r + 1, board); + board[r][c] = '.'; + } + } + } + + private boolean isSafe(int r, int c, char[][] board) { + for (int i = r - 1; i >= 0; i--) { + if (board[i][c] == 'Q') return false; + } + for (int i = r - 1, j = c - 1; i >= 0 && j >= 0; i--, j--) { + if (board[i][j] == 'Q') return false; + } + for (int i = r - 1, j = c + 1; i >= 0 && j < board.length; i--, j++) { + if (board[i][j] == 'Q') return false; + } + return true; + } +} +``` + +```cpp +class Solution { +public: + int totalNQueens(int n) { + int res = 0; + vector board(n, string(n, '.')); + backtrack(0, board, res); + return res; + } + + void backtrack(int r, vector& board, int& res) { + if (r == board.size()) { + res++; + return; + } + for (int c = 0; c < board.size(); c++) { + if (isSafe(r, c, board)) { + board[r][c] = 'Q'; + backtrack(r + 1, board, res); + board[r][c] = '.'; + } + } + } + + bool isSafe(int r, int c, vector& board) { + for (int i = r - 1; i >= 0; i--) { + if (board[i][c] == 'Q') return false; + } + for (int i = r - 1, j = c - 1; i >= 0 && j >= 0; i--, j--) { + if (board[i][j] == 'Q') return false; + } + for (int i = r - 1, j = c + 1; i >= 0 && j < board.size(); i--, j++) { + if (board[i][j] == 'Q') return false; + } + return true; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {number} + */ + totalNQueens(n) { + let res = 0; + let board = Array.from({length: n}, () => Array(n).fill('.')); + + const backtrack = (r) => { + if (r === n) { + res++; + return; + } + for (let c = 0; c < n; c++) { + if (this.isSafe(r, c, board)) { + board[r][c] = 'Q'; + backtrack(r + 1); + board[r][c] = '.'; + } + } + } + + backtrack(0); + return res; + } + + /** + * @param {number} r + * @param {number} c + * @param {string[][]} board + * @return {boolean} + */ + isSafe(r, c, board) { + for (let i = r - 1; i >= 0; i--) { + if (board[i][c] === 'Q') return false; + } + for (let i = r - 1, j = c - 1; i >= 0 && j >= 0; i--, j--) { + if (board[i][j] === 'Q') return false; + } + for (let i = r - 1, j = c + 1; i >= 0 && j < board.length; i--, j++) { + if (board[i][j] === 'Q') return false; + } + return true; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n!)$ +* Space complexity: $O(n ^ 2)$ + +--- + +## 2. Backtracking (Hash Set) + +::tabs-start + +```python +class Solution: + def totalNQueens(self, n: int) -> int: + col = set() + posDiag = set() + negDiag = set() + + res = 0 + def backtrack(r): + nonlocal res + if r == n: + res += 1 + return + + for c in range(n): + if c in col or (r + c) in posDiag or (r - c) in negDiag: + continue + + col.add(c) + posDiag.add(r + c) + negDiag.add(r - c) + + backtrack(r + 1) + + col.remove(c) + posDiag.remove(r + c) + negDiag.remove(r - c) + + backtrack(0) + return res +``` + +```java +public class Solution { + Set col = new HashSet<>(); + Set posDiag = new HashSet<>(); + Set negDiag = new HashSet<>(); + int res; + + public int totalNQueens(int n) { + res = 0; + backtrack(0, n); + return res; + } + + private void backtrack(int r, int n) { + if (r == n) { + res++; + return; + } + + for (int c = 0; c < n; c++) { + if (col.contains(c) || posDiag.contains(r + c) || negDiag.contains(r - c)) { + continue; + } + + col.add(c); + posDiag.add(r + c); + negDiag.add(r - c); + + backtrack(r + 1, n); + + col.remove(c); + posDiag.remove(r + c); + negDiag.remove(r - c); + } + } +} +``` + +```cpp +class Solution { +public: + unordered_set col; + unordered_set posDiag; + unordered_set negDiag; + + int totalNQueens(int n) { + int res = 0; + backtrack(0, n, res); + return res; + } + +private: + void backtrack(int r, int n, int& res) { + if (r == n) { + res++; + return; + } + + for (int c = 0; c < n; c++) { + if (col.count(c) || posDiag.count(r + c) || negDiag.count(r - c)) { + continue; + } + + col.insert(c); + posDiag.insert(r + c); + negDiag.insert(r - c); + + backtrack(r + 1, n, res); + + col.erase(c); + posDiag.erase(r + c); + negDiag.erase(r - c); + } + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {number} + */ + totalNQueens(n) { + const col = new Set(); + const posDiag = new Set(); + const negDiag = new Set(); + let res = 0; + + /** + * @param {number} r + * @return {void} + */ + function backtrack(r) { + if (r === n) { + res++; + return; + } + + for (let c = 0; c < n; c++) { + if (col.has(c) || posDiag.has(r + c) || + negDiag.has(r - c)) { + continue; + } + + col.add(c); + posDiag.add(r + c); + negDiag.add(r - c); + + backtrack(r + 1); + + col.delete(c); + posDiag.delete(r + c); + negDiag.delete(r - c); + } + } + + backtrack(0); + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n!)$ +* Space complexity: $O(n)$ + +--- + +## 3. Backtracking (Boolean Array) + +::tabs-start + +```python +class Solution: + def totalNQueens(self, n: int) -> int: + col = [False] * n + posDiag = [False] * (n * 2) + negDiag = [False] * (n * 2) + res = 0 + + def backtrack(r): + nonlocal res + if r == n: + res += 1 + return + for c in range(n): + if col[c] or posDiag[r + c] or negDiag[r - c + n]: + continue + col[c] = True + posDiag[r + c] = True + negDiag[r - c + n] = True + + backtrack(r + 1) + + col[c] = False + posDiag[r + c] = False + negDiag[r - c + n] = False + + backtrack(0) + return res +``` + +```java +public class Solution { + boolean[] col, posDiag, negDiag; + int res; + + public int totalNQueens(int n) { + col = new boolean[n]; + posDiag = new boolean[2 * n]; + negDiag = new boolean[2 * n]; + res = 0; + + backtrack(0, n); + return res; + } + + private void backtrack(int r, int n) { + if (r == n) { + res++; + return; + } + for (int c = 0; c < n; c++) { + if (col[c] || posDiag[r + c] || negDiag[r - c + n]) { + continue; + } + col[c] = true; + posDiag[r + c] = true; + negDiag[r - c + n] = true; + + backtrack(r + 1, n); + + col[c] = false; + posDiag[r + c] = false; + negDiag[r - c + n] = false; + } + } +} +``` + +```cpp +class Solution { +public: + vector board; + vector col, posDiag, negDiag; + + int totalNQueens(int n) { + col.resize(n, false); + posDiag.resize(2 * n, false); + negDiag.resize(2 * n, false); + + int res = 0; + backtrack(0, n, res); + return res; + } + + void backtrack(int r, int n, int& res) { + if (r == n) { + res++; + return; + } + for (int c = 0; c < n; c++) { + if (col[c] || posDiag[r + c] || negDiag[r - c + n]) { + continue; + } + col[c] = true; + posDiag[r + c] = true; + negDiag[r - c + n] = true; + + backtrack(r + 1, n, res); + + col[c] = false; + posDiag[r + c] = false; + negDiag[r - c + n] = false; + } + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {number} + */ + totalNQueens(n) { + const col = Array(n).fill(false); + const posDiag = Array(2 * n).fill(false); + const negDiag = Array(2 * n).fill(false); + let res = 0; + + /** + * @param {number} r + * @return {void} + */ + function backtrack(r) { + if (r === n) { + res++; + return; + } + for (let c = 0; c < n; c++) { + if (col[c] || posDiag[r + c] || negDiag[r - c + n]) { + continue; + } + col[c] = true; + posDiag[r + c] = true; + negDiag[r - c + n] = true; + + backtrack(r + 1); + + col[c] = false; + posDiag[r + c] = false; + negDiag[r - c + n] = false; + } + } + + backtrack(0); + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n!)$ +* Space complexity: $O(n)$ + +--- + +## 4. Backtracking (Bit Mask) + +::tabs-start + +```python +class Solution: + def totalNQueens(self, n: int) -> int: + col = 0 + posDiag = 0 + negDiag = 0 + res = 0 + + def backtrack(r): + nonlocal col, posDiag, negDiag, res + if r == n: + res += 1 + return + for c in range(n): + if ((col & (1 << c)) or (posDiag & (1 << (r + c))) + or (negDiag & (1 << (r - c + n)))): + continue + col ^= (1 << c) + posDiag ^= (1 << (r + c)) + negDiag ^= (1 << (r - c + n)) + + backtrack(r + 1) + + col ^= (1 << c) + posDiag ^= (1 << (r + c)) + negDiag ^= (1 << (r - c + n)) + + backtrack(0) + return res +``` + +```java +public class Solution { + private int col = 0, posDiag = 0, negDiag = 0, res = 0; + + public int totalNQueens(int n) { + res = 0; + backtrack(0, n); + return res; + } + + private void backtrack(int r, int n) { + if (r == n) { + res++; + return; + } + for (int c = 0; c < n; c++) { + if ((col & (1 << c)) > 0 || (posDiag & (1 << (r + c))) > 0 || + (negDiag & (1 << (r - c + n))) > 0) { + continue; + } + col ^= (1 << c); + posDiag ^= (1 << (r + c)); + negDiag ^= (1 << (r - c + n)); + + backtrack(r + 1, n); + + col ^= (1 << c); + posDiag ^= (1 << (r + c)); + negDiag ^= (1 << (r - c + n)); + } + } +} +``` + +```cpp +class Solution { +public: + int col = 0, posDiag = 0, negDiag = 0; + vector board; + + int totalNQueens(int n) { + int res = 0; + backtrack(0, n, res); + return res; + } + + void backtrack(int r, int n, int& res) { + if (r == n) { + res++; + return; + } + for (int c = 0; c < n; c++) { + if ((col & (1 << c)) || (posDiag & (1 << (r + c))) || + (negDiag & (1 << (r - c + n)))) { + continue; + } + col ^= (1 << c); + posDiag ^= (1 << (r + c)); + negDiag ^= (1 << (r - c + n)); + + backtrack(r + 1, n, res); + + col ^= (1 << c); + posDiag ^= (1 << (r + c)); + negDiag ^= (1 << (r - c + n)); + } + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {number} + */ + totalNQueens(n) { + let col = 0, posDiag = 0, negDiag = 0, res = 0; + + /** + * @param {number} r + * @return {void} + */ + function backtrack(r) { + if (r === n) { + res++; + return; + } + for (let c = 0; c < n; c++) { + if ((col & (1 << c)) > 0 || (posDiag & (1 << (r + c))) > 0 || + (negDiag & (1 << (r - c + n))) > 0) { + continue; + } + col ^= (1 << c); + posDiag ^= (1 << (r + c)); + negDiag ^= (1 << (r - c + n)); + + backtrack(r + 1); + + col ^= (1 << c); + posDiag ^= (1 << (r + c)); + negDiag ^= (1 << (r - c + n)); + } + } + + backtrack(0); + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n!)$ +* Space complexity: $O(n)$ for recursion stack. \ No newline at end of file diff --git a/articles/n-queens.md b/articles/n-queens.md index 550354955..523b992a6 100644 --- a/articles/n-queens.md +++ b/articles/n-queens.md @@ -4,7 +4,7 @@ ```python class Solution: - def solveNQueens(self, n: int): + def solveNQueens(self, n: int) -> List[List[str]]: res = [] board = [["."] * n for i in range(n)] @@ -681,7 +681,7 @@ class Solution { ```python class Solution: - def solveNQueens(self, n: int): + def solveNQueens(self, n: int) -> List[List[str]]: col = [False] * n posDiag = [False] * (n * 2) negDiag = [False] * (n * 2) @@ -836,7 +836,7 @@ class Solution { negDiag[r - c + n] = true; board[r][c] = 'Q'; - backtrack(r + 1, n); + backtrack(r + 1); col[c] = false; posDiag[r + c] = false; @@ -1001,7 +1001,7 @@ class Solution { ```python class Solution: - def solveNQueens(self, n: int): + def solveNQueens(self, n: int) -> List[List[str]]: col = 0 posDiag = 0 negDiag = 0 @@ -1153,7 +1153,7 @@ class Solution { negDiag ^= (1 << (r - c + n)); board[r][c] = 'Q'; - backtrack(r + 1, n); + backtrack(r + 1); col ^= (1 << c); posDiag ^= (1 << (r + c)); diff --git a/articles/online-stock-span.md b/articles/online-stock-span.md new file mode 100644 index 000000000..9419b89b6 --- /dev/null +++ b/articles/online-stock-span.md @@ -0,0 +1,173 @@ +## 1. Brute Force + +::tabs-start + +```python +class StockSpanner: + + def __init__(self): + self.arr = [] + + def next(self, price: int) -> int: + self.arr.append(price) + i = len(self.arr) - 2 + while i >= 0 and self.arr[i] <= price: + i -= 1 + return len(self.arr) - i - 1 +``` + +```java +public class StockSpanner { + private List arr; + + public StockSpanner() { + arr = new ArrayList<>(); + } + + public int next(int price) { + arr.add(price); + int i = arr.size() - 2; + while (i >= 0 && arr.get(i) <= price) { + i--; + } + return arr.size() - i - 1; + } +} +``` + +```cpp +class StockSpanner { + vector arr; + +public: + StockSpanner() {} + + int next(int price) { + arr.push_back(price); + int i = arr.size() - 2; + while (i >= 0 && arr[i] <= price) { + i--; + } + return arr.size() - i - 1; + } +}; +``` + +```javascript +class StockSpanner { + constructor() { + this.arr = []; + } + + /** + * @param {number} price + * @return {number} + */ + next(price) { + this.arr.push(price); + let i = this.arr.length - 2; + while (i >= 0 && this.arr[i] <= price) { + i--; + } + return this.arr.length - i - 1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n)$ + +> Where $n$ is the number of function calls. + +--- + +## 2. Monotonic Decreasing Stack + +::tabs-start + +```python +class StockSpanner: + + def __init__(self): + self.stack = [] # pair: (price, span) + + def next(self, price: int) -> int: + span = 1 + while self.stack and self.stack[-1][0] <= price: + span += self.stack[-1][1] + self.stack.pop() + self.stack.append((price, span)) + return span +``` + +```java +public class StockSpanner { + private Stack stack; // pair: [price, span] + + public StockSpanner() { + stack = new Stack<>(); + } + + public int next(int price) { + int span = 1; + while (!stack.isEmpty() && stack.peek()[0] <= price) { + span += stack.pop()[1]; + } + stack.push(new int[] {price, span}); + return span; + } +} +``` + +```cpp +class StockSpanner { + stack> stack; // pair: (price, span) + +public: + StockSpanner() {} + + int next(int price) { + int span = 1; + while (!stack.empty() && stack.top().first <= price) { + span += stack.top().second; + stack.pop(); + } + stack.push({price, span}); + return span; + } +}; +``` + +```javascript +class StockSpanner { + constructor() { + this.stack = []; // pair: [price, span] + } + + /** + * @param {number} price + * @return {number} + */ + next(price) { + let span = 1; + while (this.stack.length && this.stack[this.stack.length - 1][0] <= price) { + span += this.stack.pop()[1]; + } + this.stack.push([price, span]); + return span; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +> Where $n$ is the number of function calls. \ No newline at end of file diff --git a/articles/partition-to-k-equal-sum-subsets.md b/articles/partition-to-k-equal-sum-subsets.md new file mode 100644 index 000000000..260fe7e37 --- /dev/null +++ b/articles/partition-to-k-equal-sum-subsets.md @@ -0,0 +1,772 @@ +## 1. Backtracking + +::tabs-start + +```python +class Solution: + def canPartitionKSubsets(self, nums: List[int], k: int) -> bool: + if sum(nums) % k != 0: + return False + + nums.sort(reverse=True) + target = sum(nums) // k + used = [False] * len(nums) + + def backtrack(i, k, subsetSum): + if k == 0: + return True + if subsetSum == target: + return backtrack(0, k - 1, 0) + for j in range(i, len(nums)): + if used[j] or subsetSum + nums[j] > target: + continue + used[j] = True + if backtrack(j + 1, k, subsetSum + nums[j]): + return True + used[j] = False + return False + + return backtrack(0, k, 0) +``` + +```java +public class Solution { + private boolean[] used; + private int target; + private int n; + + public boolean canPartitionKSubsets(int[] nums, int k) { + int sum = 0; + for (int num : nums) sum += num; + if (sum % k != 0) return false; + + this.target = sum / k; + this.n = nums.length; + Arrays.sort(nums); + for (int i = 0; i < n / 2; i++) { + int tmp = nums[i]; + nums[i] = nums[n - i - 1]; + nums[n - i - 1] = tmp; + } + used = new boolean[n]; + return backtrack(nums, k, 0, 0); + } + + private boolean backtrack(int[] nums, int k, int currentSum, int start) { + if (k == 0) return true; + if (currentSum == target) return backtrack(nums, k - 1, 0, 0); + + for (int i = start; i < n; i++) { + if (used[i] || currentSum + nums[i] > target) continue; + used[i] = true; + if (backtrack(nums, k, currentSum + nums[i], i + 1)) return true; + used[i] = false; + } + return false; + } +} +``` + +```cpp +class Solution { + vector used; + int target; + +public: + bool canPartitionKSubsets(vector& nums, int k) { + int sum = accumulate(nums.begin(), nums.end(), 0); + if (sum % k != 0) return false; + + target = sum / k; + sort(nums.rbegin(), nums.rend()); + used.assign(nums.size(), false); + return backtrack(nums, k, 0, 0); + } + +private: + bool backtrack(vector& nums, int k, int currentSum, int start) { + if (k == 0) return true; + if (currentSum == target) return backtrack(nums, k - 1, 0, 0); + + for (int i = start; i < nums.size(); i++) { + if (used[i] || currentSum + nums[i] > target) continue; + used[i] = true; + if (backtrack(nums, k, currentSum + nums[i], i + 1)) return true; + used[i] = false; + } + return false; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {boolean} + */ + canPartitionKSubsets(nums, k) { + const sum = nums.reduce((a, b) => a + b, 0); + if (sum % k !== 0) return false; + + const target = sum / k; + nums.sort((a, b) => b - a); + const used = Array(nums.length).fill(false); + + const backtrack = (i, k, subsetSum) => { + if (k === 0) return true; + if (subsetSum === target) return backtrack(0, k - 1, 0); + + for (let j = i; j < nums.length; j++) { + if (used[j] || subsetSum + nums[j] > target) continue; + used[j] = true; + if (backtrack(j + 1, k, subsetSum + nums[j])) return true; + used[j] = false; + } + return false; + }; + + return backtrack(0, k, 0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(k * 2 ^ n)$ +* Space complexity: $O(n)$ + +> Where $n$ is the size of the array $nums$ and $k$ is the number of subsets. + +--- + +## 2. Backtracking (Pruning) + +::tabs-start + +```python +class Solution: + def canPartitionKSubsets(self, nums: List[int], k: int) -> bool: + total = sum(nums) + if total % k != 0: + return False + + nums.sort(reverse=True) + target = total // k + used = [False] * len(nums) + + def backtrack(i, k, subsetSum): + if k == 0: + return True + if subsetSum == target: + return backtrack(0, k - 1, 0) + for j in range(i, len(nums)): + if used[j] or subsetSum + nums[j] > target: + continue + used[j] = True + if backtrack(j + 1, k, subsetSum + nums[j]): + return True + used[j] = False + + if subsetSum == 0: # Pruning + return False + + return False + + return backtrack(0, k, 0) +``` + +```java +public class Solution { + private boolean[] used; + private int target; + private int n; + + public boolean canPartitionKSubsets(int[] nums, int k) { + int sum = 0; + for (int num : nums) sum += num; + if (sum % k != 0) return false; + + this.target = sum / k; + this.n = nums.length; + Arrays.sort(nums); + for (int i = 0; i < n / 2; i++) { + int tmp = nums[i]; + nums[i] = nums[n - i - 1]; + nums[n - i - 1] = tmp; + } + used = new boolean[n]; + return backtrack(nums, k, 0, 0); + } + + private boolean backtrack(int[] nums, int k, int currentSum, int start) { + if (k == 0) return true; + if (currentSum == target) return backtrack(nums, k - 1, 0, 0); + + for (int i = start; i < n; i++) { + if (used[i] || currentSum + nums[i] > target) continue; + used[i] = true; + if (backtrack(nums, k, currentSum + nums[i], i + 1)) return true; + used[i] = false; + if (currentSum == 0) { // Pruning + return false; + } + } + return false; + } +} +``` + +```cpp +class Solution { + vector used; + int target; + +public: + bool canPartitionKSubsets(vector& nums, int k) { + int sum = accumulate(nums.begin(), nums.end(), 0); + if (sum % k != 0) return false; + + target = sum / k; + sort(nums.rbegin(), nums.rend()); + used.assign(nums.size(), false); + return backtrack(nums, k, 0, 0); + } + +private: + bool backtrack(vector& nums, int k, int currentSum, int start) { + if (k == 0) return true; + if (currentSum == target) return backtrack(nums, k - 1, 0, 0); + + for (int i = start; i < nums.size(); i++) { + if (used[i] || currentSum + nums[i] > target) continue; + used[i] = true; + if (backtrack(nums, k, currentSum + nums[i], i + 1)) return true; + used[i] = false; + if (currentSum == 0) { // Pruning + return false; + } + } + return false; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {boolean} + */ + canPartitionKSubsets(nums, k) { + const sum = nums.reduce((a, b) => a + b, 0); + if (sum % k !== 0) return false; + + const target = sum / k; + nums.sort((a, b) => b - a); + const used = Array(nums.length).fill(false); + + const backtrack = (i, k, subsetSum) => { + if (k === 0) return true; + if (subsetSum === target) return backtrack(0, k - 1, 0); + + for (let j = i; j < nums.length; j++) { + if (used[j] || subsetSum + nums[j] > target) continue; + used[j] = true; + if (backtrack(j + 1, k, subsetSum + nums[j])) return true; + used[j] = false; + if (subsetSum === 0) { // Pruning + return false; + } + } + return false; + }; + + return backtrack(0, k, 0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(k * 2 ^ n)$ +* Space complexity: $O(n)$ + +> Where $n$ is the size of the array $nums$ and $k$ is the number of subsets. + +--- + +## 3. Backtracking (Bit Mask + Pruning) + +::tabs-start + +```python +class Solution: + def canPartitionKSubsets(self, nums: List[int], k: int) -> bool: + total = sum(nums) + if total % k != 0: + return False + + nums.sort(reverse=True) + target = total // k + n = len(nums) + + def backtrack(i, k, subsetSum, mask): + if k == 0: + return True + if subsetSum == target: + return backtrack(0, k - 1, 0, mask) + for j in range(i, n): + if (mask & (1 << j)) == 0 or subsetSum + nums[j] > target: + continue + if backtrack(j + 1, k, subsetSum + nums[j], mask ^ (1 << j)): + return True + if subsetSum == 0: + return False + return False + + return backtrack(0, k, 0, (1 << n) - 1) +``` + +```java +public class Solution { + private int target; + private int n; + + public boolean canPartitionKSubsets(int[] nums, int k) { + int total = 0; + for (int num : nums) total += num; + if (total % k != 0) return false; + + this.target = total / k; + this.n = nums.length; + Arrays.sort(nums); + reverse(nums); + + return backtrack(nums, 0, k, 0, (1 << this.n) - 1); + } + + private boolean backtrack(int[] nums, int i, int k, int subsetSum, int mask) { + if (k == 0) return true; + if (subsetSum == target) return backtrack(nums, 0, k - 1, 0, mask); + for (int j = i; j < n; j++) { + if ((mask & (1 << j)) == 0 || subsetSum + nums[j] > target) continue; + if (backtrack(nums, j + 1, k, subsetSum + nums[j], mask ^ (1 << j))) { + return true; + } + if (subsetSum == 0) return false; + } + return false; + } + + private void reverse(int[] nums) { + int l = 0, r = n - 1; + while (l < r) { + int temp = nums[l]; + nums[l++] = nums[r]; + nums[r--] = temp; + } + } +} +``` + +```cpp +class Solution { + int target, n; + +public: + bool canPartitionKSubsets(vector& nums, int k) { + int total = accumulate(nums.begin(), nums.end(), 0); + if (total % k != 0) return false; + + target = total / k; + n = nums.size(); + sort(nums.rbegin(), nums.rend()); + return backtrack(nums, 0, k, 0, (1 << n) - 1); + } + +private: + bool backtrack(vector& nums, int i, int k, int subsetSum, int mask) { + if (k == 0) return true; + if (subsetSum == target) return backtrack(nums, 0, k - 1, 0, mask); + for (int j = i; j < nums.size(); j++) { + if ((mask & (1 << j)) == 0 || subsetSum + nums[j] > target) continue; + if (backtrack(nums, j + 1, k, subsetSum + nums[j], mask ^ (1 << j))) { + return true; + } + if (subsetSum == 0) return false; + } + return false; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {boolean} + */ + canPartitionKSubsets(nums, k) { + const total = nums.reduce((a, b) => a + b, 0); + if (total % k !== 0) return false; + + const target = total / k; + const n = nums.length; + nums.sort((a, b) => b - a); + + const backtrack = (i, k, subsetSum, mask) => { + if (k === 0) return true; + if (subsetSum === target) return backtrack(0, k - 1, 0, mask); + for (let j = i; j < n; j++) { + if ((mask & (1 << j)) === 0 || subsetSum + nums[j] > target) { + continue; + } + if (backtrack(j + 1, k, subsetSum + nums[j], mask ^ (1 << j))) { + return true; + } + if (subsetSum === 0) return false; + } + return false; + }; + + return backtrack(0, k, 0, (1 << n) - 1); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(k * 2 ^ n)$ +* Space complexity: + * $O(1)$ or $O(n)$ space depending on the sorting algorithm. + * $O(n)$ for the recursion stack. + +> Where $n$ is the size of the array $nums$ and $k$ is the number of subsets. + +--- + +## 4. Dynamic Programming (Top-Down) + Bit Mask + +::tabs-start + +```python +class Solution: + def canPartitionKSubsets(self, nums: List[int], k: int) -> bool: + total = sum(nums) + if total % k != 0: + return False + + nums.sort(reverse=True) + target = total // k + n = len(nums) + dp = [None] * (1 << n) + + def backtrack(i, k, subsetSum, mask): + if dp[mask] != None: + return dp[mask] + if k == 0: + dp[mask] = True + return True + if subsetSum == target: + dp[mask] = backtrack(0, k - 1, 0, mask) + return dp[mask] + + for j in range(i, n): + if (mask & (1 << j)) == 0 or subsetSum + nums[j] > target: + continue + if backtrack(j + 1, k, subsetSum + nums[j], mask ^ (1 << j)): + dp[mask] = True + return True + if subsetSum == 0: + dp[mask] = False + return dp[mask] + dp[mask] = False + return False + + return backtrack(0, k, 0, (1 << n) - 1) +``` + +```java +public class Solution { + private int target; + private int n; + private Boolean[] dp; + + public boolean canPartitionKSubsets(int[] nums, int k) { + int total = 0; + for (int num : nums) total += num; + if (total % k != 0) return false; + + this.target = total / k; + this.n = nums.length; + Arrays.sort(nums); + reverse(nums); + dp = new Boolean[1 << this.n]; + + return backtrack(nums, 0, k, 0, (1 << this.n) - 1); + } + + private boolean backtrack(int[] nums, int i, int k, int subsetSum, int mask) { + if (dp[mask] != null) return dp[mask]; + if (k == 0) { + dp[mask] = true; + return dp[mask]; + } + if (subsetSum == target) { + dp[mask] = backtrack(nums, 0, k - 1, 0, mask); + return dp[mask]; + } + for (int j = i; j < n; j++) { + if ((mask & (1 << j)) == 0 || subsetSum + nums[j] > target) continue; + if (backtrack(nums, j + 1, k, subsetSum + nums[j], mask ^ (1 << j))) { + dp[mask] = true; + return true; + } + if (subsetSum == 0) { + dp[mask] = false; + return false; + } + } + dp[mask] = false; + return false; + } + + private void reverse(int[] nums) { + int l = 0, r = n - 1; + while (l < r) { + int temp = nums[l]; + nums[l++] = nums[r]; + nums[r--] = temp; + } + } +} +``` + +```cpp +class Solution { + int target, n; + vector dp; + +public: + bool canPartitionKSubsets(vector& nums, int k) { + int total = accumulate(nums.begin(), nums.end(), 0); + if (total % k != 0) return false; + + target = total / k; + n = nums.size(); + dp.assign(1 << n, -1); + sort(nums.rbegin(), nums.rend()); + return backtrack(nums, 0, k, 0, (1 << n) - 1); + } + +private: + int backtrack(vector& nums, int i, int k, int subsetSum, int mask) { + if (dp[mask] != -1) return dp[mask]; + if (k == 0) { + dp[mask] = 1; + return 1; + } + if (subsetSum == target) { + dp[mask] = backtrack(nums, 0, k - 1, 0, mask); + return dp[mask]; + } + for (int j = i; j < nums.size(); j++) { + if ((mask & (1 << j)) == 0 || subsetSum + nums[j] > target) continue; + if (backtrack(nums, j + 1, k, subsetSum + nums[j], mask ^ (1 << j))) { + dp[mask] = 1; + return 1; + } + if (subsetSum == 0) { + break; + } + } + dp[mask] = 0; + return 0; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {boolean} + */ + canPartitionKSubsets(nums, k) { + const total = nums.reduce((a, b) => a + b, 0); + if (total % k !== 0) return false; + + const target = total / k; + const n = nums.length; + const dp = new Array(1 << n); + nums.sort((a, b) => b - a); + + const backtrack = (i, k, subsetSum, mask) => { + if (dp[mask] !== undefined) return dp[mask]; + if (k === 0) { + dp[mask] = true; + return true; + } + if (subsetSum === target) { + dp[mask] = backtrack(0, k - 1, 0, mask); + return dp[mask]; + } + for (let j = i; j < n; j++) { + if ((mask & (1 << j)) === 0 || subsetSum + nums[j] > target) { + continue; + } + if (backtrack(j + 1, k, subsetSum + nums[j], mask ^ (1 << j))) { + dp[mask] = true; + return true; + } + if (subsetSum === 0) break; + } + dp[mask] = false; + return false; + }; + + return backtrack(0, k, 0, (1 << n) - 1); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * 2 ^ n)$ +* Space complexity: $O(2 ^ n)$ + +--- + +## 5. Dynamic Programming (Bottom-Up) + Bit Mask + +::tabs-start + +```python +class Solution: + def canPartitionKSubsets(self, nums: List[int], k: int) -> bool: + total = sum(nums) + if total % k != 0: + return False + + target = total // k + n = len(nums) + N = 1 << n + dp = [0] + [-1] * (N - 1) + + for mask in range(N): + if dp[mask] == -1: + continue + for i in range(n): + if (mask & (1 << i)) == 0 and dp[mask] + nums[i] <= target: + dp[mask | (1 << i)] = (dp[mask] + nums[i]) % target + + return dp[N - 1] == 0 +``` + +```java +public class Solution { + public boolean canPartitionKSubsets(int[] nums, int k) { + int total = 0; + for (int num : nums) total += num; + if (total % k != 0) return false; + + int target = total / k; + int n = nums.length; + int N = 1 << n; + int[] dp = new int[N]; + Arrays.fill(dp, -1); + dp[0] = 0; + + for (int mask = 0; mask < N; mask++) { + if (dp[mask] == -1) continue; + for (int i = 0; i < n; i++) { + if ((mask & (1 << i)) == 0 && dp[mask] + nums[i] <= target) { + dp[mask | (1 << i)] = (dp[mask] + nums[i]) % target; + } + } + } + + return dp[N - 1] == 0; + } +} +``` + +```cpp +class Solution { +public: + bool canPartitionKSubsets(vector& nums, int k) { + int total = accumulate(nums.begin(), nums.end(), 0); + if (total % k != 0) return false; + + int target = total / k; + int n = nums.size(); + int N = 1 << n; + vector dp(N, -1); + dp[0] = 0; + + for (int mask = 0; mask < N; mask++) { + if (dp[mask] == -1) continue; + for (int i = 0; i < n; i++) { + if ((mask & (1 << i)) == 0 && dp[mask] + nums[i] <= target) { + dp[mask | (1 << i)] = (dp[mask] + nums[i]) % target; + } + } + } + + return dp[N - 1] == 0; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {boolean} + */ + canPartitionKSubsets(nums, k) { + const total = nums.reduce((a, b) => a + b, 0); + if (total % k !== 0) return false; + + const target = total / k; + nums.sort((a, b) => b - a); + + const n = nums.length; + const N = 1 << n; + const dp = new Array(N).fill(-1); + dp[0] = 0; + + for (let mask = 0; mask < N; mask++) { + if (dp[mask] === -1) continue; + for (let i = 0; i < n; i++) { + if ((mask & (1 << i)) === 0 && dp[mask] + nums[i] <= target) { + dp[mask | (1 << i)] = (dp[mask] + nums[i]) % target; + } + } + } + + return dp[N - 1] === 0; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * 2 ^ n)$ +* Space complexity: $O(2 ^ n)$ \ No newline at end of file diff --git a/articles/reverse-linked-list-ii.md b/articles/reverse-linked-list-ii.md new file mode 100644 index 000000000..5a3d69df7 --- /dev/null +++ b/articles/reverse-linked-list-ii.md @@ -0,0 +1,707 @@ +## 1. Recursion - I + +::tabs-start + +```python +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, val=0, next=None): +# self.val = val +# self.next = next +class Solution: + def reverseBetween(self, head: Optional[ListNode], left: int, right: int) -> Optional[ListNode]: + dummy = ListNode(0) + dummy.next = head + prev = dummy + for _ in range(left - 1): + prev = prev.next + + sublist_head = prev.next + sublist_tail = sublist_head + for _ in range(right - left): + sublist_tail = sublist_tail.next + + next_node = sublist_tail.next + sublist_tail.next = None + reversed_sublist = self.reverseList(sublist_head) + prev.next = reversed_sublist + sublist_head.next = next_node + + return dummy.next + + def reverseList(self, head: Optional[ListNode]) -> Optional[ListNode]: + if not head: + return None + + newHead = head + if head.next: + newHead = self.reverseList(head.next) + head.next.next = head + head.next = None + + return newHead +``` + +```java +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode() {} + * ListNode(int val) { this.val = val; } + * ListNode(int val, ListNode next) { this.val = val; this.next = next; } + * } + */ +public class Solution { + public ListNode reverseBetween(ListNode head, int left, int right) { + ListNode dummy = new ListNode(0); + dummy.next = head; + ListNode prev = dummy; + + for (int i = 0; i < left - 1; i++) { + prev = prev.next; + } + + ListNode sublistHead = prev.next; + ListNode sublistTail = sublistHead; + for (int i = 0; i < right - left; i++) { + sublistTail = sublistTail.next; + } + + ListNode nextNode = sublistTail.next; + sublistTail.next = null; + prev.next = reverseList(sublistHead); + sublistHead.next = nextNode; + + return dummy.next; + } + + private ListNode reverseList(ListNode head) { + if (head == null || head.next == null) { + return head; + } + + ListNode newHead = reverseList(head.next); + head.next.next = head; + head.next = null; + + return newHead; + } +} +``` + +```cpp +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode() : val(0), next(nullptr) {} + * ListNode(int x) : val(x), next(nullptr) {} + * ListNode(int x, ListNode *next) : val(x), next(next) {} + * }; + */ +class Solution { +public: + ListNode* reverseBetween(ListNode* head, int left, int right) { + ListNode dummy(0); + dummy.next = head; + ListNode* prev = &dummy; + + for (int i = 0; i < left - 1; ++i) { + prev = prev->next; + } + + ListNode* sublistHead = prev->next; + ListNode* sublistTail = sublistHead; + for (int i = 0; i < right - left; ++i) { + sublistTail = sublistTail->next; + } + + ListNode* nextNode = sublistTail->next; + sublistTail->next = nullptr; + prev->next = reverseList(sublistHead); + sublistHead->next = nextNode; + + return dummy.next; + } + +private: + ListNode* reverseList(ListNode* head) { + if (!head || !head->next) { + return head; + } + + ListNode* newHead = reverseList(head->next); + head->next->next = head; + head->next = nullptr; + + return newHead; + } +}; +``` + +```javascript +/** + * Definition for singly-linked list. + * class ListNode { + * constructor(val = 0, next = null) { + * this.val = val; + * this.next = next; + * } + * } + */ +class Solution { + /** + * @param {ListNode} head + * @param {number} left + * @param {number} right + * @return {ListNode} + */ + reverseBetween(head, left, right) { + const reverseList = (head) => { + if (!head || !head.next) { + return head; + } + + const newHead = reverseList(head.next); + head.next.next = head; + head.next = null; + + return newHead; + }; + + const dummy = new ListNode(0, head); + let prev = dummy; + for (let i = 0; i < left - 1; i++) { + prev = prev.next; + } + + const sublistHead = prev.next; + let sublistTail = sublistHead; + for (let i = 0; i < right - left; i++) { + sublistTail = sublistTail.next; + } + + const nextNode = sublistTail.next; + sublistTail.next = null; + prev.next = reverseList(sublistHead); + sublistHead.next = nextNode; + + return dummy.next; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ for recursion stack. + +--- + +## 2. Recursion - II + +::tabs-start + +```python +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, val=0, next=None): +# self.val = val +# self.next = next +class Solution: + def reverseBetween(self, head: Optional[ListNode], left: int, right: int) -> Optional[ListNode]: + def reverseList(node, n): + if n == 1: + return node, node.next + new_head, next_node = reverseList(node.next, n - 1) + node.next.next = node + node.next = next_node + return new_head, next_node + + if left == 1: + new_head, _ = reverseList(head, right) + return new_head + + head.next = self.reverseBetween(head.next, left - 1, right - 1) + return head +``` + +```java +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode() {} + * ListNode(int val) { this.val = val; } + * ListNode(int val, ListNode next) { this.val = val; this.next = next; } + * } + */ +public class Solution { + private ListNode[] reverseList(ListNode node, int n) { + if (n == 1) { + return new ListNode[] { node, node.next }; + } + ListNode[] result = reverseList(node.next, n - 1); + node.next.next = node; + node.next = result[1]; + return new ListNode[] { result[0], node.next }; + } + + public ListNode reverseBetween(ListNode head, int left, int right) { + if (left == 1) { + return reverseList(head, right)[0]; + } + head.next = reverseBetween(head.next, left - 1, right - 1); + return head; + } +} +``` + +```cpp +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode() : val(0), next(nullptr) {} + * ListNode(int x) : val(x), next(nullptr) {} + * ListNode(int x, ListNode *next) : val(x), next(next) {} + * }; + */ +class Solution { +private: + pair reverseList(ListNode* node, int n) { + if (n == 1) { + return {node, node->next}; + } + auto result = reverseList(node->next, n - 1); + node->next->next = node; + node->next = result.second; + return {result.first, node->next}; + } + +public: + ListNode* reverseBetween(ListNode* head, int left, int right) { + if (left == 1) { + return reverseList(head, right).first; + } + head->next = reverseBetween(head->next, left - 1, right - 1); + return head; + } +}; +``` + +```javascript +/** + * Definition for singly-linked list. + * class ListNode { + * constructor(val = 0, next = null) { + * this.val = val; + * this.next = next; + * } + * } + */ +class Solution { + /** + * @param {ListNode} head + * @param {number} left + * @param {number} right + * @return {ListNode} + */ + reverseBetween(head, left, right) { + const reverseList = (node, n) => { + if (n === 1) { + return [node, node.next]; + } + const [newHead, nextNode] = reverseList(node.next, n - 1); + node.next.next = node; + node.next = nextNode; + return [newHead, nextNode]; + }; + + if (left === 1) { + return reverseList(head, right)[0]; + } + head.next = this.reverseBetween(head.next, left - 1, right - 1); + return head; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ for recursion stack. + +--- + +## 3. Iteration - I + +::tabs-start + +```python +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, val=0, next=None): +# self.val = val +# self.next = next +class Solution: + def reverseBetween(self, head: Optional[ListNode], left: int, right: int) -> Optional[ListNode]: + dummy = ListNode(0) + dummy.next = head + prev = dummy + for _ in range(left - 1): + prev = prev.next + + sublist_head = prev.next + sublist_tail = sublist_head + for _ in range(right - left): + sublist_tail = sublist_tail.next + + next_node = sublist_tail.next + sublist_tail.next = None + reversed_sublist = self.reverseList(sublist_head) + prev.next = reversed_sublist + sublist_head.next = next_node + + return dummy.next + + def reverseList(self, head: Optional[ListNode]) -> Optional[ListNode]: + prev, curr = None, head + + while curr: + temp = curr.next + curr.next = prev + prev = curr + curr = temp + return prev +``` + +```java +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode() {} + * ListNode(int val) { this.val = val; } + * ListNode(int val, ListNode next) { this.val = val; this.next = next; } + * } + */ +public class Solution { + public ListNode reverseBetween(ListNode head, int left, int right) { + ListNode dummy = new ListNode(0); + dummy.next = head; + ListNode prev = dummy; + + for (int i = 0; i < left - 1; i++) { + prev = prev.next; + } + + ListNode sublistHead = prev.next; + ListNode sublistTail = sublistHead; + for (int i = 0; i < right - left; i++) { + sublistTail = sublistTail.next; + } + + ListNode nextNode = sublistTail.next; + sublistTail.next = null; + prev.next = reverseList(sublistHead); + sublistHead.next = nextNode; + + return dummy.next; + } + + private ListNode reverseList(ListNode head) { + ListNode prev = null; + ListNode curr = head; + + while (curr != null) { + ListNode temp = curr.next; + curr.next = prev; + prev = curr; + curr = temp; + } + return prev; + } +} +``` + +```cpp +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode() : val(0), next(nullptr) {} + * ListNode(int x) : val(x), next(nullptr) {} + * ListNode(int x, ListNode *next) : val(x), next(next) {} + * }; + */ +class Solution { +public: + ListNode* reverseBetween(ListNode* head, int left, int right) { + ListNode dummy(0); + dummy.next = head; + ListNode* prev = &dummy; + + for (int i = 0; i < left - 1; ++i) { + prev = prev->next; + } + + ListNode* sublistHead = prev->next; + ListNode* sublistTail = sublistHead; + for (int i = 0; i < right - left; ++i) { + sublistTail = sublistTail->next; + } + + ListNode* nextNode = sublistTail->next; + sublistTail->next = nullptr; + prev->next = reverseList(sublistHead); + sublistHead->next = nextNode; + + return dummy.next; + } + +private: + ListNode* reverseList(ListNode* head) { + ListNode* prev = nullptr; + ListNode* curr = head; + + while (curr) { + ListNode* temp = curr->next; + curr->next = prev; + prev = curr; + curr = temp; + } + return prev; + } +}; +``` + +```javascript +/** + * Definition for singly-linked list. + * class ListNode { + * constructor(val = 0, next = null) { + * this.val = val; + * this.next = next; + * } + * } + */ +class Solution { + /** + * @param {ListNode} head + * @param {number} left + * @param {number} right + * @return {ListNode} + */ + reverseBetween(head, left, right) { + const reverseList = (head) => { + let prev = null; + let curr = head; + + while (curr) { + let temp = curr.next; + curr.next = prev; + prev = curr; + curr = temp; + } + return prev; + }; + + const dummy = new ListNode(0, head); + let prev = dummy; + for (let i = 0; i < left - 1; i++) { + prev = prev.next; + } + + const sublistHead = prev.next; + let sublistTail = sublistHead; + for (let i = 0; i < right - left; i++) { + sublistTail = sublistTail.next; + } + + const nextNode = sublistTail.next; + sublistTail.next = null; + prev.next = reverseList(sublistHead); + sublistHead.next = nextNode; + + return dummy.next; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. + +--- + +## 4. Iteration - II + +::tabs-start + +```python +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, val=0, next=None): +# self.val = val +# self.next = next +class Solution: + def reverseBetween(self, head: Optional[ListNode], left: int, right: int) -> Optional[ListNode]: + dummy = ListNode(0, head) + leftPrev, cur = dummy, head + + for _ in range(left - 1): + leftPrev, cur = cur, cur.next + + prev = None + for _ in range(right - left + 1): + tmpNext = cur.next + cur.next = prev + prev, cur = cur, tmpNext + + leftPrev.next.next = cur + leftPrev.next = prev + + return dummy.next +``` + +```java +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode() {} + * ListNode(int val) { this.val = val; } + * ListNode(int val, ListNode next) { this.val = val; this.next = next; } + * } + */ +public class Solution { + public ListNode reverseBetween(ListNode head, int left, int right) { + ListNode dummy = new ListNode(0); + dummy.next = head; + ListNode leftPrev = dummy, cur = head; + + for (int i = 0; i < left - 1; i++) { + leftPrev = cur; + cur = cur.next; + } + + ListNode prev = null; + for (int i = 0; i < right - left + 1; i++) { + ListNode tmpNext = cur.next; + cur.next = prev; + prev = cur; + cur = tmpNext; + } + + leftPrev.next.next = cur; + leftPrev.next = prev; + + return dummy.next; + } +} +``` + +```cpp +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode() : val(0), next(nullptr) {} + * ListNode(int x) : val(x), next(nullptr) {} + * ListNode(int x, ListNode *next) : val(x), next(next) {} + * }; + */ +class Solution { +public: + ListNode* reverseBetween(ListNode* head, int left, int right) { + ListNode dummy(0); + dummy.next = head; + ListNode* leftPrev = &dummy; + ListNode* cur = head; + + for (int i = 0; i < left - 1; ++i) { + leftPrev = cur; + cur = cur->next; + } + + ListNode* prev = nullptr; + for (int i = 0; i < right - left + 1; ++i) { + ListNode* tmpNext = cur->next; + cur->next = prev; + prev = cur; + cur = tmpNext; + } + + leftPrev->next->next = cur; + leftPrev->next = prev; + + return dummy.next; + } +}; +``` + +```javascript +/** + * Definition for singly-linked list. + * class ListNode { + * constructor(val = 0, next = null) { + * this.val = val; + * this.next = next; + * } + * } + */ +class Solution { + /** + * @param {ListNode} head + * @param {number} left + * @param {number} right + * @return {ListNode} + */ + reverseBetween(head, left, right) { + const dummy = new ListNode(0, head); + let leftPrev = dummy, cur = head; + + for (let i = 0; i < left - 1; i++) { + leftPrev = cur; + cur = cur.next; + } + + let prev = null; + for (let i = 0; i < right - left + 1; i++) { + const tmpNext = cur.next; + cur.next = prev; + prev = cur; + cur = tmpNext; + } + + leftPrev.next.next = cur; + leftPrev.next = prev; + + return dummy.next; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. \ No newline at end of file diff --git a/articles/search-in-rotated-sorted-array-ii.md b/articles/search-in-rotated-sorted-array-ii.md new file mode 100644 index 000000000..d77b4c923 --- /dev/null +++ b/articles/search-in-rotated-sorted-array-ii.md @@ -0,0 +1,207 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def search(self, nums: List[int], target: int) -> bool: + return target in nums +``` + +```java +public class Solution { + public boolean search(int[] nums, int target) { + for (int num : nums) { + if (num == target) { + return true; + } + } + return false; + } +} +``` + +```cpp +class Solution { +public: + bool search(vector& nums, int target) { + for (int& num : nums) { + if (num == target) { + return true; + } + } + return false; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} target + * @return {boolean} + */ + search(nums, target) { + for (let num of nums) { + if (num === target) { + return true; + } + } + return false; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ + +--- + +## 2. Binary Search + +::tabs-start + +```python +class Solution: + def search(self, nums: List[int], target: int) -> bool: + l, r = 0, len(nums) - 1 + while l <= r: + m = l + (r - l) // 2 + if nums[m] == target: + return True + + if nums[l] < nums[m]: # Left portion + if nums[l] <= target < nums[m]: + r = m - 1 + else: + l = m + 1 + elif nums[l] > nums[m]: # Right portion + if nums[m] < target <= nums[r]: + l = m + 1 + else: + r = m - 1 + else: + l += 1 + + return False +``` + +```java +public class Solution { + public boolean search(int[] nums, int target) { + int l = 0, r = nums.length - 1; + + while (l <= r) { + int m = l + (r - l) / 2; + + if (nums[m] == target) { + return true; + } + + if (nums[l] < nums[m]) { // Left portion + if (nums[l] <= target && target < nums[m]) { + r = m - 1; + } else { + l = m + 1; + } + } else if (nums[l] > nums[m]) { // Right portion + if (nums[m] < target && target <= nums[r]) { + l = m + 1; + } else { + r = m - 1; + } + } else { + l++; + } + } + + return false; + } +} +``` + +```cpp +class Solution { +public: + bool search(vector& nums, int target) { + int l = 0, r = nums.size() - 1; + + while (l <= r) { + int m = l + (r - l) / 2; + + if (nums[m] == target) { + return true; + } + + if (nums[l] < nums[m]) { // Left portion + if (nums[l] <= target && target < nums[m]) { + r = m - 1; + } else { + l = m + 1; + } + } else if (nums[l] > nums[m]) { // Right portion + if (nums[m] < target && target <= nums[r]) { + l = m + 1; + } else { + r = m - 1; + } + } else { + l++; + } + } + + return false; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} target + * @return {boolean} + */ + search(nums, target) { + let l = 0, r = nums.length - 1; + + while (l <= r) { + const m = Math.floor(l + (r - l) / 2); + + if (nums[m] === target) { + return true; + } + + if (nums[l] < nums[m]) { // Left portion + if (nums[l] <= target && target < nums[m]) { + r = m - 1; + } else { + l = m + 1; + } + } else if (nums[l] > nums[m]) { // Right portion + if (nums[m] < target && target <= nums[r]) { + l = m + 1; + } else { + r = m - 1; + } + } else { + l++; + } + } + + return false; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(\log n)$ in average case, $O(n)$ in worst case. +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/simplify-path.md b/articles/simplify-path.md new file mode 100644 index 000000000..23fe29c96 --- /dev/null +++ b/articles/simplify-path.md @@ -0,0 +1,213 @@ +## 1. Stack - I + +::tabs-start + +```python +class Solution: + def simplifyPath(self, path: str) -> str: + stack = [] + cur = "" + + for c in path + "/": + if c == "/": + if cur == "..": + if stack: + stack.pop() + elif cur != "" and cur != ".": + stack.append(cur) + cur = "" + else: + cur += c + + return "/" + "/".join(stack) +``` + +```java +public class Solution { + public String simplifyPath(String path) { + Stack stack = new Stack<>(); + StringBuilder cur = new StringBuilder(); + + for (char c : (path + "/").toCharArray()) { + if (c == '/') { + if (cur.toString().equals("..")) { + if (!stack.isEmpty()) stack.pop(); + } else if (!cur.toString().equals("") && !cur.toString().equals(".")) { + stack.push(cur.toString()); + } + cur.setLength(0); + } else { + cur.append(c); + } + } + + return "/" + String.join("/", stack); + } +} +``` + +```cpp +class Solution { +public: + string simplifyPath(string path) { + vector stack; + string cur; + + for (char c : path + "/") { + if (c == '/') { + if (cur == "..") { + if (!stack.empty()) stack.pop_back(); + } else if (!cur.empty() && cur != ".") { + stack.push_back(cur); + } + cur.clear(); + } else { + cur += c; + } + } + + string result = "/"; + for (int i = 0; i < stack.size(); ++i) { + if (i > 0) result += "/"; + result += stack[i]; + } + + return result; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} path + * @return {string} + */ + simplifyPath(path) { + const stack = []; + let cur = ""; + + for (const c of path + "/") { + if (c === "/") { + if (cur === "..") { + if (stack.length) stack.pop(); + } else if (cur !== "" && cur !== ".") { + stack.push(cur); + } + cur = ""; + } else { + cur += c; + } + } + + return "/" + stack.join("/"); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Stack - II + +::tabs-start + +```python +class Solution: + def simplifyPath(self, path: str) -> str: + stack = [] + paths = path.split("/") + + for cur in paths: + if cur == "..": + if stack: + stack.pop() + elif cur != "" and cur != ".": + stack.append(cur) + + return "/" + "/".join(stack) +``` + +```java +public class Solution { + public String simplifyPath(String path) { + Stack stack = new Stack<>(); + String[] paths = path.split("/"); + + for (String cur : paths) { + if (cur.equals("..")) { + if (!stack.isEmpty()) stack.pop(); + } else if (!cur.equals("") && !cur.equals(".")) { + stack.push(cur); + } + } + + return "/" + String.join("/", stack); + } +} +``` + +```cpp +class Solution { +public: + string simplifyPath(string path) { + vector stack; + string cur; + stringstream ss(path); + while (getline(ss, cur, '/')) { + if (cur.empty()) continue; + if (cur == "..") { + if (!stack.empty()) stack.pop_back(); + } else if (!cur.empty() && cur != ".") { + stack.push_back(cur); + } + } + + string result = "/"; + for (int i = 0; i < stack.size(); ++i) { + if (i > 0) result += "/"; + result += stack[i]; + } + + return result; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} path + * @return {string} + */ + simplifyPath(path) { + const stack = []; + const paths = path.split("/"); + + for (const cur of paths) { + if (cur === "..") { + if (stack.length) { + stack.pop(); + } + } else if (cur !== "" && cur !== ".") { + stack.push(cur); + } + } + + return "/" + stack.join("/"); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/split-array-largest-sum.md b/articles/split-array-largest-sum.md new file mode 100644 index 000000000..dccf649c5 --- /dev/null +++ b/articles/split-array-largest-sum.md @@ -0,0 +1,862 @@ +## 1. Recursion + +::tabs-start + +```python +class Solution: + def splitArray(self, nums: List[int], k: int) -> int: + n = len(nums) + + def dfs(i, m): + if i == n: + return 0 if m == 0 else float("inf") + if m == 0: + return float("inf") + + res = float("inf") + curSum = 0 + for j in range(i, n - m + 1): + curSum += nums[j] + res = min(res, max(curSum, dfs(j + 1, m - 1))) + + return res + + return dfs(0, k) +``` + +```java +public class Solution { + public int splitArray(int[] nums, int k) { + int n = nums.length; + + return dfs(nums, 0, k, n); + } + + private int dfs(int[] nums, int i, int m, int n) { + if (i == n) { + return m == 0 ? 0 : Integer.MAX_VALUE; + } + if (m == 0) { + return Integer.MAX_VALUE; + } + + int res = Integer.MAX_VALUE; + int curSum = 0; + for (int j = i; j <= n - m; j++) { + curSum += nums[j]; + res = Math.min(res, Math.max(curSum, dfs(nums, j + 1, m - 1, n))); + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int splitArray(vector& nums, int k) { + int n = nums.size(); + return dfs(nums, 0, k, n); + } + +private: + int dfs(vector& nums, int i, int m, int n) { + if (i == n) { + return m == 0 ? 0 : INT_MAX; + } + if (m == 0) { + return INT_MAX; + } + + int res = INT_MAX, curSum = 0; + for (int j = i; j <= n - m; j++) { + curSum += nums[j]; + res = min(res, max(curSum, dfs(nums, j + 1, m - 1, n))); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {number} + */ + splitArray(nums, k) { + const n = nums.length; + + const dfs = (i, m) => { + if (i === n) { + return m === 0 ? 0 : Infinity; + } + if (m === 0) { + return Infinity; + } + + let res = Infinity; + let curSum = 0; + for (let j = i; j <= n - m; j++) { + curSum += nums[j]; + res = Math.min(res, Math.max(curSum, dfs(j + 1, m - 1))); + } + + return res; + }; + + return dfs(0, k); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * 2 ^ n)$ +* Space complexity: $O(n)$ for recursion stack. + +--- + +## 2. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def splitArray(self, nums: List[int], k: int) -> int: + n = len(nums) + dp = [[-1] * (k + 1) for _ in range(n)] + + def dfs(i, m): + if i == n: + return 0 if m == 0 else float("inf") + if m == 0: + return float("inf") + if dp[i][m] != -1: + return dp[i][m] + + res = float("inf") + curSum = 0 + for j in range(i, n - m + 1): + curSum += nums[j] + res = min(res, max(curSum, dfs(j + 1, m - 1))) + + dp[i][m] = res + return res + + return dfs(0, k) +``` + +```java +public class Solution { + private int[][] dp; + + public int splitArray(int[] nums, int k) { + int n = nums.length; + dp = new int[n][k + 1]; + for (int[] it : dp) { + Arrays.fill(it, -1); + } + return dfs(nums, 0, k, n); + } + + private int dfs(int[] nums, int i, int m, int n) { + if (i == n) { + return m == 0 ? 0 : Integer.MAX_VALUE; + } + if (m == 0) { + return Integer.MAX_VALUE; + } + if (dp[i][m] != -1) { + return dp[i][m]; + } + + int res = Integer.MAX_VALUE; + int curSum = 0; + for (int j = i; j <= n - m; j++) { + curSum += nums[j]; + res = Math.min(res, Math.max(curSum, dfs(nums, j + 1, m - 1, n))); + } + + return dp[i][m] = res; + } +} +``` + +```cpp +class Solution { + vector> dp; + +public: + int splitArray(vector& nums, int k) { + int n = nums.size(); + dp.assign(n, vector(k + 1, -1)); + return dfs(nums, 0, k, n); + } + +private: + int dfs(vector& nums, int i, int m, int n) { + if (i == n) { + return m == 0 ? 0 : INT_MAX; + } + if (m == 0) { + return INT_MAX; + } + if (dp[i][m] != -1) { + return dp[i][m]; + } + + int res = INT_MAX, curSum = 0; + for (int j = i; j <= n - m; j++) { + curSum += nums[j]; + res = min(res, max(curSum, dfs(nums, j + 1, m - 1, n))); + } + + return dp[i][m] = res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {number} + */ + splitArray(nums, k) { + const n = nums.length; + const dp = Array.from({ length: n }, () => Array(m + 1).fill(-1)); + + const dfs = (i, m) => { + if (i === n) { + return m === 0 ? 0 : Infinity; + } + if (m === 0) { + return Infinity; + } + if (dp[i][m] !== -1) { + return dp[i][m]; + } + + let res = Infinity; + let curSum = 0; + for (let j = i; j <= n - m; j++) { + curSum += nums[j]; + res = Math.min(res, Math.max(curSum, dfs(j + 1, m - 1))); + } + + return dp[i][m] = res; + }; + + return dfs(0, k); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(k * n ^ 2)$ +* Space complexity: $O(k * n)$ + +> Where $n$ is the size of the array $nums$ and $k$ is the number of sub-arrays to form. + +--- + +## 3. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def splitArray(self, nums: List[int], k: int) -> int: + n = len(nums) + dp = [[float("inf")] * (k + 1) for _ in range(n + 1)] + dp[n][0] = 0 + + for m in range(1, k + 1): + for i in range(n - 1, -1, -1): + curSum = 0 + for j in range(i, n - m + 1): + curSum += nums[j] + dp[i][m] = min(dp[i][m], max(curSum, dp[j + 1][m - 1])) + + return dp[0][k] +``` + +```java +public class Solution { + public int splitArray(int[] nums, int k) { + int n = nums.length; + int[][] dp = new int[n + 1][k + 1]; + for (int[] it : dp) { + Arrays.fill(it, Integer.MAX_VALUE); + } + dp[n][0] = 0; + + for (int m = 1; m <= k; m++) { + for (int i = n - 1; i >= 0; i--) { + int curSum = 0; + for (int j = i; j < n - m + 1; j++) { + curSum += nums[j]; + dp[i][m] = Math.min(dp[i][m], Math.max(curSum, dp[j + 1][m - 1])); + } + } + } + + return dp[0][k]; + } +} +``` + +```cpp +class Solution { +public: + int splitArray(vector& nums, int k) { + int n = nums.size(); + vector> dp(n + 1, vector(k + 1, INT_MAX)); + dp[n][0] = 0; + + for (int m = 1; m <= k; m++) { + for (int i = n - 1; i >= 0; i--) { + int curSum = 0; + for (int j = i; j < n - m + 1; j++) { + curSum += nums[j]; + dp[i][m] = min(dp[i][m], max(curSum, dp[j + 1][m - 1])); + } + } + } + + return dp[0][k]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {number} + */ + splitArray(nums, k) { + const n = nums.length; + const dp = Array.from({ length: n + 1 }, () => + Array(k + 1).fill(Infinity) + ); + dp[n][0] = 0; + + for (let m = 1; m <= k; m++) { + for (let i = n - 1; i >= 0; i--) { + let curSum = 0; + for (let j = i; j < n - m + 1; j++) { + curSum += nums[j]; + dp[i][m] = Math.min(dp[i][m], Math.max(curSum, dp[j + 1][m - 1])); + } + } + } + + return dp[0][k]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(k * n ^ 2)$ +* Space complexity: $O(k * n)$ + +> Where $n$ is the size of the array $nums$ and $k$ is the number of sub-arrays to form. + +--- + +## 4. Dynamic Programming (Space Optimized) + +::tabs-start + +```python +class Solution: + def splitArray(self, nums: List[int], k: int) -> int: + n = len(nums) + dp = [float("inf")] * (n + 1) + dp[n] = 0 + + for m in range(1, k + 1): + nextDp = [float("inf")] * (n + 1) + for i in range(n - 1, -1, -1): + curSum = 0 + for j in range(i, n - m + 1): + curSum += nums[j] + nextDp[i] = min(nextDp[i], max(curSum, dp[j + 1])) + dp = nextDp + + return dp[0] +``` + +```java +public class Solution { + public int splitArray(int[] nums, int k) { + int n = nums.length; + int[] dp = new int[n + 1]; + int[] nextDp = new int[n + 1]; + Arrays.fill(dp, Integer.MAX_VALUE); + dp[n] = 0; + + for (int m = 1; m <= k; m++) { + Arrays.fill(nextDp, Integer.MAX_VALUE); + for (int i = n - 1; i >= 0; i--) { + int curSum = 0; + for (int j = i; j < n - m + 1; j++) { + curSum += nums[j]; + nextDp[i] = Math.min(nextDp[i], Math.max(curSum, dp[j + 1])); + } + } + int[] temp = dp; + dp = nextDp; + nextDp = temp; + } + + return dp[0]; + } +} +``` + +```cpp +class Solution { +public: + int splitArray(vector& nums, int k) { + int n = nums.size(); + vector dp(n + 1, INT_MAX), nextDp(n + 1, INT_MAX); + dp[n] = 0; + + for (int m = 1; m <= k; m++) { + fill(nextDp.begin(), nextDp.end(), INT_MAX); + for (int i = n - 1; i >= 0; i--) { + int curSum = 0; + for (int j = i; j < n - m + 1; j++) { + curSum += nums[j]; + nextDp[i] = min(nextDp[i], max(curSum, dp[j + 1])); + } + } + dp.swap(nextDp); + } + + return dp[0]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {number} + */ + splitArray(nums, k) { + const n = nums.length; + let dp = new Array(n + 1).fill(Infinity); + let nextDp = new Array(n + 1).fill(Infinity); + dp[n] = 0; + + for (let m = 1; m <= k; m++) { + nextDp.fill(Infinity); + for (let i = n - 1; i >= 0; i--) { + let curSum = 0; + for (let j = i; j < n - m + 1; j++) { + curSum += nums[j]; + nextDp[i] = Math.min(nextDp[i], Math.max(curSum, dp[j + 1])); + } + } + [dp, nextDp] = [nextDp, dp]; + } + + return dp[0]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(k * n ^ 2)$ +* Space complexity: $O(n)$ + +> Where $n$ is the size of the array $nums$ and $k$ is the number of sub-arrays to form. + +--- + +## 5. Binary Search + +::tabs-start + +```python +class Solution: + def splitArray(self, nums: List[int], k: int) -> int: + def canSplit(largest): + subarray = 1 + curSum = 0 + for num in nums: + curSum += num + if curSum > largest: + subarray += 1 + if subarray > k: + return False + curSum = num + return True + + l, r = max(nums), sum(nums) + res = r + while l <= r: + mid = l + (r - l) // 2 + if canSplit(mid): + res = mid + r = mid - 1 + else: + l = mid + 1 + return res +``` + +```java +public class Solution { + public int splitArray(int[] nums, int k) { + int l = 0, r = 0, res = 0; + for (int num : nums) { + l = Math.max(l, num); + r += num; + } + res = r; + + while (l <= r) { + int mid = l + (r - l) / 2; + if (canSplit(nums, k, mid)) { + res = mid; + r = mid - 1; + } else { + l = mid + 1; + } + } + return res; + } + + private boolean canSplit(int[] nums, int k, int largest) { + int subarray = 1, curSum = 0; + for (int num : nums) { + curSum += num; + if (curSum > largest) { + subarray++; + if (subarray > k) return false; + curSum = num; + } + } + return true; + } +} +``` + +```cpp +class Solution { +public: + int splitArray(vector& nums, int k) { + int l = *max_element(nums.begin(), nums.end()); + int r = accumulate(nums.begin(), nums.end(), 0); + int res = r; + + while (l <= r) { + int mid = l + (r - l) / 2; + if (canSplit(nums, k, mid)) { + res = mid; + r = mid - 1; + } else { + l = mid + 1; + } + } + return res; + } + +private: + bool canSplit(vector& nums, int k, int largest) { + int subarray = 1, curSum = 0; + for (int num : nums) { + curSum += num; + if (curSum > largest) { + subarray++; + if (subarray > k) return 0; + curSum = num; + } + } + return true; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {number} + */ + splitArray(nums, k) { + const canSplit = (largest) => { + let subarray = 1, curSum = 0; + for (const num of nums) { + curSum += num; + if (curSum > largest) { + subarray++; + if (subarray > k) return false; + curSum = num; + } + } + return true; + }; + + let l = Math.max(...nums); + let r = nums.reduce((a, b) => a + b, 0); + let res = r; + + while (l <= r) { + const mid = Math.floor(l + (r - l) / 2); + if (canSplit(mid)) { + res = mid; + r = mid - 1; + } else { + l = mid + 1; + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log s)$ +* Space complexity: $O(1)$ + +> Where $n$ is the size of the array $nums$ and $s$ is the sum of all the elements in the array. + +--- + +## 6. Binary Search + Prefix Sum + +::tabs-start + +```python +class Solution: + def splitArray(self, nums: List[int], k: int) -> int: + n = len(nums) + prefix = [0] * (n + 1) + for i in range(n): + prefix[i + 1] = prefix[i] + nums[i] + + def canSplit(largest): + subarrays = 0 + i = 0 + while i < n: + l, r = i + 1, n + while l <= r: + mid = l + (r - l) // 2 + if prefix[mid] - prefix[i] <= largest: + l = mid + 1 + else: + r = mid - 1 + subarrays += 1 + i = r + if subarrays > k: + return False + return True + + l, r = max(nums), sum(nums) + res = r + while l <= r: + mid = l + (r - l) // 2 + if canSplit(mid): + res = mid + r = mid - 1 + else: + l = mid + 1 + + return res +``` + +```java +public class Solution { + private int[] prefix; + private int n; + + public int splitArray(int[] nums, int k) { + n = nums.length; + prefix = new int[n + 1]; + for (int i = 0; i < n; i++) { + prefix[i + 1] = prefix[i] + nums[i]; + } + + int l = Integer.MIN_VALUE, r = 0; + for (int num : nums) { + l = Math.max(l, num); + r += num; + } + + int res = r; + while (l <= r) { + int mid = l + (r - l) / 2; + if (canSplit(mid, k)) { + res = mid; + r = mid - 1; + } else { + l = mid + 1; + } + } + return res; + } + + private boolean canSplit(int largest, int k) { + int subarrays = 0, i = 0; + while (i < n) { + int l = i + 1, r = n; + while (l <= r) { + int mid = l + (r - l) / 2; + if (prefix[mid] - prefix[i] <= largest) { + l = mid + 1; + } else { + r = mid - 1; + } + } + subarrays++; + i = r; + if (subarrays > k) { + return false; + } + } + return true; + } +} +``` + +```cpp +class Solution { +private: + vector prefix; + int n; + +public: + int splitArray(vector& nums, int k) { + n = nums.size(); + prefix.resize(n + 1, 0); + for (int i = 0; i < n; ++i) { + prefix[i + 1] = prefix[i] + nums[i]; + } + + int l = *max_element(nums.begin(), nums.end()); + int r = accumulate(nums.begin(), nums.end(), 0); + int res = r; + + while (l <= r) { + int mid = l + (r - l) / 2; + if (canSplit(mid, k)) { + res = mid; + r = mid - 1; + } else { + l = mid + 1; + } + } + + return res; + } + +private: + bool canSplit(int largest, int k) { + int subarrays = 0, i = 0; + while (i < n) { + int l = i + 1, r = n; + while (l <= r) { + int mid = l + (r - l) / 2; + if (prefix[mid] - prefix[i] <= largest) { + l = mid + 1; + } else { + r = mid - 1; + } + } + subarrays++; + i = r; + if (subarrays > k) { + return false; + } + } + return true; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {number} + */ + splitArray(nums, k) { + const n = nums.length; + const prefix = new Array(n + 1).fill(0); + for (let i = 0; i < n; i++) { + prefix[i + 1] = prefix[i] + nums[i]; + } + + const canSplit = (largest) => { + let subarrays = 0, i = 0; + while (i < n) { + let l = i + 1, r = n; + while (l <= r) { + const mid = Math.floor(l + (r - l) / 2); + if (prefix[mid] - prefix[i] <= largest) { + l = mid + 1; + } else { + r = mid - 1; + } + } + subarrays++; + i = r; + if (subarrays > k) { + return false; + } + } + return true; + }; + + let l = Math.max(...nums); + let r = nums.reduce((a, b) => a + b, 0), res = r; + while (l <= r) { + const mid = Math.floor(l + (r - l) / 2); + if (canSplit(mid)) { + res = mid; + r = mid - 1; + } else { + l = mid + 1; + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n + (k * \log n * \log s))$ +* Space complexity: $O(n)$ + +> Where $n$ is the size of the array $nums$, $s$ is the sum of all the elements in the array, and $k$ is the number of sub-arrays to form. \ No newline at end of file diff --git a/articles/sqrtx.md b/articles/sqrtx.md new file mode 100644 index 000000000..13d854b33 --- /dev/null +++ b/articles/sqrtx.md @@ -0,0 +1,378 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def mySqrt(self, x: int) -> int: + if x == 0: + return 0 + + res = 1 + for i in range(1, x + 1): + if i * i > x: + return res + res = i + + return res +``` + +```java +public class Solution { + public int mySqrt(int x) { + if (x == 0) { + return 0; + } + + int res = 1; + for (int i = 1; i <= x; i++) { + if ((long) i * i > x) { + return res; + } + res = i; + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int mySqrt(int x) { + if (x == 0) { + return 0; + } + + int res = 1; + for (int i = 1; i <= x; i++) { + if ((long long) i * i > x) { + return res; + } + res = i; + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} x + * @return {number} + */ + mySqrt(x) { + if (x === 0) { + return 0; + } + + let res = 1; + for (let i = 1; i <= x; i++) { + if (i * i > x) { + return res; + } + res = i; + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(\sqrt {n})$ +* Space complexity: $O(1)$ + +--- + +## 2. In-Built Function + +::tabs-start + +```python +class Solution: + def mySqrt(self, x: int) -> int: + return int(sqrt(x)) +``` + +```java +public class Solution { + public int mySqrt(int x) { + return (int) Math.sqrt(x); + } +} +``` + +```cpp +class Solution { +public: + int mySqrt(int x) { + return (int) sqrt(x); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} x + * @return {number} + */ + mySqrt(x) { + return Math.floor(Math.sqrt(x)); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(1)$ +* Space complexity: $O(1)$ + +--- + +## 3. Binary Search + +::tabs-start + +```python +class Solution: + def mySqrt(self, x: int) -> int: + l, r = 0, x + res = 0 + + while l <= r: + m = l + (r - l) // 2 + if m * m > x: + r = m - 1 + elif m * m < x: + l = m + 1 + res = m + else: + return m + + return res +``` + +```java +public class Solution { + public int mySqrt(int x) { + int l = 0, r = x; + int res = 0; + + while (l <= r) { + int m = l + (r - l) / 2; + if ((long) m * m > x) { + r = m - 1; + } else if ((long) m * m < x) { + l = m + 1; + res = m; + } else { + return m; + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int mySqrt(int x) { + int l = 0, r = x; + int res = 0; + + while (l <= r) { + int m = l + (r - l) / 2; + if ((long long) m * m > x) { + r = m - 1; + } else if ((long long) m * m < x) { + l = m + 1; + res = m; + } else { + return m; + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} x + * @return {number} + */ + mySqrt(x) { + let l = 0, r = x; + let res = 0; + + while (l <= r) { + const m = Math.floor(l + (r - l) / 2); + if (m * m > x) { + r = m - 1; + } else if (m * m < x) { + l = m + 1; + res = m; + } else { + return m; + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(\log n)$ +* Space complexity: $O(1)$ + +--- + +## 4. Recursion + +::tabs-start + +```python +class Solution: + def mySqrt(self, x: int) -> int: + if x < 2: + return x + + l = self.mySqrt(x >> 2) << 1 + r = l + 1 + return l if r ** 2 > x else r +``` + +```java +public class Solution { + public int mySqrt(int x) { + if (x < 2) { + return x; + } + + int l = mySqrt(x >> 2) << 1; + int r = l + 1; + return (long) r * r > x ? l : r; + } +} +``` + +```cpp +class Solution { +public: + int mySqrt(int x) { + if (x < 2) { + return x; + } + + int l = mySqrt(x >> 2) << 1; + int r = l + 1; + return (long long) r * r > x ? l : r; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} x + * @return {number} + */ + mySqrt(x) { + if (x < 2) { + return x; + } + + const l = this.mySqrt(x >> 2) << 1; + const r = l + 1; + return r * r > x ? l : r; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(\log n)$ +* Space complexity: $O(\log n)$ for recursion stack. + +--- + +## 5. Newton's Method + +::tabs-start + +```python +class Solution: + def mySqrt(self, x: int) -> int: + r = x + while r * r > x: + r = (r + x // r) >> 1 + return r +``` + +```java +public class Solution { + public int mySqrt(int x) { + long r = x; + while (r * r > x) { + r = (r + x / r) >> 1; + } + return (int) r; + } +} +``` + +```cpp +class Solution { +public: + int mySqrt(int x) { + long long r = x; + while (r * r > x) { + r = (r + x / r) >> 1; + } + return r; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} x + * @return {number} + */ + mySqrt(x) { + let r = x; + while (r * r > x) { + r = (r + Math.floor(x / r)) >>> 1; + } + return r; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(\log n)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/transpose-matrix.md b/articles/transpose-matrix.md new file mode 100644 index 000000000..d92b422ab --- /dev/null +++ b/articles/transpose-matrix.md @@ -0,0 +1,208 @@ +## 1. Iteration - I + +::tabs-start + +```python +class Solution: + def transpose(self, matrix: List[List[int]]) -> List[List[int]]: + ROWS, COLS = len(matrix), len(matrix[0]) + res = [[0] * ROWS for _ in range(COLS)] + + for r in range(ROWS): + for c in range(COLS): + res[c][r] = matrix[r][c] + + return res +``` + +```java +public class Solution { + public int[][] transpose(int[][] matrix) { + int ROWS = matrix.length, COLS = matrix[0].length; + int[][] res = new int[COLS][ROWS]; + + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + res[c][r] = matrix[r][c]; + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector> transpose(vector>& matrix) { + int ROWS = matrix.size(), COLS = matrix[0].size(); + vector> res(COLS, vector(ROWS)); + + for (int r = 0; r < ROWS; ++r) { + for (int c = 0; c < COLS; ++c) { + res[c][r] = matrix[r][c]; + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} matrix + * @return {number[][]} + */ + transpose(matrix) { + const ROWS = matrix.length, COLS = matrix[0].length; + const res = Array.from({ length: COLS }, () => Array(ROWS).fill(0)); + + for (let r = 0; r < ROWS; r++) { + for (let c = 0; c < COLS; c++) { + res[c][r] = matrix[r][c]; + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * m)$ +* Space complexity: $O(n * m)$ for the output array. + +> Where $n$ is the number of rows and $m$ is the number of columns in the matrix. + +--- + +## 2. Iteration - II + +::tabs-start + +```python +class Solution: + def transpose(self, matrix: List[List[int]]) -> List[List[int]]: + ROWS, COLS = len(matrix), len(matrix[0]) + + if ROWS == COLS: + for r in range(ROWS): + for c in range(r): + matrix[r][c], matrix[c][r] = matrix[c][r], matrix[r][c] + + return matrix + + res = [[0] * ROWS for _ in range(COLS)] + + for r in range(ROWS): + for c in range(COLS): + res[c][r] = matrix[r][c] + + return res +``` + +```java +public class Solution { + public int[][] transpose(int[][] matrix) { + int ROWS = matrix.length, COLS = matrix[0].length; + + if (ROWS == COLS) { + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < r; c++) { + int tmp = matrix[r][c]; + matrix[r][c] = matrix[c][r]; + matrix[c][r] = tmp; + } + } + + return matrix; + } + + int[][] res = new int[COLS][ROWS]; + + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + res[c][r] = matrix[r][c]; + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector> transpose(vector>& matrix) { + int ROWS = matrix.size(), COLS = matrix[0].size(); + + if (ROWS == COLS) { + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < r; c++) { + swap(matrix[r][c], matrix[c][r]); + } + } + + return matrix; + } + + vector> res(COLS, vector(ROWS)); + + for (int r = 0; r < ROWS; ++r) { + for (int c = 0; c < COLS; ++c) { + res[c][r] = matrix[r][c]; + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} matrix + * @return {number[][]} + */ + transpose(matrix) { + const ROWS = matrix.length, COLS = matrix[0].length; + + if (ROWS === COLS) { + for (let r = 0; r < ROWS; r++) { + for (let c = 0; c < r; c++) { + [matrix[r][c], matrix[c][r]] = [matrix[c][r], matrix[r][c]]; + } + } + + return matrix; + } + + const res = Array.from({ length: COLS }, () => Array(ROWS).fill(0)); + + for (let r = 0; r < ROWS; r++) { + for (let c = 0; c < COLS; c++) { + res[c][r] = matrix[r][c]; + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * m)$ +* Space complexity: $O(n * m)$ + +> Where $n$ is the number of rows and $m$ is the number of columns in the matrix. \ No newline at end of file From 03352fe2823cf061bb853856b59532f26e03a8ce Mon Sep 17 00:00:00 2001 From: Sri Hari <94112314+Srihari2222@users.noreply.github.com> Date: Sat, 4 Jan 2025 04:50:44 +0530 Subject: [PATCH 24/45] Sri Hari: Batch-5/Neetcode-250/Added-articles (#3791) * Batch-5/Neetcode-250/Added-articles * Batch-5/Neetcode-250/Added-articles --- articles/binary-tree-inorder-traversal.md | 467 +++++++++ articles/binary-tree-postorder-traversal.md | 662 +++++++++++++ articles/binary-tree-preorder-traversal.md | 468 +++++++++ articles/construct-quad-tree.md | 733 ++++++++++++++ articles/delete-leaves-with-a-given-value.md | 577 +++++++++++ articles/delete-node-in-a-bst.md | 632 ++++++++++++ articles/house-robber-iii.md | 457 +++++++++ articles/insert-into-a-binary-search-tree.md | 292 ++++++ articles/word-break-ii.md | 975 +++++++++++++++++++ 9 files changed, 5263 insertions(+) create mode 100644 articles/binary-tree-inorder-traversal.md create mode 100644 articles/binary-tree-postorder-traversal.md create mode 100644 articles/binary-tree-preorder-traversal.md create mode 100644 articles/construct-quad-tree.md create mode 100644 articles/delete-leaves-with-a-given-value.md create mode 100644 articles/delete-node-in-a-bst.md create mode 100644 articles/house-robber-iii.md create mode 100644 articles/insert-into-a-binary-search-tree.md create mode 100644 articles/word-break-ii.md diff --git a/articles/binary-tree-inorder-traversal.md b/articles/binary-tree-inorder-traversal.md new file mode 100644 index 000000000..4735f8400 --- /dev/null +++ b/articles/binary-tree-inorder-traversal.md @@ -0,0 +1,467 @@ +## 1. Depth First Search + +::tabs-start + +```python +# 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 +class Solution: + def inorderTraversal(self, root: Optional[TreeNode]) -> List[int]: + res = [] + + def inorder(node): + if not node: + return + + inorder(node.left) + res.append(node.val) + inorder(node.right) + + inorder(root) + return res +``` + +```java +/** + * 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; + * } + * } + */ +public class Solution { + private List res; + + public List inorderTraversal(TreeNode root) { + res = new ArrayList<>(); + inorder(root); + return res; + } + + private void inorder(TreeNode node) { + if (node == null) { + return; + } + inorder(node.left); + res.add(node.val); + inorder(node.right); + } +} +``` + +```cpp +/** + * 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 { + vector res; + +public: + vector inorderTraversal(TreeNode* root) { + inorder(root); + return res; + } + +private: + void inorder(TreeNode* node) { + if (!node) { + return; + } + inorder(node->left); + res.push_back(node->val); + inorder(node->right); + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {number[]} + */ + inorderTraversal(root) { + const res = []; + + const inorder = (node) => { + if (!node) return; + inorder(node.left); + res.push(node.val); + inorder(node.right); + }; + + inorder(root); + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: + * $O(n)$ space for the recursion stack. + * $O(n)$ space for the output array. + +--- + +## 2. Iterative Depth First Search + +::tabs-start + +```python +# 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 +class Solution: + def inorderTraversal(self, root: Optional[TreeNode]) -> List[int]: + res = [] + stack = [] + cur = root + + while cur or stack: + while cur: + stack.append(cur) + cur = cur.left + cur = stack.pop() + res.append(cur.val) + cur = cur.right + + return res +``` + +```java +/** + * 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; + * } + * } + */ +public class Solution { + public List inorderTraversal(TreeNode root) { + List res = new ArrayList<>(); + Stack stack = new Stack<>(); + TreeNode cur = root; + + while (cur != null || !stack.isEmpty()) { + while (cur != null) { + stack.push(cur); + cur = cur.left; + } + cur = stack.pop(); + res.add(cur.val); + cur = cur.right; + } + + return res; + } +} +``` + +```cpp +/** + * 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: + vector inorderTraversal(TreeNode* root) { + vector res; + stack stack; + TreeNode* cur = root; + + while (cur || !stack.empty()) { + while (cur) { + stack.push(cur); + cur = cur->left; + } + cur = stack.top(); + stack.pop(); + res.push_back(cur->val); + cur = cur->right; + } + + return res; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {number[]} + */ + inorderTraversal(root) { + const res = []; + const stack = []; + let cur = root; + + while (cur || stack.length > 0) { + while (cur) { + stack.push(cur); + cur = cur.left; + } + cur = stack.pop(); + res.push(cur.val); + cur = cur.right; + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: + * $O(n)$ space for the stack. + * $O(n)$ space for the output array. + +--- + +## 3. Morris Traversal + +::tabs-start + +```python +# 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 +class Solution: + def inorderTraversal(self, root: Optional[TreeNode]) -> List[int]: + res = [] + cur = root + + while cur: + if not cur.left: + res.append(cur.val) + cur = cur.right + else: + prev = cur.left + while prev.right and prev.right != cur: + prev = prev.right + + if not prev.right: + prev.right = cur + cur = cur.left + else: + prev.right = None + res.append(cur.val) + cur = cur.right + + return res +``` + +```java +/** + * 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; + * } + * } + */ +public class Solution { + public List inorderTraversal(TreeNode root) { + List res = new ArrayList<>(); + TreeNode cur = root; + + while (cur != null) { + if (cur.left == null) { + res.add(cur.val); + cur = cur.right; + } else { + TreeNode prev = cur.left; + while (prev.right != null && prev.right != cur) { + prev = prev.right; + } + + if (prev.right == null) { + prev.right = cur; + cur = cur.left; + } else { + prev.right = null; + res.add(cur.val); + cur = cur.right; + } + } + } + + return res; + } +} +``` + +```cpp +/** + * 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: + vector inorderTraversal(TreeNode* root) { + vector res; + TreeNode* cur = root; + + while (cur) { + if (!cur->left) { + res.push_back(cur->val); + cur = cur->right; + } else { + TreeNode* prev = cur->left; + while (prev->right && prev->right != cur) { + prev = prev->right; + } + + if (!prev->right) { + prev->right = cur; + cur = cur->left; + } else { + prev->right = nullptr; + res.push_back(cur->val); + cur = cur->right; + } + } + } + + return res; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {number[]} + */ + inorderTraversal(root) { + const res = []; + let cur = root; + + while (cur) { + if (!cur.left) { + res.push(cur.val); + cur = cur.right; + } else { + let prev = cur.left; + while (prev.right && prev.right !== cur) { + prev = prev.right; + } + + if (!prev.right) { + prev.right = cur; + cur = cur.left; + } else { + prev.right = null; + res.push(cur.val); + cur = cur.right; + } + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: + * $O(1)$ extra space. + * $O(n)$ space for the output array. \ No newline at end of file diff --git a/articles/binary-tree-postorder-traversal.md b/articles/binary-tree-postorder-traversal.md new file mode 100644 index 000000000..554c3006a --- /dev/null +++ b/articles/binary-tree-postorder-traversal.md @@ -0,0 +1,662 @@ +## 1. Depth First Search + +::tabs-start + +```python +# 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 +class Solution: + def postorderTraversal(self, root: Optional[TreeNode]) -> List[int]: + res = [] + + def postorder(node): + if not node: + return + + postorder(node.left) + postorder(node.right) + res.append(node.val) + + postorder(root) + return res +``` + +```java +/** + * 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; + * } + * } + */ +public class Solution { + private List res; + + public List postorderTraversal(TreeNode root) { + res = new ArrayList<>(); + postorder(root); + return res; + } + + private void postorder(TreeNode node) { + if (node == null) { + return; + } + postorder(node.left); + postorder(node.right); + res.add(node.val); + } +} +``` + +```cpp +/** + * 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 { + vector res; + +public: + vector postorderTraversal(TreeNode* root) { + postorder(root); + return res; + } + +private: + void postorder(TreeNode* node) { + if (!node) { + return; + } + postorder(node->left); + postorder(node->right); + res.push_back(node->val); + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {number[]} + */ + postorderTraversal(root) { + const res = []; + + const postorder = (node) => { + if (!node) return; + postorder(node.left); + postorder(node.right); + res.push(node.val); + }; + + postorder(root); + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: + * $O(n)$ space for the recursion stack. + * $O(n)$ space for the output array. + +--- + +## 2. Iterative Depth First Search - I + +::tabs-start + +```python +# 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 +class Solution: + def postorderTraversal(self, root: Optional[TreeNode]) -> List[int]: + stack = [root] + visit = [False] + res = [] + + while stack: + cur, v = stack.pop(), visit.pop() + if cur: + if v: + res.append(cur.val) + else: + stack.append(cur) + visit.append(True) + stack.append(cur.right) + visit.append(False) + stack.append(cur.left) + visit.append(False) + + return res +``` + +```java +/** + * 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; + * } + * } + */ +public class Solution { + public List postorderTraversal(TreeNode root) { + Stack stack = new Stack<>(); + Stack visit = new Stack<>(); + List res = new ArrayList<>(); + + stack.push(root); + visit.push(false); + + while (!stack.isEmpty()) { + TreeNode cur = stack.pop(); + boolean v = visit.pop(); + + if (cur != null) { + if (v) { + res.add(cur.val); + } else { + stack.push(cur); + visit.push(true); + stack.push(cur.right); + visit.push(false); + stack.push(cur.left); + visit.push(false); + } + } + } + + return res; + } +} +``` + +```cpp +/** + * 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: + vector postorderTraversal(TreeNode* root) { + stack stk; + stack visit; + vector res; + + stk.push(root); + visit.push(false); + + while (!stk.empty()) { + TreeNode* cur = stk.top(); + bool v = visit.top(); + stk.pop(); + visit.pop(); + + if (cur) { + if (v) { + res.push_back(cur->val); + } else { + stk.push(cur); + visit.push(true); + stk.push(cur->right); + visit.push(false); + stk.push(cur->left); + visit.push(false); + } + } + } + + return res; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {number[]} + */ + postorderTraversal(root) { + const stack = [root]; + const visit = [false]; + const res = []; + + while (stack.length) { + const cur = stack.pop(); + const v = visit.pop(); + + if (cur) { + if (v) { + res.push(cur.val); + } else { + stack.push(cur); + visit.push(true); + stack.push(cur.right); + visit.push(false); + stack.push(cur.left); + visit.push(false); + } + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: + * $O(n)$ space for the stacks. + * $O(n)$ space for the output array. + +--- + +## 3. Iterative Depth First Search - II + +::tabs-start + +```python +# 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 +class Solution: + def postorderTraversal(self, root: Optional[TreeNode]) -> List[int]: + res = [] + stack = [] + cur = root + + while cur or stack: + if cur: + res.append(cur.val) + stack.append(cur) + cur = cur.right + else: + cur = stack.pop() + cur = cur.left + + res.reverse() + return res +``` + +```java +/** + * 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; + * } + * } + */ +public class Solution { + public List postorderTraversal(TreeNode root) { + List res = new ArrayList<>(); + Stack stack = new Stack<>(); + TreeNode cur = root; + + while (cur != null || !stack.isEmpty()) { + if (cur != null) { + res.add(cur.val); + stack.push(cur); + cur = cur.right; + } else { + cur = stack.pop(); + cur = cur.left; + } + } + + Collections.reverse(res); + return res; + } +} +``` + +```cpp +/** + * 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: + vector postorderTraversal(TreeNode* root) { + vector res; + stack stack; + TreeNode* cur = root; + + while (cur || !stack.empty()) { + if (cur) { + res.push_back(cur->val); + stack.push(cur); + cur = cur->right; + } else { + cur = stack.top(); + stack.pop(); + cur = cur->left; + } + } + + reverse(res.begin(), res.end()); + return res; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {number[]} + */ + postorderTraversal(root) { + const res = []; + const stack = []; + let cur = root; + + while (cur || stack.length) { + if (cur) { + res.push(cur.val); + stack.push(cur); + cur = cur.right; + } else { + cur = stack.pop(); + cur = cur.left; + } + } + + res.reverse(); + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: + * $O(n)$ space for the stack. + * $O(n)$ space for the output array. + +--- + +## 4. Morris Traversal + +::tabs-start + +```python +# 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 +class Solution: + def postorderTraversal(self, root: Optional[TreeNode]) -> List[int]: + res = [] + cur = root + + while cur: + if not cur.right: + res.append(cur.val) + cur = cur.left + else: + prev = cur.right + while prev.left and prev.left != cur: + prev = prev.left + + if not prev.left: + res.append(cur.val) + prev.left = cur + cur = cur.right + else: + prev.left = None + cur = cur.left + + res.reverse() + return res +``` + +```java +/** + * 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; + * } + * } + */ +public class Solution { + public List postorderTraversal(TreeNode root) { + List res = new ArrayList<>(); + TreeNode cur = root; + + while (cur != null) { + if (cur.right == null) { + res.add(cur.val); + cur = cur.left; + } else { + TreeNode prev = cur.right; + while (prev.left != null && prev.left != cur) { + prev = prev.left; + } + + if (prev.left == null) { + res.add(cur.val); + prev.left = cur; + cur = cur.right; + } else { + prev.left = null; + cur = cur.left; + } + } + } + + Collections.reverse(res); + return res; + } +} +``` + +```cpp +/** + * 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: + vector postorderTraversal(TreeNode* root) { + vector res; + TreeNode* cur = root; + + while (cur) { + if (!cur->right) { + res.push_back(cur->val); + cur = cur->left; + } else { + TreeNode* prev = cur->right; + while (prev->left && prev->left != cur) { + prev = prev->left; + } + + if (!prev->left) { + res.push_back(cur->val); + prev->left = cur; + cur = cur->right; + } else { + prev->left = nullptr; + cur = cur->left; + } + } + } + + reverse(res.begin(), res.end()); + return res; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {number[]} + */ + postorderTraversal(root) { + const res = []; + let cur = root; + + while (cur) { + if (!cur.right) { + res.push(cur.val); + cur = cur.left; + } else { + let prev = cur.right; + while (prev.left && prev.left !== cur) { + prev = prev.left; + } + + if (!prev.left) { + res.push(cur.val); + prev.left = cur; + cur = cur.right; + } else { + prev.left = null; + cur = cur.left; + } + } + } + + res.reverse(); + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: + * $O(1)$ extra space. + * $O(n)$ space for the output array. \ No newline at end of file diff --git a/articles/binary-tree-preorder-traversal.md b/articles/binary-tree-preorder-traversal.md new file mode 100644 index 000000000..fbcc8b4d6 --- /dev/null +++ b/articles/binary-tree-preorder-traversal.md @@ -0,0 +1,468 @@ +## 1. Depth First Search + +::tabs-start + +```python +# 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 +class Solution: + def preorderTraversal(self, root: Optional[TreeNode]) -> List[int]: + res = [] + + def preorder(node): + if not node: + return + + res.append(node.val) + preorder(node.left) + preorder(node.right) + + preorder(root) + return res +``` + +```java +/** + * 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; + * } + * } + */ +public class Solution { + private List res; + + public List preorderTraversal(TreeNode root) { + res = new ArrayList<>(); + preorder(root); + return res; + } + + private void preorder(TreeNode node) { + if (node == null) { + return; + } + res.add(node.val); + preorder(node.left); + preorder(node.right); + } +} +``` + +```cpp +/** + * 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 { + vector res; + +public: + vector preorderTraversal(TreeNode* root) { + preorder(root); + return res; + } + +private: + void preorder(TreeNode* node) { + if (!node) { + return; + } + res.push_back(node->val); + preorder(node->left); + preorder(node->right); + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {number[]} + */ + preorderTraversal(root) { + const res = []; + + const preorder = (node) => { + if (!node) return; + res.push(node.val); + preorder(node.left); + preorder(node.right); + }; + + preorder(root); + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: + * $O(n)$ space for the recursion stack. + * $O(n)$ space for the output array. + +--- + +## 2. Iterative Depth First Search + +::tabs-start + +```python +# 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 +class Solution: + def preorderTraversal(self, root: Optional[TreeNode]) -> List[int]: + res = [] + stack = [] + cur = root + + while cur or stack: + if cur: + res.append(cur.val) + stack.append(cur.right) + cur = cur.left + else: + cur = stack.pop() + + return res +``` + +```java +/** + * 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; + * } + * } + */ +public class Solution { + public List preorderTraversal(TreeNode root) { + List res = new ArrayList<>(); + Stack stack = new Stack<>(); + TreeNode cur = root; + + while (cur != null || !stack.isEmpty()) { + if (cur != null) { + res.add(cur.val); + stack.push(cur.right); + cur = cur.left; + } else { + cur = stack.pop(); + } + } + + return res; + } +} +``` + +```cpp +/** + * 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: + vector preorderTraversal(TreeNode* root) { + vector res; + stack stack; + TreeNode* cur = root; + + while (cur || !stack.empty()) { + if (cur) { + res.push_back(cur->val); + stack.push(cur->right); + cur = cur->left; + } else { + cur = stack.top(); + stack.pop(); + } + } + + return res; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {number[]} + */ + preorderTraversal(root) { + const res = []; + const stack = []; + let cur = root; + + while (cur || stack.length > 0) { + if (cur) { + res.push(cur.val); + stack.push(cur.right); + cur = cur.left; + } else { + cur = stack.pop(); + + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: + * $O(n)$ space for the stack. + * $O(n)$ space for the output array. + +--- + +## 3. Morris Traversal + +::tabs-start + +```python +# 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 +class Solution: + def preorderTraversal(self, root: Optional[TreeNode]) -> List[int]: + res = [] + cur = root + + while cur: + if not cur.left: + res.append(cur.val) + cur = cur.right + else: + prev = cur.left + while prev.right and prev.right != cur: + prev = prev.right + + if not prev.right: + res.append(cur.val) + prev.right = cur + cur = cur.left + else: + prev.right = None + cur = cur.right + + return res +``` + +```java +/** + * 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; + * } + * } + */ +public class Solution { + public List preorderTraversal(TreeNode root) { + List res = new ArrayList<>(); + TreeNode cur = root; + + while (cur != null) { + if (cur.left == null) { + res.add(cur.val); + cur = cur.right; + } else { + TreeNode prev = cur.left; + while (prev.right != null && prev.right != cur) { + prev = prev.right; + } + + if (prev.right == null) { + res.add(cur.val); + prev.right = cur; + cur = cur.left; + } else { + prev.right = null; + cur = cur.right; + } + } + } + + return res; + } +} +``` + +```cpp +/** + * 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: + vector preorderTraversal(TreeNode* root) { + vector res; + TreeNode* cur = root; + + while (cur) { + if (!cur->left) { + res.push_back(cur->val); + cur = cur->right; + } else { + TreeNode* prev = cur->left; + while (prev->right && prev->right != cur) { + prev = prev->right; + } + + if (!prev->right) { + res.push_back(cur->val); + prev->right = cur; + cur = cur->left; + } else { + prev->right = nullptr; + cur = cur->right; + } + } + } + + return res; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {number[]} + */ + preorderTraversal(root) { + const res = []; + let cur = root; + + while (cur) { + if (!cur.left) { + res.push(cur.val); + cur = cur.right; + } else { + let prev = cur.left; + while (prev.right && prev.right !== cur) { + prev = prev.right; + } + + if (!prev.right) { + res.push(cur.val); + prev.right = cur; + cur = cur.left; + } else { + prev.right = null; + cur = cur.right; + } + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: + * $O(1)$ extra space. + * $O(n)$ space for the output array. \ No newline at end of file diff --git a/articles/construct-quad-tree.md b/articles/construct-quad-tree.md new file mode 100644 index 000000000..48e117a86 --- /dev/null +++ b/articles/construct-quad-tree.md @@ -0,0 +1,733 @@ +## 1. Recursion + +::tabs-start + +```python +""" +# Definition for a QuadTree node. +class Node: + def __init__(self, val, isLeaf, topLeft, topRight, bottomLeft, bottomRight): + self.val = val + self.isLeaf = isLeaf + self.topLeft = topLeft + self.topRight = topRight + self.bottomLeft = bottomLeft + self.bottomRight = bottomRight +""" + +class Solution: + def construct(self, grid: List[List[int]]) -> 'Node': + def dfs(n, r, c): + allSame = True + for i in range(n): + for j in range(n): + if grid[r][c] != grid[r + i][c + j]: + allSame = False + break + if allSame: + return Node(grid[r][c], True) + + n = n // 2 + topleft = dfs(n, r, c) + topright = dfs(n, r, c + n) + bottomleft = dfs(n, r + n, c) + bottomright = dfs(n, r + n, c + n) + + return Node(0, False, topleft, topright, bottomleft, bottomright) + + return dfs(len(grid), 0, 0) +``` + +```java +/* +// Definition for a QuadTree node. +class Node { + public boolean val; + public boolean isLeaf; + public Node topLeft; + public Node topRight; + public Node bottomLeft; + public Node bottomRight; + + + public Node() { + this.val = false; + this.isLeaf = false; + this.topLeft = null; + this.topRight = null; + this.bottomLeft = null; + this.bottomRight = null; + } + + public Node(boolean val, boolean isLeaf) { + this.val = val; + this.isLeaf = isLeaf; + this.topLeft = null; + this.topRight = null; + this.bottomLeft = null; + this.bottomRight = null; + } + + public Node(boolean val, boolean isLeaf, Node topLeft, Node topRight, Node bottomLeft, Node bottomRight) { + this.val = val; + this.isLeaf = isLeaf; + this.topLeft = topLeft; + this.topRight = topRight; + this.bottomLeft = bottomLeft; + this.bottomRight = bottomRight; + } +} +*/ + +public class Solution { + public Node construct(int[][] grid) { + return dfs(grid, grid.length, 0, 0); + } + + private Node dfs(int[][] grid, int n, int r, int c) { + boolean allSame = true; + + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { + if (grid[r][c] != grid[r + i][c + j]) { + allSame = false; + break; + } + } + } + + if (allSame) { + return new Node(grid[r][c] == 1, true); + } + + int mid = n / 2; + Node topLeft = dfs(grid, mid, r, c); + Node topRight = dfs(grid, mid, r, c + mid); + Node bottomLeft = dfs(grid, mid, r + mid, c); + Node bottomRight = dfs(grid, mid, r + mid, c + mid); + + return new Node(false, false, topLeft, topRight, bottomLeft, bottomRight); + } +} +``` + +```cpp +/* +// Definition for a QuadTree node. +class Node { +public: + bool val; + bool isLeaf; + Node* topLeft; + Node* topRight; + Node* bottomLeft; + Node* bottomRight; + + Node() { + val = false; + isLeaf = false; + topLeft = NULL; + topRight = NULL; + bottomLeft = NULL; + bottomRight = NULL; + } + + Node(bool _val, bool _isLeaf) { + val = _val; + isLeaf = _isLeaf; + topLeft = NULL; + topRight = NULL; + bottomLeft = NULL; + bottomRight = NULL; + } + + Node(bool _val, bool _isLeaf, Node* _topLeft, Node* _topRight, Node* _bottomLeft, Node* _bottomRight) { + val = _val; + isLeaf = _isLeaf; + topLeft = _topLeft; + topRight = _topRight; + bottomLeft = _bottomLeft; + bottomRight = _bottomRight; + } +}; +*/ + +class Solution { +public: + Node* construct(vector>& grid) { + return dfs(grid, grid.size(), 0, 0); + } + +private: + Node* dfs(vector>& grid, int n, int r, int c) { + bool allSame = true; + + for (int i = 0; i < n && allSame; ++i) { + for (int j = 0; j < n; ++j) { + if (grid[r][c] != grid[r + i][c + j]) { + allSame = false; + break; + } + } + } + + if (allSame) { + return new Node(grid[r][c] == 1, true); + } + + int mid = n / 2; + Node* topLeft = dfs(grid, mid, r, c); + Node* topRight = dfs(grid, mid, r, c + mid); + Node* bottomLeft = dfs(grid, mid, r + mid, c); + Node* bottomRight = dfs(grid, mid, r + mid, c + mid); + + return new Node(false, false, topLeft, topRight, bottomLeft, bottomRight); + } +}; +``` + +```javascript +/** + * // Definition for a QuadTree node. + * class Node { + * constructor(val,isLeaf,topLeft,topRight,bottomLeft,bottomRight) { + * this.val = val; + * this.isLeaf = isLeaf; + * this.topLeft = topLeft; + * this.topRight = topRight; + * this.bottomLeft = bottomLeft; + * this.bottomRight = bottomRight; + * } + * } + */ + +class Solution { + /** + * @param {number[][]} grid + * @return {Node} + */ + construct(grid) { + const dfs = (n, r, c) => { + let allSame = true; + + for (let i = 0; i < n && allSame; i++) { + for (let j = 0; j < n; j++) { + if (grid[r][c] !== grid[r + i][c + j]) { + allSame = false; + break; + } + } + } + + if (allSame) { + return new Node(grid[r][c] === 1, true); + } + + const mid = Math.floor(n / 2); + const topLeft = dfs(mid, r, c); + const topRight = dfs(mid, r, c + mid); + const bottomLeft = dfs(mid, r + mid, c); + const bottomRight = dfs(mid, r + mid, c + mid); + + return new Node(false, false, topLeft, topRight, bottomLeft, bottomRight); + }; + + return dfs(grid.length, 0, 0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2 \log n)$ +* Space complexity: $O(\log n)$ for recursion stack. + +--- + +## 2. Recursion (Optimal) + +::tabs-start + +```python +""" +# Definition for a QuadTree node. +class Node: + def __init__(self, val, isLeaf, topLeft, topRight, bottomLeft, bottomRight): + self.val = val + self.isLeaf = isLeaf + self.topLeft = topLeft + self.topRight = topRight + self.bottomLeft = bottomLeft + self.bottomRight = bottomRight +""" + +class Solution: + def construct(self, grid: List[List[int]]) -> 'Node': + def dfs(n, r, c): + if n == 1: + return Node(grid[r][c] == 1, True) + + mid = n // 2 + topLeft = dfs(mid, r, c) + topRight = dfs(mid, r, c + mid) + bottomLeft = dfs(mid, r + mid, c) + bottomRight = dfs(mid, r + mid, c + mid) + + if (topLeft.isLeaf and topRight.isLeaf and + bottomLeft.isLeaf and bottomRight.isLeaf and + topLeft.val == topRight.val == bottomLeft.val == bottomRight.val): + return Node(topLeft.val, True) + + return Node(False, False, topLeft, topRight, bottomLeft, bottomRight) + + return dfs(len(grid), 0, 0) +``` + +```java +/* +// Definition for a QuadTree node. +class Node { + public boolean val; + public boolean isLeaf; + public Node topLeft; + public Node topRight; + public Node bottomLeft; + public Node bottomRight; + + + public Node() { + this.val = false; + this.isLeaf = false; + this.topLeft = null; + this.topRight = null; + this.bottomLeft = null; + this.bottomRight = null; + } + + public Node(boolean val, boolean isLeaf) { + this.val = val; + this.isLeaf = isLeaf; + this.topLeft = null; + this.topRight = null; + this.bottomLeft = null; + this.bottomRight = null; + } + + public Node(boolean val, boolean isLeaf, Node topLeft, Node topRight, Node bottomLeft, Node bottomRight) { + this.val = val; + this.isLeaf = isLeaf; + this.topLeft = topLeft; + this.topRight = topRight; + this.bottomLeft = bottomLeft; + this.bottomRight = bottomRight; + } +} +*/ + +public class Solution { + public Node construct(int[][] grid) { + return dfs(grid, grid.length, 0, 0); + } + + private Node dfs(int[][] grid, int n, int r, int c) { + if (n == 1) { + return new Node(grid[r][c] == 1, true); + } + + int mid = n / 2; + Node topLeft = dfs(grid, mid, r, c); + Node topRight = dfs(grid, mid, r, c + mid); + Node bottomLeft = dfs(grid, mid, r + mid, c); + Node bottomRight = dfs(grid, mid, r + mid, c + mid); + + if (topLeft.isLeaf && topRight.isLeaf && + bottomLeft.isLeaf && bottomRight.isLeaf && + topLeft.val == topRight.val && + topLeft.val == bottomLeft.val && + topLeft.val == bottomRight.val) { + return new Node(topLeft.val, true); + } + + return new Node(false, false, topLeft, topRight, bottomLeft, bottomRight); + } +} +``` + +```cpp +/* +// Definition for a QuadTree node. +class Node { +public: + bool val; + bool isLeaf; + Node* topLeft; + Node* topRight; + Node* bottomLeft; + Node* bottomRight; + + Node() { + val = false; + isLeaf = false; + topLeft = NULL; + topRight = NULL; + bottomLeft = NULL; + bottomRight = NULL; + } + + Node(bool _val, bool _isLeaf) { + val = _val; + isLeaf = _isLeaf; + topLeft = NULL; + topRight = NULL; + bottomLeft = NULL; + bottomRight = NULL; + } + + Node(bool _val, bool _isLeaf, Node* _topLeft, Node* _topRight, Node* _bottomLeft, Node* _bottomRight) { + val = _val; + isLeaf = _isLeaf; + topLeft = _topLeft; + topRight = _topRight; + bottomLeft = _bottomLeft; + bottomRight = _bottomRight; + } +}; +*/ + +class Solution { +public: + Node* construct(vector>& grid) { + return dfs(grid, grid.size(), 0, 0); + } + +private: + Node* dfs(vector>& grid, int n, int r, int c) { + if (n == 1) { + return new Node(grid[r][c] == 1, true); + } + + int mid = n / 2; + Node* topLeft = dfs(grid, mid, r, c); + Node* topRight = dfs(grid, mid, r, c + mid); + Node* bottomLeft = dfs(grid, mid, r + mid, c); + Node* bottomRight = dfs(grid, mid, r + mid, c + mid); + + if (topLeft->isLeaf && topRight->isLeaf && + bottomLeft->isLeaf && bottomRight->isLeaf && + topLeft->val == topRight->val && + topLeft->val == bottomLeft->val && + topLeft->val == bottomRight->val) { + return new Node(topLeft->val, true); + } + + return new Node(false, false, topLeft, topRight, bottomLeft, bottomRight); + } +}; +``` + +```javascript +/** + * // Definition for a QuadTree node. + * class Node { + * constructor(val,isLeaf,topLeft,topRight,bottomLeft,bottomRight) { + * this.val = val; + * this.isLeaf = isLeaf; + * this.topLeft = topLeft; + * this.topRight = topRight; + * this.bottomLeft = bottomLeft; + * this.bottomRight = bottomRight; + * } + * } + */ + +class Solution { + /** + * @param {number[][]} grid + * @return {Node} + */ + construct(grid) { + const dfs = (n, r, c) => { + if (n === 1) { + return new Node(grid[r][c] === 1, true); + } + + const mid = Math.floor(n / 2); + const topLeft = dfs(mid, r, c); + const topRight = dfs(mid, r, c + mid); + const bottomLeft = dfs(mid, r + mid, c); + const bottomRight = dfs(mid, r + mid, c + mid); + + if (topLeft.isLeaf && topRight.isLeaf && + bottomLeft.isLeaf && bottomRight.isLeaf && + topLeft.val === topRight.val && + topLeft.val === bottomLeft.val && + topLeft.val === bottomRight.val) { + return new Node(topLeft.val, true); + } + + return new Node(false, false, topLeft, topRight, bottomLeft, bottomRight); + }; + + return dfs(grid.length, 0, 0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(\log n)$ for recursion stack. + +--- + +## 3. Recursion (Space Optimized) + +::tabs-start + +```python +""" +# Definition for a QuadTree node. +class Node: + def __init__(self, val, isLeaf, topLeft, topRight, bottomLeft, bottomRight): + self.val = val + self.isLeaf = isLeaf + self.topLeft = topLeft + self.topRight = topRight + self.bottomLeft = bottomLeft + self.bottomRight = bottomRight +""" + +class Solution: + def construct(self, grid: List[List[int]]) -> 'Node': + leafNodes = { + 0: Node(False, True), + 1: Node(True, True) + } + + def dfs(n, r, c): + if n == 1: + return leafNodes[grid[r][c]] + + n //= 2 + topLeft = dfs(n, r, c) + topRight = dfs(n, r, c + n) + bottomLeft = dfs(n, r + n, c) + bottomRight = dfs(n, r + n, c + n) + + if (topLeft.isLeaf and topRight.isLeaf and + bottomLeft.isLeaf and bottomRight.isLeaf and + topLeft.val == topRight.val == bottomLeft.val == bottomRight.val): + return topLeft + + return Node(False, False, topLeft, topRight, bottomLeft, bottomRight) + + return dfs(len(grid), 0, 0) +``` + +```java +/* +// Definition for a QuadTree node. +class Node { + public boolean val; + public boolean isLeaf; + public Node topLeft; + public Node topRight; + public Node bottomLeft; + public Node bottomRight; + + + public Node() { + this.val = false; + this.isLeaf = false; + this.topLeft = null; + this.topRight = null; + this.bottomLeft = null; + this.bottomRight = null; + } + + public Node(boolean val, boolean isLeaf) { + this.val = val; + this.isLeaf = isLeaf; + this.topLeft = null; + this.topRight = null; + this.bottomLeft = null; + this.bottomRight = null; + } + + public Node(boolean val, boolean isLeaf, Node topLeft, Node topRight, Node bottomLeft, Node bottomRight) { + this.val = val; + this.isLeaf = isLeaf; + this.topLeft = topLeft; + this.topRight = topRight; + this.bottomLeft = bottomLeft; + this.bottomRight = bottomRight; + } +} +*/ + +public class Solution { + private static final Node falseLeaf = new Node(false, true); + private static final Node trueLeaf = new Node(true, true); + + public Node construct(int[][] grid) { + return dfs(grid, grid.length, 0, 0); + } + + private Node dfs(int[][] grid, int n, int r, int c) { + if (n == 1) { + return grid[r][c] == 1 ? trueLeaf : falseLeaf; + } + + n /= 2; + Node topLeft = dfs(grid, n, r, c); + Node topRight = dfs(grid, n, r, c + n); + Node bottomLeft = dfs(grid, n, r + n, c); + Node bottomRight = dfs(grid, n, r + n, c + n); + + if (topLeft.isLeaf && topRight.isLeaf && + bottomLeft.isLeaf && bottomRight.isLeaf && + topLeft.val == topRight.val && topLeft.val == bottomLeft.val && + topLeft.val == bottomRight.val) { + return topLeft; + } + + return new Node(false, false, topLeft, topRight, bottomLeft, bottomRight); + } +} +``` + +```cpp +/* +// Definition for a QuadTree node. +class Node { +public: + bool val; + bool isLeaf; + Node* topLeft; + Node* topRight; + Node* bottomLeft; + Node* bottomRight; + + Node() { + val = false; + isLeaf = false; + topLeft = NULL; + topRight = NULL; + bottomLeft = NULL; + bottomRight = NULL; + } + + Node(bool _val, bool _isLeaf) { + val = _val; + isLeaf = _isLeaf; + topLeft = NULL; + topRight = NULL; + bottomLeft = NULL; + bottomRight = NULL; + } + + Node(bool _val, bool _isLeaf, Node* _topLeft, Node* _topRight, Node* _bottomLeft, Node* _bottomRight) { + val = _val; + isLeaf = _isLeaf; + topLeft = _topLeft; + topRight = _topRight; + bottomLeft = _bottomLeft; + bottomRight = _bottomRight; + } +}; +*/ + +class Solution { +private: + Node* falseLeaf = new Node(false, true); + Node* trueLeaf = new Node(true, true); + +public: + Node* construct(vector>& grid) { + return dfs(grid, grid.size(), 0, 0); + } + +private: + Node* dfs(vector>& grid, int n, int r, int c) { + if (n == 1) { + return grid[r][c] == 1 ? trueLeaf : falseLeaf; + } + + n /= 2; + Node* topLeft = dfs(grid, n, r, c); + Node* topRight = dfs(grid, n, r, c + n); + Node* bottomLeft = dfs(grid, n, r + n, c); + Node* bottomRight = dfs(grid, n, r + n, c + n); + + if (topLeft->isLeaf && topRight->isLeaf && + bottomLeft->isLeaf && bottomRight->isLeaf && + topLeft->val == topRight->val && topLeft->val == bottomLeft->val && + topLeft->val == bottomRight->val) { + return topLeft; + } + + return new Node(false, false, topLeft, topRight, bottomLeft, bottomRight); + } +}; +``` + +```javascript +/** + * // Definition for a QuadTree node. + * class Node { + * constructor(val,isLeaf,topLeft,topRight,bottomLeft,bottomRight) { + * this.val = val; + * this.isLeaf = isLeaf; + * this.topLeft = topLeft; + * this.topRight = topRight; + * this.bottomLeft = bottomLeft; + * this.bottomRight = bottomRight; + * } + * } + */ + +class Solution { + /** + * @param {number[][]} grid + * @return {Node} + */ + construct(grid) { + const falseLeaf = new Node(false, true); + const trueLeaf = new Node(true, true); + + const dfs = (n, r, c) => { + if (n === 1) { + return grid[r][c] === 1 ? trueLeaf : falseLeaf; + } + + n = Math.floor(n / 2); + const topLeft = dfs(n, r, c); + const topRight = dfs(n, r, c + n); + const bottomLeft = dfs(n, r + n, c); + const bottomRight = dfs(n, r + n, c + n); + + if (topLeft.isLeaf && topRight.isLeaf && + bottomLeft.isLeaf && bottomRight.isLeaf && + topLeft.val === topRight.val && topLeft.val === bottomLeft.val && + topLeft.val === bottomRight.val) { + return topLeft; + } + + return new Node(false, false, topLeft, topRight, bottomLeft, bottomRight); + }; + + return dfs(grid.length, 0, 0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(\log n)$ for recursion stack. \ No newline at end of file diff --git a/articles/delete-leaves-with-a-given-value.md b/articles/delete-leaves-with-a-given-value.md new file mode 100644 index 000000000..ccfb4637b --- /dev/null +++ b/articles/delete-leaves-with-a-given-value.md @@ -0,0 +1,577 @@ +## 1. Recursion (Postorder Traversal) + +::tabs-start + +```python +# 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 +class Solution: + def removeLeafNodes(self, root: Optional[TreeNode], target: int) -> Optional[TreeNode]: + if not root: + return None + + root.left = self.removeLeafNodes(root.left, target) + root.right = self.removeLeafNodes(root.right, target) + + if not root.left and not root.right and root.val == target: + return None + + return root +``` + +```java +/** + * 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; + * } + * } + */ +public class Solution { + public TreeNode removeLeafNodes(TreeNode root, int target) { + if (root == null) { + return null; + } + + root.left = removeLeafNodes(root.left, target); + root.right = removeLeafNodes(root.right, target); + + if (root.left == null && root.right == null && root.val == target) { + return null; + } + + return root; + } +} +``` + +```cpp +/** + * 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* removeLeafNodes(TreeNode* root, int target) { + if (!root) { + return nullptr; + } + + root->left = removeLeafNodes(root->left, target); + root->right = removeLeafNodes(root->right, target); + + if (!root->left && !root->right && root->val == target) { + return nullptr; + } + + return root; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @param {number} target + * @return {TreeNode} + */ + removeLeafNodes(root, target) { + if (!root) { + return null; + } + + root.left = this.removeLeafNodes(root.left, target); + root.right = this.removeLeafNodes(root.right, target); + + if (!root.left && !root.right && root.val === target) { + return null; + } + + return root; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ for recursion stack. + +--- + +## 2. Iterative Postorder Traversal + +::tabs-start + +```python +# 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 +class Solution: + def removeLeafNodes(self, root: Optional[TreeNode], target: int) -> Optional[TreeNode]: + stack = [root] + visit = set() + parents = {root: None} + + while stack: + node = stack.pop() + if not node.left and not node.right: + if node.val == target: + p = parents[node] + if not p: + return None + if p.left == node: + p.left = None + if p.right == node: + p.right = None + elif node not in visit: + visit.add(node) + stack.append(node) + if node.left: + stack.append(node.left) + parents[node.left] = node + if node.right: + stack.append(node.right) + parents[node.right] = node + + return root +``` + +```java +/** + * 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; + * } + * } + */ +public class Solution { + public TreeNode removeLeafNodes(TreeNode root, int target) { + Stack stack = new Stack<>(); + Set visit = new HashSet<>(); + Map parents = new HashMap<>(); + parents.put(root, null); + stack.push(root); + + while (!stack.isEmpty()) { + TreeNode node = stack.pop(); + if (node.left == null && node.right == null) { + if (node.val == target) { + TreeNode p = parents.get(node); + if (p == null) { + return null; + } + if (p.left == node) { + p.left = null; + } + if (p.right == node) { + p.right = null; + } + } + } else if (!visit.contains(node)) { + visit.add(node); + stack.push(node); + if (node.left != null) { + stack.push(node.left); + parents.put(node.left, node); + } + if (node.right != null) { + stack.push(node.right); + parents.put(node.right, node); + } + } + } + + return root; + } +} +``` + +```cpp +/** + * 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* removeLeafNodes(TreeNode* root, int target) { + stack stack; + unordered_set visit; + unordered_map parents; + parents[root] = nullptr; + stack.push(root); + + while (!stack.empty()) { + TreeNode* node = stack.top(); + stack.pop(); + if (!node->left && !node->right) { + if (node->val == target) { + TreeNode* p = parents[node]; + if (!p) { + return nullptr; + } + if (p->left == node) { + p->left = nullptr; + } + if (p->right == node) { + p->right = nullptr; + } + } + } else if (visit.find(node) == visit.end()) { + visit.insert(node); + stack.push(node); + if (node->left) { + stack.push(node->left); + parents[node->left] = node; + } + if (node->right) { + stack.push(node->right); + parents[node->right] = node; + } + } + } + + return root; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @param {number} target + * @return {TreeNode} + */ + removeLeafNodes(root, target) { + const stack = [root]; + const visit = new Set(); + const parents = new Map(); + parents.set(root, null); + + while (stack.length > 0) { + const node = stack.pop(); + if (!node.left && !node.right) { + if (node.val === target) { + const p = parents.get(node); + if (!p) { + return null; + } + if (p.left === node) { + p.left = null; + } + if (p.right === node) { + p.right = null; + } + } + } else if (!visit.has(node)) { + visit.add(node); + stack.push(node); + if (node.left) { + stack.push(node.left); + parents.set(node.left, node); + } + if (node.right) { + stack.push(node.right); + parents.set(node.right, node); + } + } + } + + return root; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Iterative Postorder Traversal (Optimal) + +::tabs-start + +```python +# 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 +class Solution: + def removeLeafNodes(self, root: Optional[TreeNode], target: int) -> Optional[TreeNode]: + if not root: + return None + + stack = [] + cur = root + visited = None + + while stack or cur: + while cur: + stack.append(cur) + cur = cur.left + + cur = stack[-1] + if cur.right and cur.right != visited: + cur = cur.right + continue + + stack.pop() + if not cur.left and not cur.right and cur.val == target: + if not stack: + return None + + parent = stack[-1] + if parent.left == cur: + parent.left = None + elif parent.right == cur: + parent.right = None + else: + visited = cur + + cur = None + + return root +``` + +```java +/** + * 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; + * } + * } + */ +public class Solution { + public TreeNode removeLeafNodes(TreeNode root, int target) { + if (root == null) return null; + + Stack stack = new Stack<>(); + TreeNode cur = root, visited = null; + + while (!stack.isEmpty() || cur != null) { + while (cur != null) { + stack.push(cur); + cur = cur.left; + } + + cur = stack.peek(); + if (cur.right != null && cur.right != visited) { + cur = cur.right; + continue; + } + + stack.pop(); + if (cur.left == null && cur.right == null && cur.val == target) { + if (stack.isEmpty()) return null; + + TreeNode parent = stack.peek(); + if (parent.left == cur) { + parent.left = null; + } else if (parent.right == cur) { + parent.right = null; + } + } else { + visited = cur; + } + + cur = null; + } + + return root; + } +} +``` + +```cpp +/** + * 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* removeLeafNodes(TreeNode* root, int target) { + if (!root) return nullptr; + + stack stack; + TreeNode* cur = root; + TreeNode* visited = nullptr; + + while (!stack.empty() || cur) { + while (cur) { + stack.push(cur); + cur = cur->left; + } + + cur = stack.top(); + if (cur->right && cur->right != visited) { + cur = cur->right; + continue; + } + + stack.pop(); + if (!cur->left && !cur->right && cur->val == target) { + if (stack.empty()) return nullptr; + + TreeNode* parent = stack.top(); + if (parent->left == cur) { + parent->left = nullptr; + } else if (parent->right == cur) { + parent->right = nullptr; + } + } else { + visited = cur; + } + + cur = nullptr; + } + + return root; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @param {number} target + * @return {TreeNode} + */ + removeLeafNodes(root, target) { + if (!root) return null; + + const stack = []; + let cur = root, visited = null; + + while (stack.length > 0 || cur !== null) { + while (cur !== null) { + stack.push(cur); + cur = cur.left; + } + + cur = stack[stack.length - 1]; + if (cur.right && cur.right !== visited) { + cur = cur.right; + continue; + } + + stack.pop(); + if (!cur.left && !cur.right && cur.val === target) { + if (stack.length === 0) return null; + + const parent = stack[stack.length - 1]; + if (parent.left === cur) { + parent.left = null; + } else if (parent.right === cur) { + parent.right = null; + } + } else { + visited = cur; + } + + cur = null; + } + + return root; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/delete-node-in-a-bst.md b/articles/delete-node-in-a-bst.md new file mode 100644 index 000000000..4b086145a --- /dev/null +++ b/articles/delete-node-in-a-bst.md @@ -0,0 +1,632 @@ +## 1. Recursion - I + +::tabs-start + +```python +# 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 +class Solution: + def deleteNode(self, root: Optional[TreeNode], key: int) -> Optional[TreeNode]: + if not root: + return root + + if key > root.val: + root.right = self.deleteNode(root.right, key) + elif key < root.val: + root.left = self.deleteNode(root.left, key) + else: + if not root.left: + return root.right + elif not root.right: + return root.left + + cur = root.right + while cur.left: + cur = cur.left + root.val = cur.val + root.right = self.deleteNode(root.right, root.val) + + return root +``` + +```java +/** + * 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; + * } + * } + */ +public class Solution { + public TreeNode deleteNode(TreeNode root, int key) { + if (root == null) return root; + + if (key > root.val) { + root.right = deleteNode(root.right, key); + } else if (key < root.val) { + root.left = deleteNode(root.left, key); + } else { + if (root.left == null) return root.right; + if (root.right == null) return root.left; + + TreeNode cur = root.right; + while (cur.left != null) { + cur = cur.left; + } + root.val = cur.val; + root.right = deleteNode(root.right, root.val); + } + + return root; + } +} +``` + +```cpp +/** + * 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* deleteNode(TreeNode* root, int key) { + if (!root) return root; + + if (key > root->val) { + root->right = deleteNode(root->right, key); + } else if (key < root->val) { + root->left = deleteNode(root->left, key); + } else { + if (!root->left) return root->right; + if (!root->right) return root->left; + + TreeNode* cur = root->right; + while (cur->left) { + cur = cur->left; + } + root->val = cur->val; + root->right = deleteNode(root->right, root->val); + } + + return root; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @param {number} key + * @return {TreeNode} + */ + deleteNode(root, key) { + if (!root) return root; + + if (key > root.val) { + root.right = this.deleteNode(root.right, key); + } else if (key < root.val) { + root.left = this.deleteNode(root.left, key); + } else { + if (!root.left) return root.right; + if (!root.right) return root.left; + + let cur = root.right; + while (cur.left) { + cur = cur.left; + } + root.val = cur.val; + root.right = this.deleteNode(root.right, root.val); + } + + return root; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(h)$ +* Space complexity: $O(h)$ for the recursion stack. + +> Where $h$ is the height of the given binary search tree. + +--- + +## 2. Recursion - II + +::tabs-start + +```python +# 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 +class Solution: + def deleteNode(self, root: Optional[TreeNode], key: int) -> Optional[TreeNode]: + if not root: + return root + + if key > root.val: + root.right = self.deleteNode(root.right, key) + elif key < root.val: + root.left = self.deleteNode(root.left, key) + else: + if not root.left: + return root.right + elif not root.right: + return root.left + + cur = root.right + while cur.left: + cur = cur.left + cur.left = root.left + res = root.right + del root + return res + + return root +``` + +```java +/** + * 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; + * } + * } + */ +public class Solution { + public TreeNode deleteNode(TreeNode root, int key) { + if (root == null) return root; + + if (key > root.val) { + root.right = deleteNode(root.right, key); + } else if (key < root.val) { + root.left = deleteNode(root.left, key); + } else { + if (root.left == null) return root.right; + if (root.right == null) return root.left; + + TreeNode cur = root.right; + while (cur.left != null) { + cur = cur.left; + } + cur.left = root.left; + TreeNode res = root.right; + root = null; + return res; + } + + return root; + } +} +``` + +```cpp +/** + * 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* deleteNode(TreeNode* root, int key) { + if (!root) return root; + + if (key > root->val) { + root->right = deleteNode(root->right, key); + } else if (key < root->val) { + root->left = deleteNode(root->left, key); + } else { + if (!root->left) return root->right; + if (!root->right) return root->left; + + TreeNode* cur = root->right; + while (cur->left) { + cur = cur->left; + } + cur->left = root->left; + TreeNode* res = root->right; + delete root; + return res; + } + + return root; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @param {number} key + * @return {TreeNode} + */ + deleteNode(root, key) { + if (!root) return root; + + if (key > root.val) { + root.right = this.deleteNode(root.right, key); + } else if (key < root.val) { + root.left = this.deleteNode(root.left, key); + } else { + if (!root.left) return root.right; + if (!root.right) return root.left; + + let cur = root.right; + while (cur.left) { + cur = cur.left; + } + cur.left = root.left; + let res = root.right; + root = null; + return res; + } + + return root; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(h)$ +* Space complexity: $O(h)$ for the recursion stack. + +> Where $h$ is the height of the given binary search tree. + +--- + +## 3. Iteration + +::tabs-start + +```python +# 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 +class Solution: + def deleteNode(self, root: Optional[TreeNode], key: int) -> Optional[TreeNode]: + if not root: + return root + + parent = None + cur = root + + # Find the node to delete + while cur and cur.val != key: + parent = cur + if key > cur.val: + cur = cur.right + else: + cur = cur.left + + if not cur: + return root + + # Node with only one child or no child + if not cur.left or not cur.right: + child = cur.left if cur.left else cur.right + if not parent: + return child + if parent.left == cur: + parent.left = child + else: + parent.right = child + else: + # Node with two children + par = None # parent of right subTree min node + delNode = cur + cur = cur.right + while cur.left: + par = cur + cur = cur.left + + if par: # if there was a left traversal + par.left = cur.right + cur.right = delNode.right + + cur.left = delNode.left + + if not parent: # if we're deleting root + return cur + + if parent.left == delNode: + parent.left = cur + else: + parent.right = cur + + return root +``` + +```java +/** + * 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; + * } + * } + */ +public class Solution { + public TreeNode deleteNode(TreeNode root, int key) { + if (root == null) return root; + + TreeNode parent = null; + TreeNode cur = root; + + // Find the node to delete + while (cur != null && cur.val != key) { + parent = cur; + if (key > cur.val) { + cur = cur.right; + } else { + cur = cur.left; + } + } + + if (cur == null) return root; + + // Node with only one child or no child + if (cur.left == null || cur.right == null) { + TreeNode child = (cur.left != null) ? cur.left : cur.right; + if (parent == null) return child; + if (parent.left == cur) { + parent.left = child; + } else { + parent.right = child; + } + } else { + // Node with two children + TreeNode par = null; // parent of right subtree's min node + TreeNode delNode = cur; + cur = cur.right; + while (cur.left != null) { + par = cur; + cur = cur.left; + } + + if (par != null) { // if there was a left traversal + par.left = cur.right; + cur.right = delNode.right; + } + cur.left = delNode.left; + + if (parent == null) return cur; // if deleting root + + if (parent.left == delNode) { + parent.left = cur; + } else { + parent.right = cur; + } + } + + return root; + } +} +``` + +```cpp +/** + * 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* deleteNode(TreeNode* root, int key) { + if (!root) return root; + + TreeNode* parent = nullptr; + TreeNode* cur = root; + + // Find the node to delete + while (cur && cur->val != key) { + parent = cur; + if (key > cur->val) { + cur = cur->right; + } else { + cur = cur->left; + } + } + + if (!cur) return root; + + // Node with only one child or no child + if (!cur->left || !cur->right) { + TreeNode* child = cur->left ? cur->left : cur->right; + if (!parent) return child; + if (parent->left == cur) { + parent->left = child; + } else { + parent->right = child; + } + } else { + // Node with two children + TreeNode* par = nullptr; // parent of right subtree's min node + TreeNode* delNode = cur; + cur = cur->right; + while (cur->left) { + par = cur; + cur = cur->left; + } + + if (par) { // if there was a left traversal + par->left = cur->right; + cur->right = delNode->right; + } + cur->left = delNode->left; + + if (!parent) return cur; // if deleting root + + if (parent->left == delNode) { + parent->left = cur; + } else { + parent->right = cur; + } + } + + return root; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @param {number} key + * @return {TreeNode} + */ + deleteNode(root, key) { + if (!root) return root; + + let parent = null; + let cur = root; + + // Find the node to delete + while (cur && cur.val !== key) { + parent = cur; + if (key > cur.val) { + cur = cur.right; + } else { + cur = cur.left; + } + } + + if (!cur) return root; + + // Node with only one child or no child + if (!cur.left || !cur.right) { + const child = cur.left || cur.right; + if (!parent) return child; + if (parent.left === cur) { + parent.left = child; + } else { + parent.right = child; + } + } else { + // Node with two children + let par = null; // parent of right subtree's min node + const delNode = cur; + cur = cur.right; + while (cur.left) { + par = cur; + cur = cur.left; + } + + if (par) { // if there was a left traversal + par.left = cur.right; + cur.right = delNode.right; + } + cur.left = delNode.left; + + if (!parent) return cur; // if deleting root + + if (parent.left === delNode) { + parent.left = cur; + } else { + parent.right = cur; + } + } + + return root; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(h)$ +* Space complexity: $O(1)$ extra space. + +> Where $h$ is the height of the given binary search tree. \ No newline at end of file diff --git a/articles/house-robber-iii.md b/articles/house-robber-iii.md new file mode 100644 index 000000000..08d521c95 --- /dev/null +++ b/articles/house-robber-iii.md @@ -0,0 +1,457 @@ +## 1. Recursion + +::tabs-start + +```python +# 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 +class Solution: + def rob(self, root: Optional[TreeNode]) -> int: + if not root: + return 0 + + res = root.val + if root.left: + res += self.rob(root.left.left) + self.rob(root.left.right) + if root.right: + res += self.rob(root.right.left) + self.rob(root.right.right) + + res = max(res, self.rob(root.left) + self.rob(root.right)) + return res +``` + +```java +/** + * 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; + * } + * } + */ +public class Solution { + public int rob(TreeNode root) { + if (root == null) { + return 0; + } + + int res = root.val; + if (root.left != null) { + res += rob(root.left.left) + rob(root.left.right); + } + if (root.right != null) { + res += rob(root.right.left) + rob(root.right.right); + } + + res = Math.max(res, rob(root.left) + rob(root.right)); + return res; + } +} +``` + +```cpp +/** + * 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: + int rob(TreeNode* root) { + if (!root) { + return 0; + } + + int res = root->val; + if (root->left) { + res += rob(root->left->left) + rob(root->left->right); + } + if (root->right) { + res += rob(root->right->left) + rob(root->right->right); + } + + res = max(res, rob(root->left) + rob(root->right)); + return res; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {number} + */ + rob(root) { + if (!root) { + return 0; + } + + let res = root.val; + if (root.left) { + res += this.rob(root.left.left) + this.rob(root.left.right); + } + if (root.right) { + res += this.rob(root.right.left) + this.rob(root.right.right); + } + + res = Math.max(res, this.rob(root.left) + this.rob(root.right)); + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(2 ^ n)$ +* Space complexity: $O(n)$ for recursion stack. + +--- + +## 2. Dynamic Programming (Memoization) + +::tabs-start + +```python +# 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 +class Solution: + def rob(self, root: Optional[TreeNode]) -> int: + cache = { None : 0 } + + def dfs(root): + if root in cache: + return cache[root] + + cache[root] = root.val + if root.left: + cache[root] += dfs(root.left.left) + dfs(root.left.right) + if root.right: + cache[root] += dfs(root.right.left) + dfs(root.right.right) + + cache[root] = max(cache[root], dfs(root.left) + dfs(root.right)) + return cache[root] + + return dfs(root) +``` + +```java +/** + * 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; + * } + * } + */ +public class Solution { + private Map cache; + + public int rob(TreeNode root) { + cache = new HashMap<>(); + cache.put(null, 0); + return dfs(root); + } + + private int dfs(TreeNode root) { + if (cache.containsKey(root)) { + return cache.get(root); + } + + int res = root.val; + if (root.left != null) { + res += dfs(root.left.left) + dfs(root.left.right); + } + if (root.right != null) { + res += dfs(root.right.left) + dfs(root.right.right); + } + + res = Math.max(res, dfs(root.left) + dfs(root.right)); + cache.put(root, res); + return res; + } +} +``` + +```cpp +/** + * 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 { + unordered_map cache; + +public: + int rob(TreeNode* root) { + cache[nullptr] = 0; + return dfs(root); + } + +private: + int dfs(TreeNode* root) { + if (cache.find(root) != cache.end()) { + return cache[root]; + } + + int res = root->val; + if (root->left) { + res += rob(root->left->left) + rob(root->left->right); + } + if (root->right) { + res += rob(root->right->left) + rob(root->right->right); + } + + res = max(res, rob(root->left) + rob(root->right)); + cache[root] = res; + return res; + + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {number} + */ + rob(root) { + const cache = new Map(); + cache.set(null, 0); + + const dfs = (root) => { + if (cache.has(root)) { + return cache.get(root); + } + + let res = root.val; + if (root.left) { + res += dfs(root.left.left) + dfs(root.left.right); + } + if (root.right) { + res += dfs(root.right.left) + dfs(root.right.right); + } + + res = Math.max(res, dfs(root.left) + dfs(root.right)); + cache.set(root, res); + return res; + }; + + return dfs(root); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Dynamic Programming (Optimal) + +::tabs-start + +```python +# 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 +class Solution: + def rob(self, root: TreeNode) -> int: + def dfs(root): + if not root: + return [0, 0] + + leftPair = dfs(root.left) + rightPair = dfs(root.right) + + withRoot = root.val + leftPair[1] + rightPair[1] + withoutRoot = max(leftPair) + max(rightPair) + + return [withRoot, withoutRoot] + + return max(dfs(root)) +``` + +```java +/** + * 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; + * } + * } + */ +public class Solution { + public int rob(TreeNode root) { + int[] result = dfs(root); + return Math.max(result[0], result[1]); + } + + private int[] dfs(TreeNode root) { + if (root == null) { + return new int[]{0, 0}; + } + + int[] leftPair = dfs(root.left); + int[] rightPair = dfs(root.right); + + int withRoot = root.val + leftPair[1] + rightPair[1]; + int withoutRoot = Math.max(leftPair[0], leftPair[1]) + + Math.max(rightPair[0], rightPair[1]); + + return new int[]{withRoot, withoutRoot}; + } +} +``` + +```cpp +/** + * 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: + int rob(TreeNode* root) { + auto result = dfs(root); + return max(result.first, result.second); + } + +private: + pair dfs(TreeNode* root) { + if (!root) { + return {0, 0}; + } + + auto leftPair = dfs(root->left); + auto rightPair = dfs(root->right); + + int withRoot = root->val + leftPair.second + rightPair.second; + int withoutRoot = max(leftPair.first, leftPair.second) + + max(rightPair.first, rightPair.second); + + return {withRoot, withoutRoot}; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {number} + */ + rob(root) { + const dfs = (node) => { + if (!node) { + return [0, 0]; + } + + const leftPair = dfs(node.left); + const rightPair = dfs(node.right); + + const withRoot = node.val + leftPair[1] + rightPair[1]; + const withoutRoot = Math.max(...leftPair) + Math.max(...rightPair); + + return [withRoot, withoutRoot]; + }; + + const result = dfs(root); + return Math.max(...result); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ for recursion stack. \ No newline at end of file diff --git a/articles/insert-into-a-binary-search-tree.md b/articles/insert-into-a-binary-search-tree.md new file mode 100644 index 000000000..d8597610b --- /dev/null +++ b/articles/insert-into-a-binary-search-tree.md @@ -0,0 +1,292 @@ +## 1. Recursion + +::tabs-start + +```python +# 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 +class Solution: + def insertIntoBST(self, root: Optional[TreeNode], val: int) -> Optional[TreeNode]: + if not root: + return TreeNode(val) + + if val > root.val: + root.right = self.insertIntoBST(root.right, val) + else: + root.left = self.insertIntoBST(root.left, val) + + return root +``` + +```java +/** + * 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; + * } + * } + */ +public class Solution { + public TreeNode insertIntoBST(TreeNode root, int val) { + if (root == null) { + return new TreeNode(val); + } + + if (val > root.val) { + root.right = insertIntoBST(root.right, val); + } else { + root.left = insertIntoBST(root.left, val); + } + + return root; + } +} +``` + +```cpp +/** + * 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* insertIntoBST(TreeNode* root, int val) { + if (!root) { + return new TreeNode(val); + } + + if (val > root->val) { + root->right = insertIntoBST(root->right, val); + } else { + root->left = insertIntoBST(root->left, val); + } + + return root; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @param {number} val + * @return {TreeNode} + */ + insertIntoBST(root, val) { + if (!root) { + return new TreeNode(val); + } + + if (val > root.val) { + root.right = this.insertIntoBST(root.right, val); + } else { + root.left = this.insertIntoBST(root.left, val); + } + + return root; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(h)$ +* Space complexity: $O(h)$ for the recursion stack. + +> Where $h$ is the height of the given binary search tree. + +--- + +## 2. Iteration + +::tabs-start + +```python +# 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 +class Solution: + def insertIntoBST(self, root: Optional[TreeNode], val: int) -> Optional[TreeNode]: + if not root: + return TreeNode(val) + + cur = root + while True: + if val > cur.val: + if not cur.right: + cur.right = TreeNode(val) + return root + cur = cur.right + else: + if not cur.left: + cur.left = TreeNode(val) + return root + cur = cur.left +``` + +```java +/** + * 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; + * } + * } + */ +public class Solution { + public TreeNode insertIntoBST(TreeNode root, int val) { + if (root == null) { + return new TreeNode(val); + } + + TreeNode cur = root; + while (true) { + if (val > cur.val) { + if (cur.right == null) { + cur.right = new TreeNode(val); + return root; + } + cur = cur.right; + } else { + if (cur.left == null) { + cur.left = new TreeNode(val); + return root; + } + cur = cur.left; + } + } + } +} +``` + +```cpp +/** + * 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* insertIntoBST(TreeNode* root, int val) { + if (!root) { + return new TreeNode(val); + } + + TreeNode* cur = root; + while (true) { + if (val > cur->val) { + if (!cur->right) { + cur->right = new TreeNode(val); + return root; + } + cur = cur->right; + } else { + if (!cur->left) { + cur->left = new TreeNode(val); + return root; + } + cur = cur->left; + } + } + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @param {number} val + * @return {TreeNode} + */ + insertIntoBST(root, val) { + if (!root) { + return new TreeNode(val); + } + + let cur = root; + while (true) { + if (val > cur.val) { + if (!cur.right) { + cur.right = new TreeNode(val); + return root; + } + cur = cur.right; + } else { + if (!cur.left) { + cur.left = new TreeNode(val); + return root; + } + cur = cur.left; + } + } + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(h)$ +* Space complexity: $O(1)$ extra space. + +> Where $h$ is the height of the given binary search tree. \ No newline at end of file diff --git a/articles/word-break-ii.md b/articles/word-break-ii.md new file mode 100644 index 000000000..c075d2b67 --- /dev/null +++ b/articles/word-break-ii.md @@ -0,0 +1,975 @@ +## 1. Backtracking + +::tabs-start + +```python +class Solution: + def wordBreak(self, s: str, wordDict: List[str]) -> List[str]: + wordDict = set(wordDict) + + def backtrack(i): + if i == len(s): + res.append(" ".join(cur)) + return + + for j in range(i, len(s)): + w = s[i:j + 1] + if w in wordDict: + cur.append(w) + backtrack(j + 1) + cur.pop() + + cur = [] + res = [] + backtrack(0) + return res +``` + +```java +public class Solution { + private Set wordSet; + private List res; + + public List wordBreak(String s, List wordDict) { + wordSet = new HashSet<>(wordDict); + res = new ArrayList<>(); + List cur = new ArrayList<>(); + backtrack(s, 0, cur); + return res; + } + + private void backtrack(String s, int i, List cur) { + if (i == s.length()) { + res.add(String.join(" ", cur)); + return; + } + + for (int j = i; j < s.length(); j++) { + String w = s.substring(i, j + 1); + if (wordSet.contains(w)) { + cur.add(w); + backtrack(s, j + 1, cur); + cur.remove(cur.size() - 1); + } + } + } +} +``` + +```cpp +class Solution { + unordered_set wordSet; + vector res; + +public: + vector wordBreak(string s, vector& wordDict) { + wordSet = unordered_set(wordDict.begin(), wordDict.end()); + vector cur; + backtrack(s, 0, cur); + return res; + } + +private: + void backtrack(const string& s, int i, vector& cur) { + if (i == s.size()) { + res.push_back(join(cur)); + return; + } + + for (int j = i; j < s.size(); ++j) { + string w = s.substr(i, j - i + 1); + if (wordSet.count(w)) { + cur.push_back(w); + backtrack(s, j + 1, cur); + cur.pop_back(); + } + } + } + + string join(const vector& words) { + ostringstream oss; + for (int i = 0; i < words.size(); ++i) { + if (i > 0) oss << " "; + oss << words[i]; + } + return oss.str(); + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @param {string[]} wordDict + * @return {string[]} + */ + wordBreak(s, wordDict) { + const wordSet = new Set(wordDict); + const res = []; + const cur = []; + + const backtrack = (i) => { + if (i === s.length) { + res.push(cur.join(" ")); + return; + } + + for (let j = i; j < s.length; j++) { + const w = s.substring(i, j + 1); + if (wordSet.has(w)) { + cur.push(w); + backtrack(j + 1); + cur.pop(); + } + } + }; + + backtrack(0); + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m + n * 2 ^ n)$ +* Space complexity: $O(m + 2 ^ n)$ + +> Where $n$ is the length of the string $s$ and $m$ is the sum of the lengths of the strings in the $wordDict$. + +--- + +## 2. Backtracking + Trie + +::tabs-start + +```python +class TrieNode: + def __init__(self): + self.children = {} + self.isWord = False + +class Trie: + def __init__(self): + self.root = TrieNode() + + def addWord(self, word): + curr = self.root + for c in word: + if c not in curr.children: + curr.children[c] = TrieNode() + curr = curr.children[c] + curr.isWord = True + +class Solution: + def wordBreak(self, s: str, wordDict: List[str]) -> List[str]: + trie = Trie() + for word in wordDict: + trie.addWord(word) + + def backtrack(i, path): + if i == len(s): + res.append(" ".join(path)) + return + + node = trie.root + word = [] + for i in range(i, len(s)): + char = s[i] + if char not in node.children: + break + + word.append(char) + node = node.children[char] + + if node.isWord: + path.append("".join(word)) + backtrack(i + 1, path) + path.pop() + + res = [] + backtrack(0, []) + return res +``` + +```java +class TrieNode { + HashMap children = new HashMap<>(); + boolean isWord = false; +} + +class Trie { + TrieNode root; + + Trie() { + root = new TrieNode(); + } + + void addWord(String word) { + TrieNode curr = root; + for (char c : word.toCharArray()) { + curr.children.putIfAbsent(c, new TrieNode()); + curr = curr.children.get(c); + } + curr.isWord = true; + } +} + +public class Solution { + public List wordBreak(String s, List wordDict) { + Trie trie = new Trie(); + for (String word : wordDict) { + trie.addWord(word); + } + + List res = new ArrayList<>(); + backtrack(0, s, new ArrayList<>(), trie, res); + return res; + } + + private void backtrack(int index, String s, List path, Trie trie, List res) { + if (index == s.length()) { + res.add(String.join(" ", path)); + return; + } + + TrieNode node = trie.root; + StringBuilder word = new StringBuilder(); + for (int i = index; i < s.length(); i++) { + char c = s.charAt(i); + if (!node.children.containsKey(c)) { + break; + } + + word.append(c); + node = node.children.get(c); + + if (node.isWord) { + path.add(word.toString()); + backtrack(i + 1, s, path, trie, res); + path.remove(path.size() - 1); + } + } + } +} +``` + +```cpp +struct TrieNode { + unordered_map children; + bool isWord = false; +}; + +class Trie { +public: + TrieNode* root; + + Trie() { + root = new TrieNode(); + } + + void addWord(const string& word) { + TrieNode* curr = root; + for (char c : word) { + if (!curr->children.count(c)) { + curr->children[c] = new TrieNode(); + } + curr = curr->children[c]; + } + curr->isWord = true; + } +}; + +class Solution { +public: + vector wordBreak(string s, vector& wordDict) { + Trie trie; + for (const string& word : wordDict) { + trie.addWord(word); + } + + vector res; + vector path; + backtrack(0, s, path, trie, res); + return res; + } + +private: + void backtrack(int index, string& s, vector& path, Trie& trie, vector& res) { + if (index == s.size()) { + stringstream ss; + for (int i = 0; i < path.size(); ++i) { + if (i > 0) ss << " "; + ss << path[i]; + } + res.push_back(ss.str()); + return; + } + + TrieNode* node = trie.root; + string word; + for (int i = index; i < s.size(); ++i) { + char c = s[i]; + if (!node->children.count(c)) break; + + word.push_back(c); + node = node->children[c]; + + if (node->isWord) { + path.push_back(word); + backtrack(i + 1, s, path, trie, res); + path.pop_back(); + } + } + } +}; +``` + +```javascript +class TrieNode { + constructor() { + this.children = new Map(); + this.isWord = false; + } +} + +class Trie { + constructor() { + this.root = new TrieNode(); + } + + /** + * @param {string} word + * @return {void} + */ + addWord(word) { + let curr = this.root; + for (const c of word) { + if (!curr.children.has(c)) { + curr.children.set(c, new TrieNode()); + } + curr = curr.children.get(c); + } + curr.isWord = true; + } +} + +class Solution { + /** + * @param {string} s + * @param {string[]} wordDict + * @return {string[]} + */ + wordBreak(s, wordDict) { + const trie = new Trie(); + for (const word of wordDict) { + trie.addWord(word); + } + + const res = []; + const backtrack = (index, path) => { + if (index === s.length) { + res.push(path.join(" ")); + return; + } + + let node = trie.root; + let word = ""; + for (let i = index; i < s.length; i++) { + const char = s[i]; + if (!node.children.has(char)) { + break; + } + + word += char; + node = node.children.get(char); + + if (node.isWord) { + path.push(word); + backtrack(i + 1, path); + path.pop(); + } + } + }; + + backtrack(0, []); + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m + n * 2 ^ n)$ +* Space complexity: $O(m + 2 ^ n)$ + +> Where $n$ is the length of the string $s$ and $m$ is the sum of the lengths of the strings in the $wordDict$. + +--- + +## 3. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def wordBreak(self, s: str, wordDict: List[str]) -> List[str]: + wordDict = set(wordDict) + cache = {} + + def backtrack(i): + if i == len(s): + return [""] + if i in cache: + return cache[i] + + res = [] + for j in range(i, len(s)): + w = s[i:j + 1] + if w not in wordDict: + continue + strings = backtrack(j + 1) + for substr in strings: + sentence = w + if substr: + sentence += " " + substr + res.append(sentence) + cache[i] = res + return res + + return backtrack(0) +``` + +```java +public class Solution { + private Set wordSet; + private Map> cache; + + public List wordBreak(String s, List wordDict) { + wordSet = new HashSet<>(wordDict); + cache = new HashMap<>(); + return backtrack(s, 0); + } + + private List backtrack(String s, int i) { + if (i == s.length()) + return Arrays.asList(""); + if (cache.containsKey(i)) + return cache.get(i); + + List res = new ArrayList<>(); + for (int j = i; j < s.length(); j++) { + String w = s.substring(i, j + 1); + if (!wordSet.contains(w)) + continue; + List strings = backtrack(s, j + 1); + for (String substr : strings) { + String sentence = w; + if (!substr.isEmpty()) + sentence += " " + substr; + res.add(sentence); + } + } + cache.put(i, res); + return res; + } +} +``` + +```cpp +class Solution { +public: + vector wordBreak(string s, vector& wordDict) { + wordSet = unordered_set(wordDict.begin(), wordDict.end()); + cache = unordered_map>(); + return backtrack(s, 0); + } + +private: + unordered_set wordSet; + unordered_map> cache; + + vector backtrack(const string& s, int i) { + if (i == s.size()) + return {""}; + if (cache.count(i)) + return cache[i]; + + vector res; + for (int j = i; j < s.size(); ++j) { + string w = s.substr(i, j - i + 1); + if (!wordSet.count(w)) + continue; + vector strings = backtrack(s, j + 1); + for (const string& substr : strings) { + string sentence = w; + if (!substr.empty()) + sentence += " " + substr; + res.push_back(sentence); + } + } + cache[i] = res; + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @param {string[]} wordDict + * @return {string[]} + */ + wordBreak(s, wordDict) { + const wordSet = new Set(wordDict); + const cache = new Map(); + + const backtrack = (i) => { + if (i === s.length) { + return [""]; + } + if (cache.has(i)) { + return cache.get(i); + } + + const res = []; + for (let j = i; j < s.length; j++) { + const w = s.substring(i, j + 1); + if (!wordSet.has(w)) continue; + + const strings = backtrack(j + 1); + for (const substr of strings) { + let sentence = w; + if (substr) { + sentence += " " + substr; + } + res.push(sentence); + } + } + cache.set(i, res); + return res; + }; + + return backtrack(0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m + n * 2 ^ n)$ +* Space complexity: $O(m + n * 2 ^ n)$ + +> Where $n$ is the length of the string $s$ and $m$ is the sum of the lengths of the strings in the $wordDict$. + +--- + +## 4. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def wordBreak(self, s: str, wordDict: List[str]) -> List[str]: + wordSet = set(wordDict) + n = len(s) + dp = [[] for _ in range(n + 1)] + dp[0] = [""] + + for i in range(1, n + 1): + for j in range(i): + if s[j:i] in wordSet: + for sentence in dp[j]: + dp[i].append((sentence + " " + s[j:i]).strip()) + + return dp[n] +``` + +```java +public class Solution { + public List wordBreak(String s, List wordDict) { + Set wordSet = new HashSet<>(wordDict); + int n = s.length(); + List[] dp = new ArrayList[n + 1]; + for (int i = 0; i <= n; i++) { + dp[i] = new ArrayList<>(); + } + dp[0].add(""); + + for (int i = 1; i <= n; i++) { + for (int j = 0; j < i; j++) { + if (wordSet.contains(s.substring(j, i))) { + for (String sentence : dp[j]) { + dp[i].add((sentence + " " + s.substring(j, i)).trim()); + } + } + } + } + + return dp[n]; + } +} +``` + +```cpp +class Solution { +public: + vector wordBreak(string s, vector& wordDict) { + unordered_set wordSet(wordDict.begin(), wordDict.end()); + int n = s.size(); + vector> dp(n + 1); + dp[0] = {""}; + + for (int i = 1; i <= n; ++i) { + for (int j = 0; j < i; ++j) { + string word = s.substr(j, i - j); + if (wordSet.count(word)) { + for (const string& sentence : dp[j]) { + if (sentence.empty()) { + dp[i].push_back(word); + } else { + dp[i].push_back(sentence + " " + word); + } + } + } + } + } + + return dp[n]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @param {string[]} wordDict + * @return {string[]} + */ + wordBreak(s, wordDict) { + const wordSet = new Set(wordDict); + const n = s.length; + const dp = Array.from({ length: n + 1 }, () => []); + dp[0].push(""); + + for (let i = 1; i <= n; i++) { + for (let j = 0; j < i; j++) { + if (wordSet.has(s.substring(j, i))) { + for (const sentence of dp[j]) { + dp[i].push((sentence + " " + s.substring(j, i)).trim()); + } + } + } + } + + return dp[n]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m + n * 2 ^ n)$ +* Space complexity: $O(m + n * 2 ^ n)$ + +> Where $n$ is the length of the string $s$ and $m$ is the sum of the lengths of the strings in the $wordDict$. + +--- + +## 5. Dynamic Programming (Top-Down) Using Trie + +::tabs-start + +```python +class TrieNode: + def __init__(self): + self.children = {} + self.isWord = False + +class Trie: + def __init__(self): + self.root = TrieNode() + + def addWord(self, word): + curr = self.root + for c in word: + if c not in curr.children: + curr.children[c] = TrieNode() + curr = curr.children[c] + curr.isWord = True + +class Solution: + def wordBreak(self, s: str, wordDict: List[str]) -> List[str]: + trie = Trie() + for word in wordDict: + trie.addWord(word) + + cache = {} + + def backtrack(index): + if index == len(s): + return [""] + if index in cache: + return cache[index] + + res = [] + curr = trie.root + for i in range(index, len(s)): + char = s[i] + if char not in curr.children: + break + curr = curr.children[char] + if curr.isWord: + for suffix in backtrack(i + 1): + if suffix: + res.append(s[index:i + 1] + " " + suffix) + else: + res.append(s[index:i + 1]) + + cache[index] = res + return res + + return backtrack(0) +``` + +```java +class TrieNode { + Map children = new HashMap<>(); + boolean isWord = false; +} + +class Trie { + TrieNode root; + + Trie() { + root = new TrieNode(); + } + + void addWord(String word) { + TrieNode curr = root; + for (char c : word.toCharArray()) { + curr.children.putIfAbsent(c, new TrieNode()); + curr = curr.children.get(c); + } + curr.isWord = true; + } +} + +public class Solution { + public List wordBreak(String s, List wordDict) { + Trie trie = new Trie(); + for (String word : wordDict) { + trie.addWord(word); + } + + Map> cache = new HashMap<>(); + return backtrack(0, s, trie, cache); + } + + private List backtrack(int index, String s, Trie trie, Map> cache) { + if (index == s.length()) { + return Collections.singletonList(""); + } + + if (cache.containsKey(index)) { + return cache.get(index); + } + + List res = new ArrayList<>(); + TrieNode curr = trie.root; + + for (int i = index; i < s.length(); i++) { + char c = s.charAt(i); + if (!curr.children.containsKey(c)) { + break; + } + curr = curr.children.get(c); + if (curr.isWord) { + for (String suffix : backtrack(i + 1, s, trie, cache)) { + if (!suffix.isEmpty()) { + res.add(s.substring(index, i + 1) + " " + suffix); + } else { + res.add(s.substring(index, i + 1)); + } + } + } + } + + cache.put(index, res); + return res; + } +} +``` + +```cpp +struct TrieNode { + unordered_map children; + bool isWord = false; +}; + +class Trie { +public: + TrieNode* root; + + Trie() { + root = new TrieNode(); + } + + void addWord(const string& word) { + TrieNode* curr = root; + for (char c : word) { + if (!curr->children.count(c)) { + curr->children[c] = new TrieNode(); + } + curr = curr->children[c]; + } + curr->isWord = true; + } +}; + +class Solution { +public: + vector wordBreak(string s, vector& wordDict) { + Trie trie; + for (const string& word : wordDict) { + trie.addWord(word); + } + + unordered_map> cache; + return backtrack(0, s, trie, cache); + } + +private: + vector backtrack(int index, string& s, Trie& trie, unordered_map>& cache) { + if (index == s.size()) { + return {""}; + } + + if (cache.count(index)) { + return cache[index]; + } + + vector res; + TrieNode* curr = trie.root; + + for (int i = index; i < s.size(); ++i) { + char c = s[i]; + if (!curr->children.count(c)) { + break; + } + curr = curr->children[c]; + if (curr->isWord) { + for (const string& suffix : backtrack(i + 1, s, trie, cache)) { + if (!suffix.empty()) { + res.push_back(s.substr(index, i - index + 1) + " " + suffix); + } else { + res.push_back(s.substr(index, i - index + 1)); + } + } + } + } + + return cache[index] = res; + } +}; +``` + +```javascript +class TrieNode { + constructor() { + this.children = new Map(); + this.isWord = false; + } +} + +class Trie { + constructor() { + this.root = new TrieNode(); + } + + /** + * @param {string} word + * @return {void} + */ + addWord(word) { + let curr = this.root; + for (const c of word) { + if (!curr.children.has(c)) { + curr.children.set(c, new TrieNode()); + } + curr = curr.children.get(c); + } + curr.isWord = true; + } +} + +class Solution { + /** + * @param {string} s + * @param {string[]} wordDict + * @return {string[]} + */ + wordBreak(s, wordDict) { + const trie = new Trie(); + for (const word of wordDict) { + trie.addWord(word); + } + + const cache = new Map(); + + const backtrack = (index) => { + if (index === s.length) { + return [""]; + } + if (cache.has(index)) { + return cache.get(index); + } + + const res = []; + let curr = trie.root; + + for (let i = index; i < s.length; i++) { + const char = s[i]; + if (!curr.children.has(char)) { + break; + } + curr = curr.children.get(char); + if (curr.isWord) { + for (const suffix of backtrack(i + 1)) { + if (suffix) { + res.push(s.slice(index, i + 1) + " " + suffix); + } else { + res.push(s.slice(index, i + 1)); + } + } + } + } + + cache.set(index, res); + return res; + }; + + return backtrack(0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m + n * 2 ^ n)$ +* Space complexity: $O(m + n * 2 ^ n)$ + +> Where $n$ is the length of the string $s$ and $m$ is the sum of the lengths of the strings in the $wordDict$. \ No newline at end of file From d750e953b7a9cfdf6e80b6bcd90dd5b8f2a93a16 Mon Sep 17 00:00:00 2001 From: Sri Hari <94112314+Srihari2222@users.noreply.github.com> Date: Tue, 7 Jan 2025 04:37:24 +0530 Subject: [PATCH 25/45] Sri Hari: Batch-5/Neetcode-250/Added-articles (#3794) * Batch-5/Neetcode-250/Added-articles * Batch-5/Neetcode-250/Added-articles * Batch-5/Neetcode-250/Added-articles * Batch-5/Neetcode-250/Added-articles --- articles/accounts-merge.md | 932 ++++++++++++ articles/add-binary.md | 240 +++ articles/bitwise-and-of-numbers-range.md | 292 ++++ articles/candy.md | 399 +++++ articles/dota2-senate.md | 423 ++++++ articles/evaluate-division.md | 836 +++++++++++ articles/excel-sheet-column-title.md | 142 ++ ...critical-edges-in-minimum-spanning-tree.md | 1281 +++++++++++++++++ .../greatest-common-divisor-of-strings.md | 459 ++++++ articles/jump-game-vii.md | 526 +++++++ articles/minimum-array-end.md | 320 ++++ articles/minimum-height-trees.md | 847 +++++++++++ articles/path-with-minimum-effort.md | 851 +++++++++++ articles/reorganize-string.md | 546 +++++++ articles/roman-to-integer.md | 95 ++ articles/stone-game-iii.md | 491 +++++++ hints/cheapest-flight-path.md | 2 +- 17 files changed, 8681 insertions(+), 1 deletion(-) create mode 100644 articles/accounts-merge.md create mode 100644 articles/add-binary.md create mode 100644 articles/bitwise-and-of-numbers-range.md create mode 100644 articles/candy.md create mode 100644 articles/dota2-senate.md create mode 100644 articles/evaluate-division.md create mode 100644 articles/excel-sheet-column-title.md create mode 100644 articles/find-critical-and-pseudo-critical-edges-in-minimum-spanning-tree.md create mode 100644 articles/greatest-common-divisor-of-strings.md create mode 100644 articles/jump-game-vii.md create mode 100644 articles/minimum-array-end.md create mode 100644 articles/minimum-height-trees.md create mode 100644 articles/path-with-minimum-effort.md create mode 100644 articles/reorganize-string.md create mode 100644 articles/roman-to-integer.md create mode 100644 articles/stone-game-iii.md diff --git a/articles/accounts-merge.md b/articles/accounts-merge.md new file mode 100644 index 000000000..665334798 --- /dev/null +++ b/articles/accounts-merge.md @@ -0,0 +1,932 @@ +## 1. Depth First Search + +::tabs-start + +```python +class Solution: + def accountsMerge(self, accounts: List[List[str]]) -> List[List[str]]: + n = len(accounts) + emailIdx = {} # email -> id + emails = [] # set of emails of all accounts + emailToAcc = {} # email_index -> account_Id + + m = 0 + for accId, a in enumerate(accounts): + for i in range(1, len(a)): + email = a[i] + if email in emailIdx: + continue + emails.append(email) + emailIdx[email] = m + emailToAcc[m] = accId + m += 1 + + adj = [[] for _ in range(m)] + for a in accounts: + for i in range(2, len(a)): + id1 = emailIdx[a[i]] + id2 = emailIdx[a[i - 1]] + adj[id1].append(id2) + adj[id2].append(id1) + + emailGroup = defaultdict(list) # index of acc -> list of emails + visited = [False] * m + def dfs(node, accId): + visited[node] = True + emailGroup[accId].append(emails[node]) + for nei in adj[node]: + if not visited[nei]: + dfs(nei, accId) + + for i in range(m): + if not visited[i]: + dfs(i, emailToAcc[i]) + + res = [] + for accId in emailGroup: + name = accounts[accId][0] + res.append([name] + sorted(emailGroup[accId])) + + return res +``` + +```java +public class Solution { + private Map emailIdx = new HashMap<>(); // email -> id + private List emails = new ArrayList<>(); // set of emails of all accounts + private Map emailToAcc = new HashMap<>(); // email_index -> account_Id + private List> adj; + private Map> emailGroup = new HashMap<>(); // index of acc -> list of emails + private boolean[] visited; + + public List> accountsMerge(List> accounts) { + int n = accounts.size(); + int m = 0; + + // Build email index and mappings + for (int accId = 0; accId < n; accId++) { + List account = accounts.get(accId); + for (int i = 1; i < account.size(); i++) { + String email = account.get(i); + if (!emailIdx.containsKey(email)) { + emails.add(email); + emailIdx.put(email, m); + emailToAcc.put(m, accId); + m++; + } + } + } + + // Build adjacency list + adj = new ArrayList<>(); + for (int i = 0; i < m; i++) { + adj.add(new ArrayList<>()); + } + for (List account : accounts) { + for (int i = 2; i < account.size(); i++) { + int id1 = emailIdx.get(account.get(i)); + int id2 = emailIdx.get(account.get(i - 1)); + adj.get(id1).add(id2); + adj.get(id2).add(id1); + } + } + + // Initialize visited array + visited = new boolean[m]; + + // DFS traversal + for (int i = 0; i < m; i++) { + if (!visited[i]) { + int accId = emailToAcc.get(i); + emailGroup.putIfAbsent(accId, new ArrayList<>()); + dfs(i, accId); + } + } + + // Build result + List> res = new ArrayList<>(); + for (int accId : emailGroup.keySet()) { + List group = emailGroup.get(accId); + Collections.sort(group); + List merged = new ArrayList<>(); + merged.add(accounts.get(accId).get(0)); + merged.addAll(group); + res.add(merged); + } + + return res; + } + + private void dfs(int node, int accId) { + visited[node] = true; + emailGroup.get(accId).add(emails.get(node)); + for (int neighbor : adj.get(node)) { + if (!visited[neighbor]) { + dfs(neighbor, accId); + } + } + } +} +``` + +```cpp +class Solution { + unordered_map emailIdx; // email -> id + vector emails; // set of emails of all accounts + unordered_map emailToAcc; // email_index -> account_Id + vector> adj; + unordered_map> emailGroup; // index of acc -> list of emails + vector visited; + +public: + vector> accountsMerge(vector>& accounts) { + int n = accounts.size(); + int m = 0; + + // Build email index and mappings + for (int accId = 0; accId < n; accId++) { + vector& account = accounts[accId]; + for (int i = 1; i < account.size(); i++) { + string& email = account[i]; + if (emailIdx.find(email) == emailIdx.end()) { + emails.push_back(email); + emailIdx[email] = m; + emailToAcc[m] = accId; + m++; + } + } + } + + // Build adjacency list + adj.resize(m); + for (auto& account : accounts) { + for (int i = 2; i < account.size(); i++) { + int id1 = emailIdx[account[i]]; + int id2 = emailIdx[account[i - 1]]; + adj[id1].push_back(id2); + adj[id2].push_back(id1); + } + } + + visited.resize(m, false); + // DFS traversal + for (int i = 0; i < m; i++) { + if (!visited[i]) { + int accId = emailToAcc[i]; + dfs(i, accId); + } + } + + // Build result + vector> res; + for (auto& [accId, group] : emailGroup) { + sort(group.begin(), group.end()); + vector merged; + merged.push_back(accounts[accId][0]); + merged.insert(merged.end(), group.begin(), group.end()); + res.push_back(merged); + } + + return res; + } + +private: + void dfs(int node, int& accId) { + visited[node] = true; + emailGroup[accId].push_back(emails[node]); + for (int& neighbor : adj[node]) { + if (!visited[neighbor]) { + dfs(neighbor, accId); + } + } + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[][]} accounts + * @return {string[][]} + */ + accountsMerge(accounts) { + const emailIdx = new Map(); // email -> id + const emails = []; // set of emails of all accounts + const emailToAcc = new Map(); // email_index -> account_Id + const adj = []; + const emailGroup = new Map(); // index of acc -> list of emails + let visited = []; + + const n = accounts.length; + let m = 0; + + // Build email index and mappings + for (let accId = 0; accId < n; accId++) { + const account = accounts[accId]; + for (let i = 1; i < account.length; i++) { + const email = account[i]; + if (!emailIdx.has(email)) { + emails.push(email); + emailIdx.set(email, m); + emailToAcc.set(m, accId); + m++; + } + } + } + + // Build adjacency list + for (let i = 0; i < m; i++) { + adj.push([]); + } + for (const account of accounts) { + for (let i = 2; i < account.length; i++) { + const id1 = emailIdx.get(account[i]); + const id2 = emailIdx.get(account[i - 1]); + adj[id1].push(id2); + adj[id2].push(id1); + } + } + + // Initialize visited array + visited = Array(m).fill(false); + + // DFS traversal + const dfs = (node, accId) => { + visited[node] = true; + emailGroup.get(accId).push(emails[node]); + for (const neighbor of adj[node]) { + if (!visited[neighbor]) { + dfs(neighbor, accId); + } + } + }; + + for (let i = 0; i < m; i++) { + if (!visited[i]) { + const accId = emailToAcc.get(i); + if (!emailGroup.has(accId)) { + emailGroup.set(accId, []); + } + dfs(i, accId); + } + } + + // Build result + const res = []; + for (const [accId, group] of emailGroup.entries()) { + group.sort(); + const merged = [accounts[accId][0], ...group]; + res.push(merged); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O((n * m)\log (n * m))$ +* Space complexity: $O(n * m)$ + +> Where $n$ is the number of accounts and $m$ is the number of emails. + +--- + +## 2. Breadth First Search + +::tabs-start + +```python +class Solution: + def accountsMerge(self, accounts: List[List[str]]) -> List[List[str]]: + n = len(accounts) + emailIdx = {} # email -> id + emails = [] # set of emails of all accounts + emailToAcc = {} # email_index -> account_Id + + m = 0 + for accId, a in enumerate(accounts): + for i in range(1, len(a)): + email = a[i] + if email in emailIdx: + continue + emails.append(email) + emailIdx[email] = m + emailToAcc[m] = accId + m += 1 + + adj = [[] for _ in range(m)] + for a in accounts: + for i in range(2, len(a)): + id1 = emailIdx[a[i]] + id2 = emailIdx[a[i - 1]] + adj[id1].append(id2) + adj[id2].append(id1) + + emailGroup = defaultdict(list) # index of acc -> list of emails + visited = [False] * m + + def bfs(start, accId): + queue = deque([start]) + visited[start] = True + while queue: + node = queue.popleft() + emailGroup[accId].append(emails[node]) + for nei in adj[node]: + if not visited[nei]: + visited[nei] = True + queue.append(nei) + + for i in range(m): + if not visited[i]: + bfs(i, emailToAcc[i]) + + res = [] + for accId in emailGroup: + name = accounts[accId][0] + res.append([name] + sorted(emailGroup[accId])) + + return res +``` + +```java +public class Solution { + private Map emailIdx = new HashMap<>(); // email -> id + private List emails = new ArrayList<>(); // set of emails of all accounts + private Map emailToAcc = new HashMap<>(); // email_index -> account_Id + private List> adj; + private Map> emailGroup = new HashMap<>(); // index of acc -> list of emails + private boolean[] visited; + + public List> accountsMerge(List> accounts) { + int n = accounts.size(); + int m = 0; + + // Build email index and mappings + for (int accId = 0; accId < n; accId++) { + List account = accounts.get(accId); + for (int i = 1; i < account.size(); i++) { + String email = account.get(i); + if (!emailIdx.containsKey(email)) { + emails.add(email); + emailIdx.put(email, m); + emailToAcc.put(m, accId); + m++; + } + } + } + + // Build adjacency list + adj = new ArrayList<>(); + for (int i = 0; i < m; i++) { + adj.add(new ArrayList<>()); + } + for (List account : accounts) { + for (int i = 2; i < account.size(); i++) { + int id1 = emailIdx.get(account.get(i)); + int id2 = emailIdx.get(account.get(i - 1)); + adj.get(id1).add(id2); + adj.get(id2).add(id1); + } + } + + // Initialize visited array + visited = new boolean[m]; + + // BFS traversal + for (int i = 0; i < m; i++) { + if (!visited[i]) { + int accId = emailToAcc.get(i); + emailGroup.putIfAbsent(accId, new ArrayList<>()); + bfs(i, accId); + } + } + + // Build result + List> res = new ArrayList<>(); + for (int accId : emailGroup.keySet()) { + List group = emailGroup.get(accId); + Collections.sort(group); + List merged = new ArrayList<>(); + merged.add(accounts.get(accId).get(0)); + merged.addAll(group); + res.add(merged); + } + + return res; + } + + private void bfs(int start, int accId) { + Queue queue = new LinkedList<>(); + queue.offer(start); + visited[start] = true; + + while (!queue.isEmpty()) { + int node = queue.poll(); + emailGroup.get(accId).add(emails.get(node)); + for (int neighbor : adj.get(node)) { + if (!visited[neighbor]) { + visited[neighbor] = true; + queue.offer(neighbor); + } + } + } + } +} +``` + +```cpp +class Solution { + unordered_map emailIdx; // email -> id + vector emails; // set of emails of all accounts + unordered_map emailToAcc; // email_index -> account_Id + vector> adj; + unordered_map> emailGroup; // index of acc -> list of emails + vector visited; + +public: + vector> accountsMerge(vector>& accounts) { + int n = accounts.size(); + int m = 0; + + // Build email index and mappings + for (int accId = 0; accId < n; accId++) { + vector& account = accounts[accId]; + for (int i = 1; i < account.size(); i++) { + string& email = account[i]; + if (emailIdx.find(email) == emailIdx.end()) { + emails.push_back(email); + emailIdx[email] = m; + emailToAcc[m] = accId; + m++; + } + } + } + + // Build adjacency list + adj.resize(m); + for (auto& account : accounts) { + for (int i = 2; i < account.size(); i++) { + int id1 = emailIdx[account[i]]; + int id2 = emailIdx[account[i - 1]]; + adj[id1].push_back(id2); + adj[id2].push_back(id1); + } + } + + visited.resize(m, false); + // BFS traversal + for (int i = 0; i < m; i++) { + if (!visited[i]) { + int accId = emailToAcc[i]; + bfs(i, accId); + } + } + + // Build result + vector> res; + for (auto& [accId, group] : emailGroup) { + sort(group.begin(), group.end()); + vector merged; + merged.push_back(accounts[accId][0]); + merged.insert(merged.end(), group.begin(), group.end()); + res.push_back(merged); + } + + return res; + } + +private: + void bfs(int start, int accId) { + queue q; + q.push(start); + visited[start] = true; + + while (!q.empty()) { + int node = q.front(); + q.pop(); + emailGroup[accId].push_back(emails[node]); + for (int& neighbor : adj[node]) { + if (!visited[neighbor]) { + visited[neighbor] = true; + q.push(neighbor); + } + } + } + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[][]} accounts + * @return {string[][]} + */ + accountsMerge(accounts) { + const emailIdx = new Map(); // email -> id + const emails = []; // set of emails of all accounts + const emailToAcc = new Map(); // email_index -> account_Id + const adj = []; + const emailGroup = new Map(); // index of acc -> list of emails + let visited = []; + + const n = accounts.length; + let m = 0; + + // Build email index and mappings + for (let accId = 0; accId < n; accId++) { + const account = accounts[accId]; + for (let i = 1; i < account.length; i++) { + const email = account[i]; + if (!emailIdx.has(email)) { + emails.push(email); + emailIdx.set(email, m); + emailToAcc.set(m, accId); + m++; + } + } + } + + // Build adjacency list + for (let i = 0; i < m; i++) { + adj.push([]); + } + for (const account of accounts) { + for (let i = 2; i < account.length; i++) { + const id1 = emailIdx.get(account[i]); + const id2 = emailIdx.get(account[i - 1]); + adj[id1].push(id2); + adj[id2].push(id1); + } + } + + // Initialize visited array + visited = Array(m).fill(false); + + // BFS traversal + const bfs = (start, accId) => { + const queue = new Queue([start]); + visited[start] = true; + + while (!queue.isEmpty()) { + const node = queue.pop(); + emailGroup.get(accId).push(emails[node]); + for (const neighbor of adj[node]) { + if (!visited[neighbor]) { + visited[neighbor] = true; + queue.push(neighbor); + } + } + } + }; + + for (let i = 0; i < m; i++) { + if (!visited[i]) { + const accId = emailToAcc.get(i); + if (!emailGroup.has(accId)) { + emailGroup.set(accId, []); + } + bfs(i, accId); + } + } + + // Build result + const res = []; + for (const [accId, group] of emailGroup.entries()) { + group.sort(); + const merged = [accounts[accId][0], ...group]; + res.push(merged); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O((n * m)\log (n * m))$ +* Space complexity: $O(n * m)$ + +> Where $n$ is the number of accounts and $m$ is the number of emails. + +--- + +## 3. Disjoint Set Union + +::tabs-start + +```python +class UnionFind: + def __init__(self, n): + self.par = [i for i in range(n)] + self.rank = [1] * n + + def find(self, x): + while x != self.par[x]: + self.par[x] = self.par[self.par[x]] + x = self.par[x] + return x + + def union(self, x1, x2): + p1, p2 = self.find(x1), self.find(x2) + if p1 == p2: + return False + if self.rank[p1] > self.rank[p2]: + self.par[p2] = p1 + self.rank[p1] += self.rank[p2] + else: + self.par[p1] = p2 + self.rank[p2] += self.rank[p1] + return True + +class Solution: + def accountsMerge(self, accounts: List[List[str]]) -> List[List[str]]: + uf = UnionFind(len(accounts)) + emailToAcc = {} # email -> index of acc + + for i, a in enumerate(accounts): + for e in a[1:]: + if e in emailToAcc: + uf.union(i, emailToAcc[e]) + else: + emailToAcc[e] = i + + emailGroup = defaultdict(list) # index of acc -> list of emails + for e, i in emailToAcc.items(): + leader = uf.find(i) + emailGroup[leader].append(e) + + res = [] + for i, emails in emailGroup.items(): + name = accounts[i][0] + res.append([name] + sorted(emailGroup[i])) + return res +``` + +```java +class UnionFind { + private int[] parent; + private int[] rank; + + public UnionFind(int n) { + parent = new int[n]; + rank = new int[n]; + for (int i = 0; i < n; i++) { + parent[i] = i; + rank[i] = 1; + } + } + + public int find(int x) { + if (x != parent[x]) { + parent[x] = find(parent[x]); + } + return parent[x]; + } + + public boolean union(int x1, int x2) { + int p1 = find(x1); + int p2 = find(x2); + if (p1 == p2) { + return false; + } + if (rank[p1] > rank[p2]) { + parent[p2] = p1; + rank[p1] += rank[p2]; + } else { + parent[p1] = p2; + rank[p2] += rank[p1]; + } + return true; + } +} + +public class Solution { + public List> accountsMerge(List> accounts) { + int n = accounts.size(); + UnionFind uf = new UnionFind(n); + Map emailToAcc = new HashMap<>(); // email -> index of acc + + // Build union-find structure + for (int i = 0; i < n; i++) { + List account = accounts.get(i); + for (int j = 1; j < account.size(); j++) { + String email = account.get(j); + if (emailToAcc.containsKey(email)) { + uf.union(i, emailToAcc.get(email)); + } else { + emailToAcc.put(email, i); + } + } + } + + // Group emails by leader account + Map> emailGroup = new HashMap<>(); // index of acc -> list of emails + for (Map.Entry entry : emailToAcc.entrySet()) { + String email = entry.getKey(); + int accId = entry.getValue(); + int leader = uf.find(accId); + emailGroup.putIfAbsent(leader, new ArrayList<>()); + emailGroup.get(leader).add(email); + } + + // Build result + List> res = new ArrayList<>(); + for (Map.Entry> entry : emailGroup.entrySet()) { + int accId = entry.getKey(); + List emails = entry.getValue(); + Collections.sort(emails); + List merged = new ArrayList<>(); + merged.add(accounts.get(accId).get(0)); // Add account name + merged.addAll(emails); + res.add(merged); + } + + return res; + } +} +``` + +```cpp +class UnionFind { + vector parent; + vector rank; + +public: + UnionFind(int n) { + parent.resize(n); + rank.resize(n, 1); + for (int i = 0; i < n; i++) { + parent[i] = i; + } + } + + int find(int x) { + if (x != parent[x]) { + parent[x] = find(parent[x]); + } + return parent[x]; + } + + bool unionSets(int x1, int x2) { + int p1 = find(x1); + int p2 = find(x2); + if (p1 == p2) { + return false; + } + if (rank[p1] > rank[p2]) { + parent[p2] = p1; + rank[p1] += rank[p2]; + } else { + parent[p1] = p2; + rank[p2] += rank[p1]; + } + return true; + } +}; + +class Solution { +public: + vector> accountsMerge(vector>& accounts) { + int n = accounts.size(); + UnionFind uf(n); + unordered_map emailToAcc; // email -> index of acc + + // Build union-find structure + for (int i = 0; i < n; i++) { + for (int j = 1; j < accounts[i].size(); j++) { + const string& email = accounts[i][j]; + if (emailToAcc.count(email)) { + uf.unionSets(i, emailToAcc[email]); + } else { + emailToAcc[email] = i; + } + } + } + + // Group emails by leader account + map> emailGroup; // index of acc -> list of emails + for (const auto& [email, accId] : emailToAcc) { + int leader = uf.find(accId); + emailGroup[leader].push_back(email); + } + + // Build result + vector> res; + for (auto& [accId, emails] : emailGroup) { + sort(emails.begin(), emails.end()); + vector merged; + merged.push_back(accounts[accId][0]); + merged.insert(merged.end(), emails.begin(), emails.end()); + res.push_back(merged); + } + + return res; + } +}; +``` + +```javascript +class UnionFind { + /** + * @constructor + * @param {number} n + */ + constructor(n) { + this.parent = Array.from({ length: n }, (_, i) => i); + this.rank = Array(n).fill(1); + } + + /** + * @param {number} x + * @return {number} + */ + find(x) { + if (x !== this.parent[x]) { + this.parent[x] = this.find(this.parent[x]); + } + return this.parent[x]; + } + + /** + * @param {number} x1 + * @param {number} x2 + * @return {boolean} + */ + union(x1, x2) { + const p1 = this.find(x1); + const p2 = this.find(x2); + if (p1 === p2) { + return false; + } + if (this.rank[p1] > this.rank[p2]) { + this.parent[p2] = p1; + this.rank[p1] += this.rank[p2]; + } else { + this.parent[p1] = p2; + this.rank[p2] += this.rank[p1]; + } + return true; + } +} + +class Solution { + /** + * @param {string[][]} accounts + * @return {string[][]} + */ + accountsMerge(accounts) { + const n = accounts.length; + const uf = new UnionFind(n); + const emailToAcc = new Map(); // email -> index of acc + + // Build union-find structure + for (let i = 0; i < n; i++) { + for (let j = 1; j < accounts[i].length; j++) { + const email = accounts[i][j]; + if (emailToAcc.has(email)) { + uf.union(i, emailToAcc.get(email)); + } else { + emailToAcc.set(email, i); + } + } + } + + // Group emails by leader account + const emailGroup = new Map(); // index of acc -> list of emails + for (const [email, accId] of emailToAcc.entries()) { + const leader = uf.find(accId); + if (!emailGroup.has(leader)) { + emailGroup.set(leader, []); + } + emailGroup.get(leader).push(email); + } + + // Build result + const res = []; + for (const [accId, emails] of emailGroup.entries()) { + emails.sort(); + const merged = [accounts[accId][0], ...emails]; + res.push(merged); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O((n * m)\log (n * m))$ +* Space complexity: $O(n * m)$ + +> Where $n$ is the number of accounts and $m$ is the number of emails. \ No newline at end of file diff --git a/articles/add-binary.md b/articles/add-binary.md new file mode 100644 index 000000000..424adb990 --- /dev/null +++ b/articles/add-binary.md @@ -0,0 +1,240 @@ +## 1. Iteration + +::tabs-start + +```python +class Solution: + def addBinary(self, a: str, b: str) -> str: + res = "" + carry = 0 + + a, b = a[::-1], b[::-1] + for i in range(max(len(a), len(b))): + digitA = ord(a[i]) - ord("0") if i < len(a) else 0 + digitB = ord(b[i]) - ord("0") if i < len(b) else 0 + + total = digitA + digitB + carry + char = str(total % 2) + res = char + res + carry = total // 2 + + if carry: + res = "1" + res + + return res +``` + +```java +public class Solution { + public String addBinary(String a, String b) { + StringBuilder res = new StringBuilder(); + int carry = 0; + + StringBuilder sa = new StringBuilder(a).reverse(); + StringBuilder sb = new StringBuilder(b).reverse(); + + for (int i = 0; i < Math.max(sa.length(), sb.length()); i++) { + int digitA = i < sa.length() ? sa.charAt(i) - '0' : 0; + int digitB = i < sb.length() ? sb.charAt(i) - '0' : 0; + + int total = digitA + digitB + carry; + char c = (char)((total % 2) + '0'); + res.append(c); + carry = total / 2; + } + + if (carry > 0) { + res.append('1'); + } + + return res.reverse().toString(); + } +} +``` + +```cpp +class Solution { +public: + string addBinary(string a, string b) { + string res = ""; + int carry = 0; + + reverse(a.begin(), a.end()); + reverse(b.begin(), b.end()); + + for (int i = 0; i < max(a.length(), b.length()); i++) { + int digitA = i < a.length() ? a[i] - '0' : 0; + int digitB = i < b.length() ? b[i] - '0' : 0; + + int total = digitA + digitB + carry; + char c = (total % 2) + '0'; + res += c; + carry = total / 2; + } + + if (carry) { + res += '1'; + } + reverse(res.begin(), res.end()); + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} a + * @param {string} b + * @return {string} + */ + addBinary(a, b) { + let res = []; + let carry = 0; + + a = a.split("").reverse().join(""); + b = b.split("").reverse().join(""); + + for (let i = 0; i < Math.max(a.length, b.length); i++) { + const digitA = i < a.length ? a[i] - '0' : 0; + const digitB = i < b.length ? b[i] - '0' : 0; + + const total = digitA + digitB + carry; + const char = (total % 2).toString(); + res.push(char) + carry = Math.floor(total / 2); + } + + if (carry) { + res.push('1'); + } + res.reverse() + return res.join(''); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(max(m, n))$ +* Space complexity: $O(m + n)$ + +> Where $m$ and $n$ are the lengths of the strings $a$ and $b$ respectively. + +--- + +## 2. Iteration (Optimal) + +::tabs-start + +```python +class Solution: + def addBinary(self, a: str, b: str) -> str: + res = [] + carry = 0 + + i, j = len(a) - 1, len(b) - 1 + while i >= 0 or j >= 0 or carry > 0: + digitA = int(a[i]) if i >= 0 else 0 + digitB = int(b[j]) if j >= 0 else 0 + + total = digitA + digitB + carry + res.append(total % 2) + carry = total // 2 + + i -= 1 + j -= 1 + + res.reverse() + return ''.join(map(str, res)) +``` + +```java +public class Solution { + public String addBinary(String a, String b) { + StringBuilder res = new StringBuilder(); + int carry = 0; + + int i = a.length() - 1, j = b.length() - 1; + while (i >= 0 || j >= 0 || carry > 0) { + int digitA = i >= 0 ? a.charAt(i) - '0' : 0; + int digitB = j >= 0 ? b.charAt(j) - '0' : 0; + + int total = digitA + digitB + carry; + res.append(total % 2); + carry = total / 2; + + i--; + j--; + } + + return res.reverse().toString(); + } +} +``` + +```cpp +class Solution { +public: + string addBinary(string a, string b) { + string res = ""; + int carry = 0; + + int i = a.size() - 1, j = b.size() - 1; + while (i >= 0 || j >= 0 || carry > 0) { + int digitA = i >= 0 ? a[i] - '0' : 0; + int digitB = j >= 0 ? b[j] - '0' : 0; + + int total = digitA + digitB + carry; + res += (total % 2) + '0'; + carry = total / 2; + + i--; + j--; + } + + reverse(res.begin(), res.end()); + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} a + * @param {string} b + * @return {string} + */ + addBinary(a, b) { + let res = []; + let carry = 0; + + let i = a.length - 1, j = b.length - 1; + while (i >= 0 || j >= 0 || carry > 0) { + const digitA = i >= 0 ? a[i] - "0" : 0; + const digitB = j >= 0 ? b[j] - "0" : 0; + + const total = digitA + digitB + carry; + res.push(total % 2); + carry = Math.floor(total / 2); + + i--; + j--; + } + res.reverse() + return res.join(''); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(max(m, n))$ +* Space complexity: $O(max(m, n))$ + +> Where $m$ and $n$ are the lengths of the strings $a$ and $b$ respectively. \ No newline at end of file diff --git a/articles/bitwise-and-of-numbers-range.md b/articles/bitwise-and-of-numbers-range.md new file mode 100644 index 000000000..6c5ab4521 --- /dev/null +++ b/articles/bitwise-and-of-numbers-range.md @@ -0,0 +1,292 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def rangeBitwiseAnd(self, left: int, right: int) -> int: + res = left + for i in range(left + 1, right + 1): + res &= i + return res +``` + +```java +public class Solution { + public int rangeBitwiseAnd(int left, int right) { + int res = left; + for (int i = left + 1; i <= right; i++) { + res &= i; + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int rangeBitwiseAnd(int left, int right) { + int res = left; + for (int i = left + 1; i <= right; i++) { + res &= i; + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} left + * @param {number} right + * @return {number} + */ + rangeBitwiseAnd(left, right) { + let res = left; + for (let i = left + 1; i <= right; i++) { + res &= i; + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ + +--- + +## 2. Bit Manipulation - I + +::tabs-start + +```python +class Solution: + def rangeBitwiseAnd(self, left: int, right: int) -> int: + res = 0 + for i in range(32): + bit = (left >> i) & 1 + if not bit: + continue + + remain = left % (1 << (i + 1)) + diff = (1 << (i + 1)) - remain + if right - left < diff: + res |= (1 << i) + + return res +``` + +```java +public class Solution { + public int rangeBitwiseAnd(int left, int right) { + int res = 0; + for (int i = 0; i < 32; i++) { + int bit = (left >> i) & 1; + if (bit == 0) { + continue; + } + + int remain = left % (1 << (i + 1)); + int diff = (1 << (i + 1)) - remain; + if (right - left < diff) { + res |= (1 << i); + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int rangeBitwiseAnd(int left, int right) { + int res = 0; + for (int i = 0; i < 32; i++) { + int bit = (left >> i) & 1; + if (!bit) { + continue; + } + + int remain = left % (1 << (i + 1)); + uint diff = (1ul << (i + 1)) - remain; + if (right - left < diff) { + res |= (1 << i); + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} left + * @param {number} right + * @return {number} + */ + rangeBitwiseAnd(left, right) { + let res = 0; + for (let i = 0; i < 32; i++) { + const bit = (left >> i) & 1; + if (!bit) { + continue; + } + const next = Math.pow(2, i + 1); + const remain = left % next; + const diff = next - remain; + if (right - left < diff) { + res |= (1 << i); + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(1)$ since we iterate $32$ times. +* Space complexity: $O(1)$ + +--- + +## 3. Bit Manipulation - II + +::tabs-start + +```python +class Solution: + def rangeBitwiseAnd(self, left: int, right: int) -> int: + i = 0 + while left != right: + left >>= 1 + right >>= 1 + i += 1 + return left << i +``` + +```java +public class Solution { + public int rangeBitwiseAnd(int left, int right) { + int i = 0; + while (left != right) { + left >>= 1; + right >>= 1; + i++; + } + return left << i; + } +} +``` + +```cpp +class Solution { +public: + int rangeBitwiseAnd(int left, int right) { + int i = 0; + while (left != right) { + left >>= 1; + right >>= 1; + i++; + } + return left << i; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} left + * @param {number} right + * @return {number} + */ + rangeBitwiseAnd(left, right) { + let i = 0; + while (left !== right) { + left >>= 1; + right >>= 1; + i++; + } + return left << i; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(1)$ +* Space complexity: $O(1)$ + +--- + +## 4. Bit Manipulation - III + +::tabs-start + +```python +class Solution: + def rangeBitwiseAnd(self, left: int, right: int) -> int: + while left < right: + right &= right - 1 + return right +``` + +```java +public class Solution { + public int rangeBitwiseAnd(int left, int right) { + while (left < right) { + right &= (right - 1); + } + return right; + } +} +``` + +```cpp +class Solution { +public: + int rangeBitwiseAnd(int left, int right) { + while (left < right) { + right &= (right - 1); + } + return right; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} left + * @param {number} right + * @return {number} + */ + rangeBitwiseAnd(left, right) { + while (left < right) { + right &= (right - 1); + } + return right; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(1)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/candy.md b/articles/candy.md new file mode 100644 index 000000000..adeba0a66 --- /dev/null +++ b/articles/candy.md @@ -0,0 +1,399 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def candy(self, ratings: List[int]) -> int: + n = len(ratings) + arr = [1] * n + + for i in range(n - 1): + if ratings[i] == ratings[i + 1]: + continue + if ratings[i + 1] > ratings[i]: + arr[i + 1] = arr[i] + 1 + elif arr[i] == arr[i + 1]: + arr[i + 1] = arr[i] + arr[i] += 1 + for j in range(i - 1, -1, -1): + if ratings[j] > ratings[j + 1]: + if arr[j + 1] < arr[j]: + break + arr[j] += 1 + + return sum(arr) +``` + +```java +public class Solution { + public int candy(int[] ratings) { + int n = ratings.length; + int[] arr = new int[n]; + for (int i = 0; i < n; i++) { + arr[i] = 1; + } + + for (int i = 0; i < n - 1; i++) { + if (ratings[i] == ratings[i + 1]) { + continue; + } + if (ratings[i + 1] > ratings[i]) { + arr[i + 1] = arr[i] + 1; + } else if (arr[i] == arr[i + 1]) { + arr[i + 1] = arr[i]; + arr[i]++; + for (int j = i - 1; j >= 0; j--) { + if (ratings[j] > ratings[j + 1]) { + if (arr[j + 1] < arr[j]) { + break; + } + arr[j]++; + } + } + } + } + + int sum = 0; + for (int num : arr) { + sum += num; + } + return sum; + } +} +``` + +```cpp +class Solution { +public: + int candy(vector& ratings) { + int n = ratings.size(); + vector arr(n, 1); + + for (int i = 0; i < n - 1; i++) { + if (ratings[i] == ratings[i + 1]) { + continue; + } + if (ratings[i + 1] > ratings[i]) { + arr[i + 1] = arr[i] + 1; + } else if (arr[i] == arr[i + 1]) { + arr[i + 1] = arr[i]; + arr[i]++; + for (int j = i - 1; j >= 0; j--) { + if (ratings[j] > ratings[j + 1]) { + if (arr[j + 1] < arr[j]) { + break; + } + arr[j]++; + } + } + } + } + + return accumulate(arr.begin(), arr.end(), 0); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} ratings + * @return {number} + */ + candy(ratings) { + const n = ratings.length; + const arr = new Array(n).fill(1); + + for (let i = 0; i < n - 1; i++) { + if (ratings[i] === ratings[i + 1]) { + continue; + } + if (ratings[i + 1] > ratings[i]) { + arr[i + 1] = arr[i] + 1; + } else if (arr[i] === arr[i + 1]) { + arr[i + 1] = arr[i]; + arr[i]++; + for (let j = i - 1; j >= 0; j--) { + if (ratings[j] > ratings[j + 1]) { + if (arr[j + 1] < arr[j]) { + break; + } + arr[j]++; + } + } + } + } + + return arr.reduce((sum, num) => sum + num, 0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n)$ + +--- + +## 2. Greedy (Two Pass) + +::tabs-start + +```python +class Solution: + def candy(self, ratings: List[int]) -> int: + n = len(ratings) + arr = [1] * n + + for i in range(1, n): + if ratings[i - 1] < ratings[i]: + arr[i] = arr[i - 1] + 1 + + for i in range(n - 2, -1, -1): + if ratings[i] > ratings[i + 1]: + arr[i] = max(arr[i], arr[i + 1] + 1) + + return sum(arr) +``` + +```java +public class Solution { + public int candy(int[] ratings) { + int n = ratings.length; + int[] arr = new int[n]; + Arrays.fill(arr, 1); + + for (int i = 1; i < n; i++) { + if (ratings[i - 1] < ratings[i]) { + arr[i] = arr[i - 1] + 1; + } + } + + for (int i = n - 2; i >= 0; i--) { + if (ratings[i] > ratings[i + 1]) { + arr[i] = Math.max(arr[i], arr[i + 1] + 1); + } + } + + int sum = 0; + for (int num : arr) { + sum += num; + } + return sum; + } +} +``` + +```cpp +class Solution { +public: + int candy(vector& ratings) { + int n = ratings.size(); + vector arr(n, 1); + + for (int i = 1; i < n; i++) { + if (ratings[i - 1] < ratings[i]) { + arr[i] = arr[i - 1] + 1; + } + } + + for (int i = n - 2; i >= 0; i--) { + if (ratings[i] > ratings[i + 1]) { + arr[i] = max(arr[i], arr[i + 1] + 1); + } + } + + return accumulate(arr.begin(), arr.end(), 0); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} ratings + * @return {number} + */ + candy(ratings) { + const n = ratings.length; + const arr = new Array(n).fill(1); + + for (let i = 1; i < n; i++) { + if (ratings[i - 1] < ratings[i]) { + arr[i] = arr[i - 1] + 1; + } + } + + for (let i = n - 2; i >= 0; i--) { + if (ratings[i] > ratings[i + 1]) { + arr[i] = Math.max(arr[i], arr[i + 1] + 1); + } + } + + return arr.reduce((sum, num) => sum + num, 0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Greedy (One Pass) + +::tabs-start + +```python +class Solution: + def candy(self, ratings: List[int]) -> int: + n = len(ratings) + res = n + + i = 1 + while i < n: + if ratings[i] == ratings[i - 1]: + i += 1 + continue + + inc = 0 + while i < n and ratings[i] > ratings[i - 1]: + inc += 1 + res += inc + i += 1 + + dec = 0 + while i < n and ratings[i] < ratings[i - 1]: + dec += 1 + res += dec + i += 1 + + res -= min(inc, dec) + + return res +``` + +```java +public class Solution { + public int candy(int[] ratings) { + int n = ratings.length; + int res = n; + + int i = 1; + while (i < n) { + if (ratings[i] == ratings[i - 1]) { + i++; + continue; + } + + int inc = 0; + while (i < n && ratings[i] > ratings[i - 1]) { + inc++; + res += inc; + i++; + } + + int dec = 0; + while (i < n && ratings[i] < ratings[i - 1]) { + dec++; + res += dec; + i++; + } + + res -= Math.min(inc, dec); + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int candy(vector& ratings) { + int n = ratings.size(); + int res = n; + + int i = 1; + while (i < n) { + if (ratings[i] == ratings[i - 1]) { + i++; + continue; + } + + int inc = 0; + while (i < n && ratings[i] > ratings[i - 1]) { + inc++; + res += inc; + i++; + } + + int dec = 0; + while (i < n && ratings[i] < ratings[i - 1]) { + dec++; + res += dec; + i++; + } + + res -= min(inc, dec); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} ratings + * @return {number} + */ + candy(ratings) { + const n = ratings.length; + let res = n; + + let i = 1; + while (i < n) { + if (ratings[i] === ratings[i - 1]) { + i++; + continue; + } + + let inc = 0; + while (i < n && ratings[i] > ratings[i - 1]) { + inc++; + res += inc; + i++; + } + + let dec = 0; + while (i < n && ratings[i] < ratings[i - 1]) { + dec++; + res += dec; + i++; + } + + res -= Math.min(inc, dec); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. \ No newline at end of file diff --git a/articles/dota2-senate.md b/articles/dota2-senate.md new file mode 100644 index 000000000..4e30f1d55 --- /dev/null +++ b/articles/dota2-senate.md @@ -0,0 +1,423 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def predictPartyVictory(self, senate: str) -> str: + s = list(senate) + while True: + if 'R' not in s: + return "Dire" + if 'D' not in s: + return "Radiant" + + i = 0 + while i < len(s): + if s[i] == 'R': + j = (i + 1) % len(s) + while s[j] == 'R': + j = (j + 1) % len(s) + s.pop(j) + if j < i: + i -= 1 + else: + j = (i + 1) % len(s) + while s[j] == 'D': + j = (j + 1) % len(s) + s.pop(j) + if j < i: + i -= 1 + i += 1 +``` + +```java +public class Solution { + public String predictPartyVictory(String senate) { + List s = new ArrayList<>(); + for (char c : senate.toCharArray()) { + s.add(c); + } + + while (true) { + if (!s.contains('R')) { + return "Dire"; + } + if (!s.contains('D')) { + return "Radiant"; + } + + int i = 0; + while (i < s.size()) { + if (s.get(i) == 'R') { + int j = (i + 1) % s.size(); + while (s.get(j) == 'R') { + j = (j + 1) % s.size(); + } + s.remove(j); + if (j < i) { + i--; + } + } else { + int j = (i + 1) % s.size(); + while (s.get(j) == 'D') { + j = (j + 1) % s.size(); + } + s.remove(j); + if (j < i) { + i--; + } + } + i++; + } + } + } +} +``` + +```cpp +class Solution { +public: + string predictPartyVictory(string senate) { + vector s(senate.begin(), senate.end()); + + while (true) { + if (find(s.begin(), s.end(), 'R') == s.end()) { + return "Dire"; + } + if (find(s.begin(), s.end(), 'D') == s.end()) { + return "Radiant"; + } + + int i = 0; + while (i < s.size()) { + if (s[i] == 'R') { + int j = (i + 1) % s.size(); + while (s[j] == 'R') { + j = (j + 1) % s.size(); + } + s.erase(s.begin() + j); + if (j < i) { + i--; + } + } else { + int j = (i + 1) % s.size(); + while (s[j] == 'D') { + j = (j + 1) % s.size(); + } + s.erase(s.begin() + j); + if (j < i) { + i--; + } + } + i++; + } + } + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} senate + * @return {string} + */ + predictPartyVictory(senate) { + const s = senate.split(""); + + while (true) { + if (!s.includes("R")) { + return "Dire"; + } + if (!s.includes("D")) { + return "Radiant"; + } + + let i = 0; + while (i < s.length) { + if (s[i] === "R") { + let j = (i + 1) % s.length; + while (s[j] === "R") { + j = (j + 1) % s.length; + } + s.splice(j, 1); + if (j < i) { + i--; + } + } else { + let j = (i + 1) % s.length; + while (s[j] === "D") { + j = (j + 1) % s.length; + } + s.splice(j, 1); + if (j < i) { + i--; + } + } + i++; + } + } + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n)$ + +--- + +## 2. Greedy (Two Queues) + +::tabs-start + +```python +class Solution: + def predictPartyVictory(self, senate: str) -> str: + D, R = deque(), deque() + n = len(senate) + + for i, c in enumerate(senate): + if c == 'R': + R.append(i) + else: + D.append(i) + + while D and R: + dTurn = D.popleft() + rTurn = R.popleft() + + if rTurn < dTurn: + R.append(rTurn + n) + else: + D.append(dTurn + n) + + return "Radiant" if R else "Dire" +``` + +```java +public class Solution { + public String predictPartyVictory(String senate) { + Queue R = new LinkedList<>(); + Queue D = new LinkedList<>(); + int n = senate.length(); + + for (int i = 0; i < n; i++) { + if (senate.charAt(i) == 'R') { + R.add(i); + } else { + D.add(i); + } + } + + while (!R.isEmpty() && !D.isEmpty()) { + int rTurn = R.poll(); + int dTurn = D.poll(); + + if (rTurn < dTurn) { + R.add(rTurn + n); + } else { + D.add(dTurn + n); + } + } + + return R.isEmpty() ? "Dire" : "Radiant"; + } +} +``` + +```cpp +class Solution { +public: + string predictPartyVictory(string senate) { + queue R, D; + int n = senate.size(); + + for (int i = 0; i < n; i++) { + if (senate[i] == 'R') { + R.push(i); + } else { + D.push(i); + } + } + + while (!R.empty() && !D.empty()) { + int rTurn = R.front(); R.pop(); + int dTurn = D.front(); D.pop(); + + if (rTurn < dTurn) { + R.push(rTurn + n); + } else { + D.push(dTurn + n); + } + } + + return R.empty() ? "Dire" : "Radiant"; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} senate + * @return {string} + */ + predictPartyVictory(senate) { + const R = new Queue(); + const D = new Queue(); + const n = senate.length; + + for (let i = 0; i < n; i++) { + if (senate[i] === "R") { + R.push(i); + } else { + D.push(i); + } + } + + while (!R.isEmpty() && !D.isEmpty()) { + const rTurn = R.pop(); + const dTurn = D.pop(); + + if (rTurn < dTurn) { + R.push(rTurn + n); + } else { + D.push(dTurn + n); + } + } + + return !R.isEmpty() ? "Radiant" : "Dire"; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Greedy + +::tabs-start + +```python +class Solution: + def predictPartyVictory(self, senate: str) -> str: + senate = list(senate) + cnt = i = 0 + + while i < len(senate): + c = senate[i] + if c == 'R': + if cnt < 0: + senate.append('D') + cnt += 1 + else: + if cnt > 0: + senate.append('R') + cnt -= 1 + i += 1 + + return "Radiant" if cnt > 0 else "Dire" +``` + +```java +public class Solution { + public String predictPartyVictory(String senate) { + StringBuilder sb = new StringBuilder(senate); + int cnt = 0, i = 0; + + while (i < sb.length()) { + char c = sb.charAt(i); + if (c == 'R') { + if (cnt < 0) { + sb.append('D'); + } + cnt++; + } else { + if (cnt > 0) { + sb.append('R'); + } + cnt--; + } + i++; + } + + return cnt > 0 ? "Radiant" : "Dire"; + } +} +``` + +```cpp +class Solution { +public: + string predictPartyVictory(string senate) { + int cnt = 0, i = 0; + + while (i < senate.size()) { + char c = senate[i]; + if (c == 'R') { + if (cnt < 0) { + senate.push_back('D'); + } + cnt++; + } else { + if (cnt > 0) { + senate.push_back('R'); + } + cnt--; + } + i++; + } + + return cnt > 0 ? "Radiant" : "Dire"; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} senate + * @return {string} + */ + predictPartyVictory(senate) { + let s = senate.split(''); + let cnt = 0, i = 0; + + while (i < s.length) { + const c = s[i]; + if (c === 'R') { + if (cnt < 0) { + s.push('D'); + } + cnt++; + } else { + if (cnt > 0) { + s.push('R'); + } + cnt--; + } + i++; + } + + return cnt > 0 ? "Radiant" : "Dire"; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/evaluate-division.md b/articles/evaluate-division.md new file mode 100644 index 000000000..647a8002b --- /dev/null +++ b/articles/evaluate-division.md @@ -0,0 +1,836 @@ +## 1. Breadth First Search + +::tabs-start + +```python +class Solution: + def calcEquation(self, equations: List[List[str]], values: List[float], queries: List[List[str]]) -> List[float]: + adj = collections.defaultdict(list) # Map a -> list of [b, a/b] + + for i, eq in enumerate(equations): + a, b = eq + adj[a].append((b, values[i])) + adj[b].append((a, 1 / values[i])) + + def bfs(src, target): + if src not in adj or target not in adj: + return -1 + q, visit = deque([(src, 1)]), set() + visit.add(src) + + while q: + node, w = q.popleft() + if node == target: + return w + for nei, weight in adj[node]: + if nei not in visit: + q.append((nei, w * weight)) + visit.add(nei) + return -1 + + return [bfs(q[0], q[1]) for q in queries] +``` + +```java +public class Solution { + public double[] calcEquation(List> equations, double[] values, List> queries) { + Map> adj = new HashMap<>(); // Map a -> list of [b, a/b] + + for (int i = 0; i < equations.size(); i++) { + String a = equations.get(i).get(0); + String b = equations.get(i).get(1); + adj.putIfAbsent(a, new ArrayList<>()); + adj.putIfAbsent(b, new ArrayList<>()); + adj.get(a).add(new Pair(b, values[i])); + adj.get(b).add(new Pair(a, 1 / values[i])); + } + + double[] res = new double[queries.size()]; + for (int i = 0; i < queries.size(); i++) { + String src = queries.get(i).get(0); + String target = queries.get(i).get(1); + res[i] = bfs(src, target, adj); + } + + return res; + } + + private double bfs(String src, String target, Map> adj) { + if (!adj.containsKey(src) || !adj.containsKey(target)) { + return -1.0; + } + + Queue q = new LinkedList<>(); + Set visited = new HashSet<>(); + q.offer(new Pair(src, 1.0)); + visited.add(src); + + while (!q.isEmpty()) { + Pair current = q.poll(); + String node = current.node; + double weight = current.weight; + + if (node.equals(target)) { + return weight; + } + + for (Pair neighbor : adj.get(node)) { + if (!visited.contains(neighbor.node)) { + visited.add(neighbor.node); + q.offer(new Pair(neighbor.node, weight * neighbor.weight)); + } + } + } + + return -1.0; + } + + class Pair { + String node; + double weight; + + Pair(String node, double weight) { + this.node = node; + this.weight = weight; + } + } +} +``` + +```cpp +class Solution { +public: + vector calcEquation(vector>& equations, vector& values, vector>& queries) { + unordered_map>> adj; // Map a -> list of [b, a/b] + + for (int i = 0; i < equations.size(); i++) { + string a = equations[i][0]; + string b = equations[i][1]; + adj[a].emplace_back(b, values[i]); + adj[b].emplace_back(a, 1.0 / values[i]); + } + + vector res; + for (const auto& query : queries) { + string src = query[0]; + string target = query[1]; + res.push_back(bfs(src, target, adj)); + } + + return res; + } + +private: + double bfs(const string& src, const string& target, unordered_map>>& adj) { + if (!adj.count(src) || !adj.count(target)) { + return -1.0; + } + + queue> q; + unordered_set visited; + q.emplace(src, 1.0); + visited.insert(src); + + while (!q.empty()) { + auto [node, weight] = q.front(); + q.pop(); + + if (node == target) { + return weight; + } + + for (const auto& [nei, neiWeight] : adj[node]) { + if (!visited.count(nei)) { + visited.insert(nei); + q.emplace(nei, weight * neiWeight); + } + } + } + + return -1.0; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[][]} equations + * @param {number[]} values + * @param {string[][]} queries + * @return {number[]} + */ + calcEquation(equations, values, queries) { + const adj = new Map(); // Map a -> list of [b, a/b] + + for (let i = 0; i < equations.length; i++) { + const [a, b] = equations[i]; + if (!adj.has(a)) adj.set(a, []); + if (!adj.has(b)) adj.set(b, []); + adj.get(a).push([b, values[i]]); + adj.get(b).push([a, 1 / values[i]]); + } + + const bfs = (src, target) => { + if (!adj.has(src) || !adj.has(target)) return -1; + + const queue = new Queue([[src, 1.0]]); + const visited = new Set(); + visited.add(src); + + while (!queue.isEmpty()) { + const [node, weight] = queue.pop(); + + if (node === target) return weight; + + for (const [nei, neiWeight] of adj.get(node)) { + if (!visited.has(nei)) { + visited.add(nei); + queue.push([nei, weight * neiWeight]); + } + } + } + + return -1; + }; + + return queries.map(([src, target]) => bfs(src, target)); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n)$ +* Space complexity: $O(n + m)$ + +> Where $n$ is the number of unique strings and $m$ is the number of queries. + +--- + +## 2. Depth First Search + +::tabs-start + +```python +class Solution: + def calcEquation(self, equations: List[List[str]], values: List[float], queries: List[List[str]]) -> List[float]: + adj = collections.defaultdict(list) # Map a -> list of [b, a/b] + + for i, eq in enumerate(equations): + a, b = eq + adj[a].append((b, values[i])) + adj[b].append((a, 1 / values[i])) + + def dfs(src, target, visited): + if src not in adj or target not in adj: + return -1 + if src == target: + return 1 + + visited.add(src) + + for nei, weight in adj[src]: + if nei not in visited: + result = dfs(nei, target, visited) + if result != -1: + return weight * result + + return -1 + + return [dfs(q[0], q[1], set()) for q in queries] +``` + +```java +public class Solution { + public double[] calcEquation(List> equations, double[] values, List> queries) { + Map> adj = new HashMap<>(); // Map a -> list of [b, a/b] + + for (int i = 0; i < equations.size(); i++) { + String a = equations.get(i).get(0); + String b = equations.get(i).get(1); + adj.putIfAbsent(a, new ArrayList<>()); + adj.putIfAbsent(b, new ArrayList<>()); + adj.get(a).add(new Pair(b, values[i])); + adj.get(b).add(new Pair(a, 1 / values[i])); + } + + double[] res = new double[queries.size()]; + for (int i = 0; i < queries.size(); i++) { + String src = queries.get(i).get(0); + String target = queries.get(i).get(1); + res[i] = dfs(src, target, adj, new HashSet<>()); + } + + return res; + } + + private double dfs(String src, String target, Map> adj, Set visited) { + if (!adj.containsKey(src) || !adj.containsKey(target)) { + return -1.0; + } + if (src.equals(target)) { + return 1.0; + } + + visited.add(src); + + for (Pair neighbor : adj.get(src)) { + if (!visited.contains(neighbor.node)) { + double result = dfs(neighbor.node, target, adj, visited); + if (result != -1.0) { + return neighbor.weight * result; + } + } + } + + return -1.0; + } + + class Pair { + String node; + double weight; + + Pair(String node, double weight) { + this.node = node; + this.weight = weight; + } + } +} +``` + +```cpp +class Solution { +public: + vector calcEquation(vector>& equations, vector& values, vector>& queries) { + unordered_map>> adj; // Map a -> list of [b, a/b] + + for (int i = 0; i < equations.size(); i++) { + string a = equations[i][0]; + string b = equations[i][1]; + adj[a].emplace_back(b, values[i]); + adj[b].emplace_back(a, 1.0 / values[i]); + } + + vector res; + for (const auto& query : queries) { + string src = query[0]; + string target = query[1]; + res.push_back(dfs(src, target, adj, unordered_set())); + } + + return res; + } + +private: + double dfs(const string& src, const string& target, unordered_map>>& adj, unordered_set visited) { + if (!adj.count(src) || !adj.count(target)) { + return -1.0; + } + if (src == target) { + return 1.0; + } + + visited.insert(src); + + for (const auto& [nei, weight] : adj[src]) { + if (!visited.count(nei)) { + double result = dfs(nei, target, adj, visited); + if (result != -1.0) { + return weight * result; + } + } + } + + return -1.0; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[][]} equations + * @param {number[]} values + * @param {string[][]} queries + * @return {number[]} + */ + calcEquation(equations, values, queries) { + const adj = new Map(); // Map a -> list of [b, a/b] + + for (let i = 0; i < equations.length; i++) { + const [a, b] = equations[i]; + if (!adj.has(a)) adj.set(a, []); + if (!adj.has(b)) adj.set(b, []); + adj.get(a).push([b, values[i]]); + adj.get(b).push([a, 1 / values[i]]); + } + + const dfs = (src, target, visited) => { + if (!adj.has(src) || !adj.has(target)) return -1; + if (src === target) return 1; + + visited.add(src); + + for (const [nei, weight] of adj.get(src)) { + if (!visited.has(nei)) { + const result = dfs(nei, target, visited); + if (result !== -1) { + return weight * result; + } + } + } + + return -1; + }; + + return queries.map(([src, target]) => dfs(src, target, new Set())); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n)$ +* Space complexity: $O(n + m)$ + +> Where $n$ is the number of unique strings and $m$ is the number of queries. + +--- + +## 3. Disjoint Set Union + +::tabs-start + +```python +class UnionFind: + def __init__(self): + self.parent = {} + self.weight = {} + + def add(self, x): + if x not in self.parent: + self.parent[x] = x + self.weight[x] = 1.0 + + def find(self, x): + if x != self.parent[x]: + orig_parent = self.parent[x] + self.parent[x] = self.find(self.parent[x]) + self.weight[x] *= self.weight[orig_parent] + return self.parent[x] + + def union(self, x, y, value): + self.add(x) + self.add(y) + root_x = self.find(x) + root_y = self.find(y) + + if root_x != root_y: + self.parent[root_x] = root_y + self.weight[root_x] = value * self.weight[y] / self.weight[x] + + def get_ratio(self, x, y): + if x not in self.parent or y not in self.parent or self.find(x) != self.find(y): + return -1.0 + return self.weight[x] / self.weight[y] + +class Solution: + def calcEquation(self, equations: List[List[str]], values: List[float], queries: List[List[str]]) -> List[float]: + uf = UnionFind() + + for (a, b), value in zip(equations, values): + uf.union(a, b, value) + + return [uf.get_ratio(a, b) for a, b in queries] +``` + +```java +class UnionFind { + private final Map parent; + private final Map weight; + + public UnionFind() { + parent = new HashMap<>(); + weight = new HashMap<>(); + } + + public void add(String x) { + if (!parent.containsKey(x)) { + parent.put(x, x); + weight.put(x, 1.0); + } + } + + public String find(String x) { + if (!x.equals(parent.get(x))) { + String origParent = parent.get(x); + parent.put(x, find(origParent)); + weight.put(x, weight.get(x) * weight.get(origParent)); + } + return parent.get(x); + } + + public void union(String x, String y, double value) { + add(x); + add(y); + String rootX = find(x); + String rootY = find(y); + + if (!rootX.equals(rootY)) { + parent.put(rootX, rootY); + weight.put(rootX, value * weight.get(y) / weight.get(x)); + } + } + + public double getRatio(String x, String y) { + if (!parent.containsKey(x) || !parent.containsKey(y) || !find(x).equals(find(y))) { + return -1.0; + } + return weight.get(x) / weight.get(y); + } +} + +public class Solution { + public double[] calcEquation(List> equations, double[] values, List> queries) { + UnionFind uf = new UnionFind(); + + for (int i = 0; i < equations.size(); i++) { + List equation = equations.get(i); + String a = equation.get(0); + String b = equation.get(1); + uf.union(a, b, values[i]); + } + + double[] result = new double[queries.size()]; + for (int i = 0; i < queries.size(); i++) { + List query = queries.get(i); + String a = query.get(0); + String b = query.get(1); + result[i] = uf.getRatio(a, b); + } + + return result; + } +} +``` + +```cpp +class UnionFind { + unordered_map parent; + unordered_map weight; + +public: + void add(const string& x) { + if (parent.find(x) == parent.end()) { + parent[x] = x; + weight[x] = 1.0; + } + } + + string find(const string& x) { + if (x != parent[x]) { + string origParent = parent[x]; + parent[x] = find(parent[x]); + weight[x] *= weight[origParent]; + } + return parent[x]; + } + + void unionSets(const string& x, const string& y, double value) { + add(x); + add(y); + string rootX = find(x); + string rootY = find(y); + + if (rootX != rootY) { + parent[rootX] = rootY; + weight[rootX] = value * weight[y] / weight[x]; + } + } + + double getRatio(const string& x, const string& y) { + if (parent.find(x) == parent.end() || parent.find(y) == parent.end() || find(x) != find(y)) { + return -1.0; + } + return weight[x] / weight[y]; + } +}; + +class Solution { +public: + vector calcEquation(vector>& equations, vector& values, vector>& queries) { + UnionFind uf; + + for (int i = 0; i < equations.size(); i++) { + string a = equations[i][0]; + string b = equations[i][1]; + uf.unionSets(a, b, values[i]); + } + + vector result; + for (const auto& query : queries) { + string a = query[0]; + string b = query[1]; + result.push_back(uf.getRatio(a, b)); + } + + return result; + } +}; +``` + +```javascript +class UnionFind { + constructor() { + this.parent = new Map(); + this.weight = new Map(); + } + + /** + * @param {string} x + * @return {void} + */ + add(x) { + if (!this.parent.has(x)) { + this.parent.set(x, x); + this.weight.set(x, 1.0); + } + } + + /** + * @param {string} x + * @return {string} + */ + find(x) { + if (x !== this.parent.get(x)) { + const origParent = this.parent.get(x); + this.parent.set(x, this.find(origParent)); + this.weight.set(x, this.weight.get(x) * this.weight.get(origParent)); + } + return this.parent.get(x); + } + + /** + * @param {string} x + * @param {string} y + * @param {number} value + * @return {number} + */ + union(x, y, value) { + this.add(x); + this.add(y); + const rootX = this.find(x); + const rootY = this.find(y); + + if (rootX !== rootY) { + this.parent.set(rootX, rootY); + this.weight.set(rootX, (value * this.weight.get(y)) / this.weight.get(x)); + } + } + + /** + * @param {string} x + * @param {string} y + * @return {number} + */ + getRatio(x, y) { + if (!this.parent.has(x) || !this.parent.has(y) || this.find(x) !== this.find(y)) { + return -1.0; + } + return this.weight.get(x) / this.weight.get(y); + } +} + +class Solution { + /** + * @param {string[][]} equations + * @param {number[]} values + * @param {string[][]} queries + * @return {number[]} + */ + calcEquation(equations, values, queries) { + const uf = new UnionFind(); + + for (let i = 0; i < equations.length; i++) { + const [a, b] = equations[i]; + uf.union(a, b, values[i]); + } + + return queries.map(([a, b]) => uf.getRatio(a, b)); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O((m + n)\log n)$ +* Space complexity: $O(n + m)$ + +> Where $n$ is the number of unique strings and $m$ is the number of queries. + +--- + +## 4. Floyd Warshall + +::tabs-start + +```python +class Solution: + def calcEquation(self, equations, values, queries): + graph = collections.defaultdict(dict) + + for (a, b), value in zip(equations, values): + graph[a][b] = value + graph[b][a] = 1.0 / value + + for k in graph: + for i in graph[k]: + for j in graph[k]: + if j not in graph[i]: + graph[i][j] = graph[i][k] * graph[k][j] + + result = [] + for a, b in queries: + if a in graph and b in graph[a]: + result.append(graph[a][b]) + else: + result.append(-1.0) + return result +``` + +```java +public class Solution { + public double[] calcEquation(List> equations, double[] values, List> queries) { + Map> graph = new HashMap<>(); + + for (int i = 0; i < equations.size(); i++) { + String a = equations.get(i).get(0); + String b = equations.get(i).get(1); + double value = values[i]; + graph.computeIfAbsent(a, k -> new HashMap<>()).put(b, value); + graph.computeIfAbsent(b, k -> new HashMap<>()).put(a, 1.0 / value); + } + + for (String k : graph.keySet()) { + for (String i : graph.get(k).keySet()) { + for (String j : graph.get(k).keySet()) { + graph.computeIfAbsent(i, x -> new HashMap<>()).putIfAbsent(j, graph.get(i).get(k) * graph.get(k).get(j)); + } + } + } + + double[] result = new double[queries.size()]; + for (int i = 0; i < queries.size(); i++) { + String a = queries.get(i).get(0); + String b = queries.get(i).get(1); + if (graph.containsKey(a) && graph.get(a).containsKey(b)) { + result[i] = graph.get(a).get(b); + } else { + result[i] = -1.0; + } + } + + return result; + } +} +``` + +```cpp +class Solution { +public: + vector calcEquation(vector>& equations, vector& values, vector>& queries) { + unordered_map> graph; + + for (int i = 0; i < equations.size(); i++) { + string a = equations[i][0]; + string b = equations[i][1]; + double value = values[i]; + graph[a][b] = value; + graph[b][a] = 1.0 / value; + } + + for (const auto& pair : graph) { + const string& k = pair.first; + for (const auto& pair1 : graph[k]) { + const string& i = pair1.first; + for (const auto& pair2 : graph[k]) { + const string& j = pair2.first; + if (!graph[i].count(j)) { + graph[i][j] = graph[i][k] * graph[k][j]; + } + } + } + } + + vector result; + for (const auto& query : queries) { + const string& a = query[0]; + const string& b = query[1]; + if (!graph.count(a) || !graph[a].count(b)) { + result.push_back(-1.0); + } else { + result.push_back(graph[a][b]); + } + } + + return result; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[][]} equations + * @param {number[]} values + * @param {string[][]} queries + * @return {number[]} + */ + calcEquation(equations, values, queries) { + const graph = new Map(); + + for (let i = 0; i < equations.length; i++) { + const [a, b] = equations[i]; + const value = values[i]; + if (!graph.has(a)) graph.set(a, new Map()); + if (!graph.has(b)) graph.set(b, new Map()); + graph.get(a).set(b, value); + graph.get(b).set(a, 1 / value); + } + + for (const k of graph.keys()) { + for (const i of graph.get(k).keys()) { + for (const j of graph.get(k).keys()) { + if (!graph.get(i).has(j)) { + graph.get(i).set(j, graph.get(i).get(k) * graph.get(k).get(j)); + } + } + } + } + + return queries.map(([a, b]) => { + if (graph.has(a) && graph.get(a).has(b)) { + return graph.get(a).get(b); + } else { + return -1.0; + } + }); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m + n ^ 3)$ +* Space complexity: $O(n ^ 2 + m)$ + +> Where $n$ is the number of unique strings and $m$ is the number of queries. \ No newline at end of file diff --git a/articles/excel-sheet-column-title.md b/articles/excel-sheet-column-title.md new file mode 100644 index 000000000..98ae4f249 --- /dev/null +++ b/articles/excel-sheet-column-title.md @@ -0,0 +1,142 @@ +## 1. Recursion + +::tabs-start + +```python +class Solution: + def convertToTitle(self, columnNumber: int) -> str: + if columnNumber == 0: + return "" + + n = columnNumber - 1 + return self.convertToTitle(n // 26) + chr(n % 26 + ord('A')) +``` + +```java +public class Solution { + public String convertToTitle(int columnNumber) { + if (columnNumber == 0) { + return ""; + } + int n = columnNumber - 1; + return convertToTitle(n / 26) + (char) ('A' + n % 26); + } +} +``` + +```cpp +class Solution { +public: + string convertToTitle(int columnNumber) { + if (columnNumber == 0) { + return ""; + } + int n = columnNumber - 1; + return convertToTitle(n / 26) + char('A' + n % 26); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} columnNumber + * @return {string} + */ + convertToTitle(columnNumber) { + if (columnNumber === 0) { + return ""; + } + const n = columnNumber - 1; + return this.convertToTitle(Math.floor(n / 26)) + String.fromCharCode('A'.charCodeAt(0) + n % 26); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(\log n)$ +* Space complexity: $O(\log n)$ for recursion stack. + +> Where $n$ is the given column number. + +--- + +## 2. Iteration + +::tabs-start + +```python +class Solution: + def convertToTitle(self, columnNumber: int) -> str: + res = [] + while columnNumber > 0: + columnNumber -= 1 + offset = columnNumber % 26 + res += chr(ord('A') + offset) + columnNumber //= 26 + + return ''.join(reversed(res)) +``` + +```java +public class Solution { + public String convertToTitle(int columnNumber) { + StringBuilder res = new StringBuilder(); + while (columnNumber > 0) { + columnNumber--; + int offset = columnNumber % 26; + res.append((char) ('A' + offset)); + columnNumber /= 26; + } + return res.reverse().toString(); + } +} +``` + +```cpp +class Solution { +public: + string convertToTitle(int columnNumber) { + string res; + while (columnNumber > 0) { + columnNumber--; + int offset = columnNumber % 26; + res += ('A' + offset); + columnNumber /= 26; + } + reverse(res.begin(), res.end()); + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} columnNumber + * @return {string} + */ + convertToTitle(columnNumber) { + let res = []; + while (columnNumber > 0) { + columnNumber--; + const offset = columnNumber % 26; + res.push(String.fromCharCode('A'.charCodeAt(0) + offset)); + columnNumber = Math.floor(columnNumber / 26); + } + return res.reverse().join(''); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(\log n)$ +* Space complexity: $O(1)$ extra space. + +> Where $n$ is the given column number. \ No newline at end of file diff --git a/articles/find-critical-and-pseudo-critical-edges-in-minimum-spanning-tree.md b/articles/find-critical-and-pseudo-critical-edges-in-minimum-spanning-tree.md new file mode 100644 index 000000000..dd184b948 --- /dev/null +++ b/articles/find-critical-and-pseudo-critical-edges-in-minimum-spanning-tree.md @@ -0,0 +1,1281 @@ +## 1. Kruskal's Algorithm - I + +::tabs-start + +```python +class UnionFind: + def __init__(self, n): + self.par = [i for i in range(n)] + self.rank = [1] * n + + def find(self, v1): + while v1 != self.par[v1]: + self.par[v1] = self.par[self.par[v1]] + v1 = self.par[v1] + return v1 + + def union(self, v1, v2): + p1, p2 = self.find(v1), self.find(v2) + if p1 == p2: + return False + if self.rank[p1] > self.rank[p2]: + self.par[p2] = p1 + self.rank[p1] += self.rank[p2] + else: + self.par[p1] = p2 + self.rank[p2] += self.rank[p1] + return True + + +class Solution: + def findCriticalAndPseudoCriticalEdges(self, n: int, edges: List[List[int]]) -> List[List[int]]: + for i, e in enumerate(edges): + e.append(i) # [v1, v2, weight, original_index] + + edges.sort(key=lambda e: e[2]) + + mst_weight = 0 + uf = UnionFind(n) + for v1, v2, w, i in edges: + if uf.union(v1, v2): + mst_weight += w + + critical, pseudo = [], [] + for n1, n2, e_weight, i in edges: + # Try without curr edge + weight = 0 + uf = UnionFind(n) + for v1, v2, w, j in edges: + if i != j and uf.union(v1, v2): + weight += w + if max(uf.rank) != n or weight > mst_weight: + critical.append(i) + continue + + # Try with curr edge + uf = UnionFind(n) + uf.union(n1, n2) + weight = e_weight + for v1, v2, w, j in edges: + if uf.union(v1, v2): + weight += w + if weight == mst_weight: + pseudo.append(i) + return [critical, pseudo] +``` + +```java +class UnionFind { + int[] par, rank; + + public UnionFind(int n) { + par = new int[n]; + rank = new int[n]; + for (int i = 0; i < n; i++) { + par[i] = i; + rank[i] = 1; + } + } + + public int find(int v) { + if (par[v] != v) { + par[v] = find(par[v]); + } + return par[v]; + } + + public boolean union(int v1, int v2) { + int p1 = find(v1), p2 = find(v2); + if (p1 == p2) return false; + if (rank[p1] > rank[p2]) { + par[p2] = p1; + rank[p1] += rank[p2]; + } else { + par[p1] = p2; + rank[p2] += rank[p1]; + } + return true; + } +} + +public class Solution { + public List> findCriticalAndPseudoCriticalEdges(int n, int[][] edges) { + List edgeList = new ArrayList<>(); + for (int i = 0; i < edges.length; i++) { + edgeList.add(new int[] { edges[i][0], edges[i][1], edges[i][2], i }); + } + + edgeList.sort(Comparator.comparingInt(a -> a[2])); + + int mstWeight = 0; + UnionFind uf = new UnionFind(n); + for (int[] edge : edgeList) { + if (uf.union(edge[0], edge[1])) { + mstWeight += edge[2]; + } + } + + List critical = new ArrayList<>(); + List pseudo = new ArrayList<>(); + + for (int[] edge : edgeList) { + // Try without current edge + UnionFind ufWithout = new UnionFind(n); + int weight = 0; + for (int[] other : edgeList) { + if (other[3] != edge[3] && ufWithout.union(other[0], other[1])) { + weight += other[2]; + } + } + if (Arrays.stream(ufWithout.rank).max().getAsInt() != n || weight > mstWeight) { + critical.add(edge[3]); + continue; + } + + // Try with current edge + UnionFind ufWith = new UnionFind(n); + ufWith.union(edge[0], edge[1]); + weight = edge[2]; + for (int[] other : edgeList) { + if (ufWith.union(other[0], other[1])) { + weight += other[2]; + } + } + if (weight == mstWeight) { + pseudo.add(edge[3]); + } + } + + return Arrays.asList(critical, pseudo); + } +} +``` + +```cpp +class UnionFind { +public: + vector par, rank; + + UnionFind(int n) : par(n), rank(n, 1) { + iota(par.begin(), par.end(), 0); + } + + int find(int v) { + if (v != par[v]) { + par[v] = find(par[v]); + } + return par[v]; + } + + bool unionSets(int v1, int v2) { + int p1 = find(v1), p2 = find(v2); + if (p1 == p2) return false; + if (rank[p1] > rank[p2]) { + par[p2] = p1; + rank[p1] += rank[p2]; + } else { + par[p1] = p2; + rank[p2] += rank[p1]; + } + return true; + } +}; + +class Solution { +public: + vector> findCriticalAndPseudoCriticalEdges(int n, vector>& edges) { + vector> edgeList; + for (int i = 0; i < edges.size(); ++i) { + edgeList.push_back({ edges[i][0], edges[i][1], edges[i][2], i }); + } + + sort(edgeList.begin(), edgeList.end(), [](auto& a, auto& b) { + return a[2] < b[2]; + }); + + int mstWeight = 0; + UnionFind uf(n); + for (auto& edge : edgeList) { + if (uf.unionSets(edge[0], edge[1])) { + mstWeight += edge[2]; + } + } + + vector critical, pseudo; + for (auto& edge : edgeList) { + // Try without current edge + UnionFind ufWithout(n); + int weight = 0; + for (auto& other : edgeList) { + if (other[3] != edge[3] && ufWithout.unionSets(other[0], other[1])) { + weight += other[2]; + } + } + if (*max_element(ufWithout.rank.begin(), ufWithout.rank.end()) != n || weight > mstWeight) { + critical.push_back(edge[3]); + continue; + } + + // Try with current edge + UnionFind ufWith(n); + ufWith.unionSets(edge[0], edge[1]); + weight = edge[2]; + for (auto& other : edgeList) { + if (ufWith.unionSets(other[0], other[1])) { + weight += other[2]; + } + } + if (weight == mstWeight) { + pseudo.push_back(edge[3]); + } + } + + return { critical, pseudo }; + } +}; +``` + +```javascript +class UnionFind { + /** + * @constructor + * @param {number} n + */ + constructor(n) { + this.par = Array.from({ length: n }, (_, i) => i); + this.rank = Array(n).fill(1); + } + + /** + * @param {number} v1 + * @return {number} + */ + find(v1) { + if (this.par[v1] !== v1) { + this.par[v1] = this.find(this.par[v1]); + } + return this.par[v1]; + } + + /** + * @param {number} v1 + * @param {number} v2 + * @return {boolean} + */ + union(v1, v2) { + const p1 = this.find(v1), p2 = this.find(v2); + if (p1 === p2) return false; + if (this.rank[p1] > this.rank[p2]) { + this.par[p2] = p1; + this.rank[p1] += this.rank[p2]; + } else { + this.par[p1] = p2; + this.rank[p2] += this.rank[p1]; + } + return true; + } +} + +class Solution { + /** + * @param {number} n + * @param {number[][]} edges + * @return {number[][]} + */ + findCriticalAndPseudoCriticalEdges(n, edges) { + edges = edges.map((edge, idx) => [...edge, idx]); + edges.sort((a, b) => a[2] - b[2]); + + const uf = new UnionFind(n); + let mstWeight = 0; + for (const [v1, v2, w] of edges) { + if (uf.union(v1, v2)) { + mstWeight += w; + } + } + + const critical = []; + const pseudo = []; + + for (const [n1, n2, weight, i] of edges) { + // Try without current edge + const ufWithout = new UnionFind(n); + let weightWithout = 0; + for (const [v1, v2, w, j] of edges) { + if (j !== i && ufWithout.union(v1, v2)) { + weightWithout += w; + } + } + if (Math.max(...ufWithout.rank) !== n || weightWithout > mstWeight) { + critical.push(i); + continue; + } + + // Try with current edge + const ufWith = new UnionFind(n); + ufWith.union(n1, n2); + let weightWith = weight; + for (const [v1, v2, w, j] of edges) { + if (ufWith.union(v1, v2)) { + weightWith += w; + } + } + if (weightWith === mstWeight) { + pseudo.push(i); + } + } + + return [critical, pseudo]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(E ^ 2)$ +* Space complexity: $O(V + E)$ + +> Where $V$ is the number of vertices and $E$ is the number of edges. + +--- + +## 2. Kruskal's Algorithm - II + +::tabs-start + +```python +class UnionFind: + def __init__(self, n): + self.n = n + self.Parent = list(range(n + 1)) + self.Size = [1] * (n + 1) + + def find(self, node): + if self.Parent[node] != node: + self.Parent[node] = self.find(self.Parent[node]) + return self.Parent[node] + + def union(self, u, v): + pu = self.find(u) + pv = self.find(v) + if pu == pv: + return False + self.n -= 1 + if self.Size[pu] < self.Size[pv]: + pu, pv = pv, pu + self.Size[pu] += self.Size[pv] + self.Parent[pv] = pu + return True + + def isConnected(self): + return self.n == 1 + +class Solution: + def findCriticalAndPseudoCriticalEdges(self, n: int, edges: List[List[int]]) -> List[List[int]]: + for i, e in enumerate(edges): + e.append(i) + edges.sort(key = lambda e : e[2]) + + def findMST(index, include): + uf = UnionFind(n) + wgt = 0 + if include: + wgt += edges[index][2] + uf.union(edges[index][0], edges[index][1]) + + for i, e in enumerate(edges): + if i == index: + continue + if uf.union(e[0], e[1]): + wgt += e[2] + return wgt if uf.isConnected() else float("inf") + + mst_wgt = findMST(-1, False) + critical, pseudo = [], [] + for i, e in enumerate(edges): + if mst_wgt < findMST(i, False): + critical.append(e[3]) + elif mst_wgt == findMST(i, True): + pseudo.append(e[3]) + + return [critical, pseudo] +``` + +```java +class UnionFind { + private int n; + private int[] Parent, Size; + + public UnionFind(int n) { + this.n = n; + Parent = new int[n + 1]; + Size = new int[n + 1]; + for (int i = 0; i <= n; i++) { + Parent[i] = i; + Size[i] = 1; + } + } + + public int find(int node) { + if (Parent[node] != node) { + Parent[node] = find(Parent[node]); + } + return Parent[node]; + } + + public boolean union(int u, int v) { + int pu = find(u), pv = find(v); + if (pu == pv) { + return false; + } + n--; + if (Size[pu] < Size[pv]) { + int temp = pu; + pu = pv; + pv = temp; + } + Size[pu] += Size[pv]; + Parent[pv] = pu; + return true; + } + + public boolean isConnected() { + return n == 1; + } +} + +public class Solution { + public List> findCriticalAndPseudoCriticalEdges(int n, int[][] edges) { + for (int i = 0; i < edges.length; i++) { + edges[i] = Arrays.copyOf(edges[i], edges[i].length + 1); + edges[i][3] = i; + } + + Arrays.sort(edges, Comparator.comparingInt(a -> a[2])); + + + int mst_wgt = findMST(n, edges, -1, false); + List critical = new ArrayList<>(); + List pseudo = new ArrayList<>(); + + for (int i = 0; i < edges.length; i++) { + if (mst_wgt < findMST(n, edges, i, false)) { + critical.add(edges[i][3]); + } else if (mst_wgt == findMST(n, edges, i, true)) { + pseudo.add(edges[i][3]); + } + } + + return Arrays.asList(critical, pseudo); + } + + public int findMST(int n, int[][] edges, int index, boolean include) { + UnionFind uf = new UnionFind(n); + int wgt = 0; + if (include) { + wgt += edges[index][2]; + uf.union(edges[index][0], edges[index][1]); + } + for (int i = 0; i < edges.length; i++) { + if (i == index) { + continue; + } + if (uf.union(edges[i][0], edges[i][1])) { + wgt += edges[i][2]; + } + } + return uf.isConnected() ? wgt : Integer.MAX_VALUE; + } +} +``` + +```cpp +class UnionFind { +private: + int n; + vector Parent, Size; + +public: + UnionFind(int n) : n(n), Parent(n + 1), Size(n + 1, 1) { + for (int i = 0; i <= n; ++i) { + Parent[i] = i; + } + } + + int find(int node) { + if (Parent[node] != node) { + Parent[node] = find(Parent[node]); + } + return Parent[node]; + } + + bool unionSets(int u, int v) { + int pu = find(u), pv = find(v); + if (pu == pv) return false; + n--; + if (Size[pu] < Size[pv]) { + swap(pu, pv); + } + Size[pu] += Size[pv]; + Parent[pv] = pu; + return true; + } + + bool isConnected() { + return n == 1; + } +}; + +class Solution { +public: + vector> findCriticalAndPseudoCriticalEdges(int n, vector>& edges) { + for (int i = 0; i < edges.size(); ++i) { + edges[i].push_back(i); + } + + sort(edges.begin(), edges.end(), [](const vector& a, const vector& b) { + return a[2] < b[2]; + }); + + auto findMST = [&](int index, bool include) -> int { + UnionFind uf(n); + int wgt = 0; + if (include) { + wgt += edges[index][2]; + uf.unionSets(edges[index][0], edges[index][1]); + } + for (int i = 0; i < edges.size(); ++i) { + if (i == index) continue; + if (uf.unionSets(edges[i][0], edges[i][1])) { + wgt += edges[i][2]; + } + } + return uf.isConnected() ? wgt : INT_MAX; + }; + + int mst_wgt = findMST(-1, false); + vector critical, pseudo; + + for (int i = 0; i < edges.size(); ++i) { + if (mst_wgt < findMST(i, false)) { + critical.push_back(edges[i][3]); + } else if (mst_wgt == findMST(i, true)) { + pseudo.push_back(edges[i][3]); + } + } + + return { critical, pseudo }; + } +}; +``` + +```javascript +class UnionFind { + /** + * @constructor + * @param {number} n + */ + constructor(n) { + this.n = n; + this.Parent = Array.from({ length: n + 1 }, (_, i) => i); + this.Size = Array(n + 1).fill(1); + } + + /** + * @param {number} v1 + * @return {number} + */ + find(v1) { + if (this.Parent[node] !== node) { + this.Parent[node] = this.find(this.Parent[node]); + } + return this.Parent[node]; + } + + /** + * @param {number} v1 + * @param {number} v2 + * @return {boolean} + */ + union(v1, v2) { + let pu = this.find(u); + let pv = this.find(v); + if (pu === pv) return false; + this.n--; + if (this.Size[pu] < this.Size[pv]) { + [pu, pv] = [pv, pu]; + } + this.Size[pu] += this.Size[pv]; + this.Parent[pv] = pu; + return true; + } + + isConnected() { + return this.n === 1; + } +} + +class Solution { + /** + * @param {number} n + * @param {number[][]} edges + * @return {number[][]} + */ + findCriticalAndPseudoCriticalEdges(n, edges) { + edges.forEach((edge, i) => edge.push(i)); + edges.sort((a, b) => a[2] - b[2]); + + const findMST = (index, include) => { + const uf = new UnionFind(n); + let wgt = 0; + if (include) { + wgt += edges[index][2]; + uf.union(edges[index][0], edges[index][1]); + } + for (let i = 0; i < edges.length; i++) { + if (i === index) continue; + if (uf.union(edges[i][0], edges[i][1])) { + wgt += edges[i][2]; + } + } + return uf.isConnected() ? wgt : Infinity; + }; + + const mst_wgt = findMST(-1, false); + const critical = []; + const pseudo = []; + + edges.forEach((edge, i) => { + if (mst_wgt < findMST(i, false)) { + critical.push(edge[3]); + } else if (mst_wgt === findMST(i, true)) { + pseudo.push(edge[3]); + } + }); + + return [critical, pseudo]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(E ^ 2)$ +* Space complexity: $O(V + E)$ + +> Where $V$ is the number of vertices and $E$ is the number of edges. + +--- + +## 3. Dijkstra's Algorithm + +::tabs-start + +```python +class Solution: + def findCriticalAndPseudoCriticalEdges(self, n: int, edges: List[List[int]]) -> List[List[int]]: + for i, edge in enumerate(edges): + edge.append(i) + + adj = defaultdict(list) + for u, v, w, idx in edges: + adj[u].append((v, w, idx)) + adj[v].append((u, w, idx)) + + def minimax(src, dst, exclude_idx): + dist = [float('inf')] * n + dist[src] = 0 + pq = [(0, src)] + + while pq: + max_w, u = heappop(pq) + if u == dst: + return max_w + + for v, weight, edge_idx in adj[u]: + if edge_idx == exclude_idx: + continue + new_w = max(max_w, weight) + if new_w < dist[v]: + dist[v] = new_w + heappush(pq, (new_w, v)) + + return float('inf') + + critical, pseudo = [], [] + for i, (u, v, w, idx) in enumerate(edges): + if w < minimax(u, v, idx): + critical.append(idx) + elif w == minimax(u, v, -1): + pseudo.append(idx) + + return [critical, pseudo] +``` + +```java +public class Solution { + private List[] adj; + + public List> findCriticalAndPseudoCriticalEdges(int n, int[][] edges) { + for (int i = 0; i < edges.length; i++) { + edges[i] = Arrays.copyOf(edges[i], edges[i].length + 1); + edges[i][3] = i; + } + + adj = new ArrayList[n]; + for (int i = 0; i < n; i++) { + adj[i] = new ArrayList<>(); + } + for (int[] edge : edges) { + adj[edge[0]].add(new int[]{edge[1], edge[2], edge[3]}); + adj[edge[1]].add(new int[]{edge[0], edge[2], edge[3]}); + } + + + List critical = new ArrayList<>(); + List pseudo = new ArrayList<>(); + + for (int[] edge : edges) { + int u = edge[0], v = edge[1], w = edge[2], idx = edge[3]; + if (w < minimax(n, u, v, idx)) { + critical.add(idx); + } else if (w == minimax(n, u, v, -1)) { + pseudo.add(idx); + } + } + + return Arrays.asList(critical, pseudo); + } + + public int minimax(int n, int src, int dst, int excludeIdx) { + int[] dist = new int[n]; + Arrays.fill(dist, Integer.MAX_VALUE); + dist[src] = 0; + + PriorityQueue pq = new PriorityQueue<>(Comparator.comparingInt(a -> a[0])); + pq.offer(new int[]{0, src}); + + while (!pq.isEmpty()) { + int[] curr = pq.poll(); + int maxW = curr[0], u = curr[1]; + if (u == dst) return maxW; + + for (int[] neighbor : adj[u]) { + int v = neighbor[0], weight = neighbor[1], edgeIdx = neighbor[2]; + if (edgeIdx == excludeIdx) continue; + int newW = Math.max(maxW, weight); + if (newW < dist[v]) { + dist[v] = newW; + pq.offer(new int[]{newW, v}); + } + } + } + return Integer.MAX_VALUE; + } +} +``` + +```cpp +class Solution { +public: + vector> findCriticalAndPseudoCriticalEdges(int n, vector>& edges) { + for (int i = 0; i < edges.size(); ++i) { + edges[i].push_back(i); + } + + vector>> adj(n); + for (const auto& edge : edges) { + adj[edge[0]].push_back({edge[1], edge[2], edge[3]}); + adj[edge[1]].push_back({edge[0], edge[2], edge[3]}); + } + + auto minimax = [&](int src, int dst, int excludeIdx) -> int { + vector dist(n, INT_MAX); + dist[src] = 0; + + priority_queue, vector>, greater<>> pq; + pq.push({0, src}); + + while (!pq.empty()) { + auto [maxW, u] = pq.top(); + pq.pop(); + if (u == dst) return maxW; + + for (const auto& neighbor : adj[u]) { + int v = neighbor[0], weight = neighbor[1], edgeIdx = neighbor[2]; + if (edgeIdx == excludeIdx) continue; + int newW = max(maxW, weight); + if (newW < dist[v]) { + dist[v] = newW; + pq.push({newW, v}); + } + } + } + return INT_MAX; + }; + + vector critical, pseudo; + for (const auto& edge : edges) { + int u = edge[0], v = edge[1], w = edge[2], idx = edge[3]; + if (w < minimax(u, v, idx)) { + critical.push_back(idx); + } else if (w == minimax(u, v, -1)) { + pseudo.push_back(idx); + } + } + + return {critical, pseudo}; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number[][]} edges + * @return {number[][]} + */ + findCriticalAndPseudoCriticalEdges(n, edges) { + edges.forEach((edge, i) => edge.push(i)); + + const adj = Array.from({ length: n }, () => []); + for (const [u, v, w, idx] of edges) { + adj[u].push([v, w, idx]); + adj[v].push([u, w, idx]); + } + + const minimax = (src, dst, excludeIdx) => { + const dist = Array(n).fill(Infinity); + dist[src] = 0; + + const pq = new MinPriorityQueue({ compare: (a, b) => a[0] - b[0] }); + pq.enqueue([0, src]); + + while (!pq.isEmpty()) { + const [maxW, u] = pq.dequeue(); + if (u === dst) return maxW; + + for (const [v, weight, edgeIdx] of adj[u]) { + if (edgeIdx === excludeIdx) continue; + const newW = Math.max(maxW, weight); + if (newW < dist[v]) { + dist[v] = newW; + pq.enqueue([newW, v]); + } + } + } + return Infinity; + }; + + const critical = []; + const pseudo = []; + + for (const [u, v, w, idx] of edges) { + if (w < minimax(u, v, idx)) { + critical.push(idx); + } else if (w === minimax(u, v, -1)) { + pseudo.push(idx); + } + } + + return [critical, pseudo]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(E ^ 2 \log V)$ +* Space complexity: $O(V + E)$ + +> Where $V$ is the number of vertices and $E$ is the number of edges. + +--- + +## 4. Kruskal's Algorithm + DFS + +::tabs-start + +```python +class UnionFind: + def __init__(self, n): + self.Parent = list(range(n + 1)) + self.Size = [1] * (n + 1) + + def find(self, node): + if self.Parent[node] != node: + self.Parent[node] = self.find(self.Parent[node]) + return self.Parent[node] + + def union(self, u, v): + pu = self.find(u) + pv = self.find(v) + if pu == pv: + return False + if self.Size[pu] < self.Size[pv]: + pu, pv = pv, pu + self.Size[pu] += self.Size[pv] + self.Parent[pv] = pu + return True + +class Solution: + def findCriticalAndPseudoCriticalEdges(self, n: int, edges: List[List[int]]) -> List[List[int]]: + mst = [[] for _ in range(n)] + mstEdge = [] + + edge_list = [(w, u, v, i) for i, (u, v, w) in enumerate(edges)] + edge_list.sort() + + uf = UnionFind(n) + for w, u, v, i in edge_list: + if uf.union(u, v): + mst[u].append((v, i)) + mst[v].append((u, i)) + mstEdge.append(i) + + def dfs(node): + for next, ind in mst[node]: + if path and ind == path[-1]: + continue + path.append(ind) + if next == dst or dfs(next): + return True + path.pop() + return False + + pseudo, mstEdge = set(), set(mstEdge) + for ind in range(len(edges)): + if ind in mstEdge: + continue + path, dst = [], edges[ind][1] + dfs(edges[ind][0]) + for i in path: + if edges[i][2] == edges[ind][2]: + pseudo.add(i) + pseudo.add(ind) + mstEdge.add(i) + mstEdge.add(ind) + + return [list(mstEdge - pseudo), list(pseudo)] +``` + +```java +class UnionFind { + private int[] parent; + private int[] size; + + public UnionFind(int n) { + parent = new int[n]; + size = new int[n]; + for (int i = 0; i < n; i++) { + parent[i] = i; + size[i] = 1; + } + } + + public int find(int node) { + if (parent[node] != node) { + parent[node] = find(parent[node]); + } + return parent[node]; + } + + public boolean union(int u, int v) { + int pu = find(u); + int pv = find(v); + if (pu == pv) { + return false; + } + if (size[pu] < size[pv]) { + int temp = pu; + pu = pv; + pv = temp; + } + size[pu] += size[pv]; + parent[pv] = pu; + return true; + } +} + +public class Solution { + private List> mst; + private Set mstEdges; + private Set pseudoCriticalEdges; + private int destination; + private List path; + + public List> findCriticalAndPseudoCriticalEdges(int n, int[][] edges) { + mst = new ArrayList<>(); + mstEdges = new HashSet<>(); + pseudoCriticalEdges = new HashSet<>(); + + for (int i = 0; i < n; i++) { + mst.add(new ArrayList<>()); + } + + List edgeList = new ArrayList<>(); + for (int i = 0; i < edges.length; i++) { + edgeList.add(new int[]{edges[i][2], edges[i][0], edges[i][1], i}); + } + edgeList.sort(Comparator.comparingInt(a -> a[0])); + + UnionFind uf = new UnionFind(n); + for (int[] edge : edgeList) { + int weight = edge[0], u = edge[1], v = edge[2], index = edge[3]; + if (uf.union(u, v)) { + mst.get(u).add(index); + mst.get(v).add(index); + mstEdges.add(index); + } + } + + for (int i = 0; i < edges.length; i++) { + if (mstEdges.contains(i)) { + continue; + } + path = new ArrayList<>(); + destination = edges[i][1]; + if (dfs(edges[i][0], -1, edges)) { + for (int p : path) { + if (edges[p][2] == edges[i][2]) { + pseudoCriticalEdges.add(i); + pseudoCriticalEdges.add(p); + } + } + } + } + + List critical = new ArrayList<>(); + for (int edge : mstEdges) { + if (!pseudoCriticalEdges.contains(edge)) { + critical.add(edge); + } + } + + return Arrays.asList(critical, new ArrayList<>(pseudoCriticalEdges)); + } + + private boolean dfs(int node, int parent, int[][] edges) { + if (node == destination) { + return true; + } + for (int edgeIndex : mst.get(node)) { + if (edgeIndex == parent) { + continue; + } + path.add(edgeIndex); + int nextNode = edges[edgeIndex][0] == node ? edges[edgeIndex][1] : edges[edgeIndex][0]; + if (dfs(nextNode, edgeIndex, edges)) { + return true; + } + path.remove(path.size() - 1); + } + return false; + } +} +``` + +```cpp +class UnionFind { + vector parent, size; + +public: + UnionFind(int n) { + parent.resize(n); + size.resize(n, 1); + for (int i = 0; i < n; ++i) { + parent[i] = i; + } + } + + int find(int node) { + if (parent[node] != node) { + parent[node] = find(parent[node]); + } + return parent[node]; + } + + bool unionSets(int u, int v) { + int pu = find(u), pv = find(v); + if (pu == pv) return false; + if (size[pu] < size[pv]) swap(pu, pv); + size[pu] += size[pv]; + parent[pv] = pu; + return true; + } +}; + +class Solution { + vector> mst; + set mstEdges, pseudoCriticalEdges; + vector path; + int destination; + +public: + vector> findCriticalAndPseudoCriticalEdges(int n, vector>& edges) { + mst.resize(n); + vector> edgeList; + for (int i = 0; i < edges.size(); ++i) { + edgeList.push_back({edges[i][2], edges[i][0], edges[i][1], i}); + } + sort(edgeList.begin(), edgeList.end()); + + UnionFind uf(n); + for (auto& [w, u, v, idx] : edgeList) { + if (uf.unionSets(u, v)) { + mst[u].push_back(idx); + mst[v].push_back(idx); + mstEdges.insert(idx); + } + } + + for (int i = 0; i < edges.size(); ++i) { + if (mstEdges.count(i)) continue; + path.clear(); + destination = edges[i][1]; + if (dfs(edges[i][0], -1, edges)) { + for (int p : path) { + if (edges[p][2] == edges[i][2]) { + pseudoCriticalEdges.insert(p); + pseudoCriticalEdges.insert(i); + } + } + } + } + + vector critical; + for (int e : mstEdges) { + if (!pseudoCriticalEdges.count(e)) critical.push_back(e); + } + + return {critical, vector(pseudoCriticalEdges.begin(), pseudoCriticalEdges.end())}; + } + + bool dfs(int node, int parent, vector>& edges) { + if (node == destination) return true; + for (int& edgeIdx : mst[node]) { + if (edgeIdx == parent) continue; + path.push_back(edgeIdx); + int next = edges[edgeIdx][0] == node ? edges[edgeIdx][1] : edges[edgeIdx][0]; + if (dfs(next, edgeIdx, edges)) return true; + path.pop_back(); + } + return false; + } +}; +``` + +```javascript +class UnionFind { + /** + * @constructor + * @param {number} n + */ + constructor(n) { + this.Parent = Array.from({ length: n + 1 }, (_, i) => i); + this.Size = Array(n + 1).fill(1); + } + + /** + * @param {number} node + * @return {number} + */ + find(node) { + if (this.Parent[node] !== node) { + this.Parent[node] = this.find(this.Parent[node]); + } + return this.Parent[node]; + } + + /** + * @param {number} u + * @param {number} v + * @return {boolean} + */ + union(u, v) { + let pu = this.find(u); + let pv = this.find(v); + if (pu === pv) return false; + if (this.Size[pu] < this.Size[pv]) { + [pu, pv] = [pv, pu]; + } + this.Size[pu] += this.Size[pv]; + this.Parent[pv] = pu; + return true; + } +} + +class Solution { + /** + * @param {number} n + * @param {number[][]} edges + * @return {number[][]} + */ + findCriticalAndPseudoCriticalEdges(n, edges) { + const mst = Array.from({ length: n }, () => []); + const mstEdges = new Set(); + const pseudoCriticalEdges = new Set(); + const edgeList = edges.map((e, i) => [...e, i]).sort((a, b) => a[2] - b[2]); + + const uf = new UnionFind(n); + + for (const [u, v, w, idx] of edgeList) { + if (uf.union(u, v)) { + mst[u].push([v, idx]); + mst[v].push([u, idx]); + mstEdges.add(idx); + } + } + + let path = []; + let destination = null; + + const dfs = (node, parent) => { + if (node === destination) return true; + for (const [next, edgeIdx] of mst[node]) { + if (edgeIdx === parent) continue; + path.push(edgeIdx); + if (dfs(next, edgeIdx)) return true; + path.pop(); + } + return false; + }; + + for (let i = 0; i < edges.length; i++) { + if (mstEdges.has(i)) continue; + + path = []; + destination = edges[i][1]; + if (dfs(edges[i][0], -1)) { + for (const edgeIdx of path) { + if (edges[edgeIdx][2] === edges[i][2]) { + pseudoCriticalEdges.add(i); + pseudoCriticalEdges.add(edgeIdx); + } + } + } + } + + const critical = [...mstEdges].filter(edgeIdx => !pseudoCriticalEdges.has(edgeIdx)); + const pseudo = [...pseudoCriticalEdges]; + + return [critical, pseudo]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(E ^ 2)$ +* Space complexity: $O(V + E)$ + +> Where $V$ is the number of vertices and $E$ is the number of edges. \ No newline at end of file diff --git a/articles/greatest-common-divisor-of-strings.md b/articles/greatest-common-divisor-of-strings.md new file mode 100644 index 000000000..ae138e7d6 --- /dev/null +++ b/articles/greatest-common-divisor-of-strings.md @@ -0,0 +1,459 @@ +## 1. Iteration + +::tabs-start + +```python +class Solution: + def gcdOfStrings(self, str1: str, str2: str) -> str: + len1, len2 = len(str1), len(str2) + + def isDivisor(l): + if len1 % l != 0 or len2 % l != 0: + return False + f1, f2 = len1 // l, len2 // l + return str1[:l] * f1 == str1 and str1[:l] * f2 == str2 + + for l in range(min(len1, len2), 0, -1): + if isDivisor(l): + return str1[:l] + + return "" +``` + +```java +public class Solution { + public String gcdOfStrings(String str1, String str2) { + int len1 = str1.length(), len2 = str2.length(); + + for (int l = Math.min(len1, len2); l > 0; l--) { + if (isDivisor(l, len1, len2, str1, str2)) { + return str1.substring(0, l); + } + } + + return ""; + } + + public boolean isDivisor(int l, int len1, int len2, String str1, String str2) { + if (len1 % l != 0 || len2 % l != 0) { + return false; + } + String sub = str1.substring(0, l); + int f1 = len1 / l, f2 = len2 / l; + return sub.repeat(f1).equals(str1) && sub.repeat(f2).equals(str2); + } +} +``` + +```cpp +class Solution { +public: + string gcdOfStrings(string str1, string str2) { + int len1 = str1.size(), len2 = str2.size(); + + auto isDivisor = [&](int l) { + if (len1 % l != 0 || len2 % l != 0) { + return false; + } + string sub = str1.substr(0, l); + int f1 = len1 / l, f2 = len2 / l; + string repeated1 = "", repeated2 = ""; + for (int i = 0; i < f1; ++i) repeated1 += sub; + for (int i = 0; i < f2; ++i) repeated2 += sub; + return repeated1 == str1 && repeated2 == str2; + }; + + for (int l = min(len1, len2); l > 0; l--) { + if (isDivisor(l)) { + return str1.substr(0, l); + } + } + + return ""; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} str1 + * @param {string} str2 + * @return {string} + */ + gcdOfStrings(str1, str2) { + const len1 = str1.length, len2 = str2.length; + + const isDivisor = (l) => { + if (len1 % l !== 0 || len2 % l !== 0) { + return false; + } + const sub = str1.slice(0, l); + const f1 = len1 / l, f2 = len2 / l; + return sub.repeat(f1) === str1 && sub.repeat(f2) === str2; + }; + + for (let l = Math.min(len1, len2); l > 0; l--) { + if (isDivisor(l)) { + return str1.slice(0, l); + } + } + + return ""; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(min(m, n) * (m + n))$ +* Space complexity: $O(m + n)$ + +> Where $m$ and $n$ are the lengths of the strings $str1$ and $str2$ respectively. + +--- + +## 2. Iteration (Space Optimized) + +::tabs-start + +```python +class Solution: + def gcdOfStrings(self, str1: str, str2: str) -> str: + m, n = len(str1), len(str2) + if m < n: + m, n = n, m + str1, str2 = str2, str1 + + for l in range(n, 0, -1): + if m % l != 0 or n % l != 0: + continue + + valid = True + for i in range(m): + if str1[i] != str2[i % l]: + valid = False + break + if not valid: continue + + for i in range(l, n): + if str2[i] != str2[i % l]: + valid = False + break + if valid: return str2[:l] + + return "" +``` + +```java +public class Solution { + public String gcdOfStrings(String str1, String str2) { + int m = str1.length(), n = str2.length(); + if (m < n) { + String temp = str1; + str1 = str2; + str2 = temp; + int tempLen = m; + m = n; + n = tempLen; + } + + for (int l = n; l > 0; l--) { + if (m % l != 0 || n % l != 0) { + continue; + } + + boolean valid = true; + for (int i = 0; i < m; i++) { + if (str1.charAt(i) != str2.charAt(i % l)) { + valid = false; + break; + } + } + if (!valid) continue; + + for (int i = l; i < n; i++) { + if (str2.charAt(i) != str2.charAt(i % l)) { + valid = false; + break; + } + } + if (valid) { + return str2.substring(0, l); + } + } + + return ""; + } +} +``` + +```cpp +class Solution { +public: + string gcdOfStrings(string str1, string str2) { + int m = str1.size(), n = str2.size(); + if (m < n) { + swap(m, n); + swap(str1, str2); + } + + for (int l = n; l > 0; l--) { + if (m % l != 0 || n % l != 0) { + continue; + } + + bool valid = true; + for (int i = 0; i < m; i++) { + if (str1[i] != str2[i % l]) { + valid = false; + break; + } + } + if (!valid) continue; + + for (int i = l; i < n; i++) { + if (str2[i] != str2[i % l]) { + valid = false; + break; + } + } + if (valid) { + return str2.substr(0, l); + } + } + + return ""; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} str1 + * @param {string} str2 + * @return {string} + */ + gcdOfStrings(str1, str2) { + let m = str1.length, n = str2.length; + if (m < n) { + [m, n] = [n, m]; + [str1, str2] = [str2, str1]; + } + + for (let l = n; l > 0; l--) { + if (m % l !== 0 || n % l !== 0) { + continue; + } + + let valid = true; + for (let i = 0; i < m; i++) { + if (str1[i] !== str2[i % l]) { + valid = false; + break; + } + } + if (!valid) continue; + + for (let i = l; i < n; i++) { + if (str2[i] !== str2[i % l]) { + valid = false; + break; + } + } + if (valid) { + return str2.slice(0, l); + } + } + + return ""; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(min(m, n) * (m + n))$ +* Space complexity: $O(g)$ for the output string. + +> Where $m$ is the length of the string $str1$, $n$ is the length of the string $str2$, and $g$ is the length of the output string. + +--- + +## 3. Greatest Common Divisor + +::tabs-start + +```python +class Solution: + def gcdOfStrings(self, str1: str, str2: str) -> str: + if str1 + str2 != str2 + str1: + return "" + + g = gcd(len(str1), len(str2)) + return str1[:g] +``` + +```java +public class Solution { + public String gcdOfStrings(String str1, String str2) { + if (!(str1 + str2).equals(str2 + str1)) { + return ""; + } + int g = gcd(str1.length(), str2.length()); + return str1.substring(0, g); + } + + private int gcd(int a, int b) { + return b == 0 ? a : gcd(b, a % b); + } +} +``` + +```cpp +class Solution { +public: + string gcdOfStrings(string str1, string str2) { + if (str1 + str2 != str2 + str1) { + return ""; + } + int g = __gcd((int)str1.size(), (int)str2.size()); + return str1.substr(0, g); + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} str1 + * @param {string} str2 + * @return {string} + */ + gcdOfStrings(str1, str2) { + if (str1 + str2 !== str2 + str1) { + return ""; + } + const gcd = (a, b) => (b === 0 ? a : gcd(b, a % b)); + const g = gcd(str1.length, str2.length); + return str1.slice(0, g); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m + n)$ +* Space complexity: $O(m + n)$. + +> Where $m$ and $n$ are the lengths of the strings $str1$ and $str2$ respectively. + +--- + +## 4. Greatest Common Divisor (Space Optimized) + +::tabs-start + +```python +class Solution: + def gcdOfStrings(self, str1: str, str2: str) -> str: + g = gcd(len(str1), len(str2)) + + if all(str1[i] == str1[i % g] for i in range(len(str1))) and \ + all(str2[i] == str1[i % g] for i in range(len(str2))): + return str1[:g] + return "" +``` + +```java +public class Solution { + public String gcdOfStrings(String str1, String str2) { + int g = gcd(str1.length(), str2.length()); + + for (int i = 0; i < str1.length(); i++) { + if (str1.charAt(i) != str1.charAt(i % g)) { + return ""; + } + } + + for (int i = 0; i < str2.length(); i++) { + if (str2.charAt(i) != str1.charAt(i % g)) { + return ""; + } + } + + return str1.substring(0, g); + } + + private int gcd(int a, int b) { + return b == 0 ? a : gcd(b, a % b); + } +} +``` + +```cpp +class Solution { +public: + string gcdOfStrings(string str1, string str2) { + int g = __gcd((int)str1.size(), (int)str2.size()); + + for (int i = 0; i < str1.size(); i++) { + if (str1[i] != str1[i % g]) { + return ""; + } + } + + for (int i = 0; i < str2.size(); i++) { + if (str2[i] != str1[i % g]) { + return ""; + } + } + + return str1.substr(0, g); + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} str1 + * @param {string} str2 + * @return {string} + */ + gcdOfStrings(str1, str2) { + const gcd = (a, b) => (b === 0 ? a : gcd(b, a % b)); + const g = gcd(str1.length, str2.length); + + for (let i = 0; i < str1.length; i++) { + if (str1[i] !== str1[i % g]) { + return ""; + } + } + + for (let i = 0; i < str2.length; i++) { + if (str2[i] !== str1[i % g]) { + return ""; + } + } + + return str1.slice(0, g); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m + n)$ +* Space complexity: $O(g)$ for the output string. + +> Where $m$ is the length of the string $str1$, $n$ is the length of the string $str2$, and $g$ is the GCD of $m$ and $n$. \ No newline at end of file diff --git a/articles/jump-game-vii.md b/articles/jump-game-vii.md new file mode 100644 index 000000000..d2be241f9 --- /dev/null +++ b/articles/jump-game-vii.md @@ -0,0 +1,526 @@ +## 1. Brute Force (Memoization) + +::tabs-start + +```python +class Solution: + def canReach(self, s: str, minJump: int, maxJump: int) -> bool: + n = len(s) + dp = [None] * n + dp[n - 1] = True + + def dfs(i): + if dp[i] is not None: + return dp[i] + + dp[i] = False + for j in range(i + minJump, min(n, i + maxJump + 1)): + if s[j] == '0' and dfs(j): + dp[i] = True + break + + return dp[i] + + if s[-1] == '1': + return False + return dfs(0) +``` + +```java +public class Solution { + private Boolean[] dp; + private int n; + + public boolean canReach(String s, int minJump, int maxJump) { + this.n = s.length(); + this.dp = new Boolean[n]; + dp[n - 1] = true; + + if (s.charAt(n - 1) == '1') { + return false; + } + + return dfs(0, s, minJump, maxJump); + } + + private boolean dfs(int i, String s, int minJump, int maxJump) { + if (dp[i] != null) { + return dp[i]; + } + + dp[i] = false; + for (int j = i + minJump; j <= Math.min(n - 1, i + maxJump); j++) { + if (s.charAt(j) == '0' && dfs(j, s, minJump, maxJump)) { + dp[i] = true; + break; + } + } + return dp[i]; + } +} +``` + +```cpp +class Solution { +public: + int n; + vector> dp; + + bool canReach(string s, int minJump, int maxJump) { + this->n = s.size(); + dp.resize(n, nullopt); + dp[n - 1] = true; + + if (s[n - 1] == '1') { + return false; + } + + return dfs(0, s, minJump, maxJump); + } + +private: + bool dfs(int i, string& s, int& minJump, int& maxJump) { + if (dp[i].has_value()) { + return dp[i].value(); + } + + dp[i] = false; + for (int j = i + minJump; j <= min(n - 1, i + maxJump); ++j) { + if (s[j] == '0' && dfs(j, s, minJump, maxJump)) { + dp[i] = true; + break; + } + } + return dp[i].value(); + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @param {number} minJump + * @param {number} maxJump + * @return {boolean} + */ + canReach(s, minJump, maxJump) { + const n = s.length; + const dp = new Array(n).fill(null); + dp[n - 1] = true; + + const dfs = (i) => { + if (dp[i] !== null) { + return dp[i]; + } + + dp[i] = false; + for (let j = i + minJump; j <= Math.min(n - 1, i + maxJump); j++) { + if (s[j] === '0' && dfs(j)) { + dp[i] = true; + break; + } + } + return dp[i]; + }; + + if (s[n - 1] === '1') { + return false; + } + return dfs(0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * m)$ +* Space complexity: $O(n)$ + +> Where $n$ is the length of the string $s$ and $m$ is the given range of the jump $(maxJump - minJump + 1)$. + +--- + +## 2. Breadth First Search + +::tabs-start + +```python +class Solution: + def canReach(self, s: str, minJump: int, maxJump: int) -> bool: + q = deque([0]) + farthest = 0 + + while q: + i = q.popleft() + start = max(i + minJump, farthest + 1) + for j in range(start, min(i + maxJump + 1, len(s))): + if s[j] == "0": + q.append(j) + if j == len(s) - 1: + return True + farthest = i + maxJump + + return False +``` + +```java +public class Solution { + public boolean canReach(String s, int minJump, int maxJump) { + Queue q = new LinkedList<>(); + q.add(0); + int farthest = 0; + int n = s.length(); + + while (!q.isEmpty()) { + int i = q.poll(); + int start = Math.max(i + minJump, farthest + 1); + + for (int j = start; j < Math.min(i + maxJump + 1, n); j++) { + if (s.charAt(j) == '0') { + q.add(j); + if (j == n - 1) { + return true; + } + } + } + farthest = i + maxJump; + } + + return false; + } +} +``` + +```cpp +class Solution { +public: + bool canReach(string s, int minJump, int maxJump) { + queue q; + q.push(0); + int farthest = 0; + int n = s.size(); + + while (!q.empty()) { + int i = q.front(); + q.pop(); + int start = max(i + minJump, farthest + 1); + + for (int j = start; j < min(i + maxJump + 1, n); ++j) { + if (s[j] == '0') { + q.push(j); + if (j == n - 1) { + return true; + } + } + } + farthest = i + maxJump; + } + + return false; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @param {number} minJump + * @param {number} maxJump + * @return {boolean} + */ + canReach(s, minJump, maxJump) { + const q = new Queue(); + q.push(0); + let farthest = 0; + + while (!q.isEmpty()) { + const i = q.pop(); + const start = Math.max(i + minJump, farthest + 1); + + for (let j = start; j < Math.min(i + maxJump + 1, s.length); j++) { + if (s[j] === '0') { + q.push(j); + if (j === s.length - 1) { + return true; + } + } + } + farthest = i + maxJump; + } + + return false; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Dynamic Programming (Sliding Window) + +::tabs-start + +```python +class Solution: + def canReach(self, s: str, minJump: int, maxJump: int) -> bool: + n = len(s) + if s[n - 1] == '1': + return False + + dp = [False] * n + dp[0] = True + cnt = 0 + for i in range(1, n): + if i >= minJump and dp[i - minJump]: + cnt += 1 + if i > maxJump and dp[i - maxJump - 1]: + cnt -= 1 + if cnt > 0 and s[i] == '0': + dp[i] = True + + return dp[n - 1] +``` + +```java +public class Solution { + public boolean canReach(String s, int minJump, int maxJump) { + int n = s.length(); + if (s.charAt(n - 1) == '1') { + return false; + } + + boolean[] dp = new boolean[n]; + dp[0] = true; + int cnt = 0; + + for (int i = 1; i < n; i++) { + if (i >= minJump && dp[i - minJump]) { + cnt++; + } + if (i > maxJump && dp[i - maxJump - 1]) { + cnt--; + } + if (cnt > 0 && s.charAt(i) == '0') { + dp[i] = true; + } + } + + return dp[n - 1]; + } +} +``` + +```cpp +class Solution { +public: + bool canReach(string s, int minJump, int maxJump) { + int n = s.size(); + if (s[n - 1] == '1') { + return false; + } + + vector dp(n, false); + dp[0] = true; + int cnt = 0; + + for (int i = 1; i < n; i++) { + if (i >= minJump && dp[i - minJump]) { + cnt++; + } + if (i > maxJump && dp[i - maxJump - 1]) { + cnt--; + } + if (cnt > 0 && s[i] == '0') { + dp[i] = true; + } + } + + return dp[n - 1]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @param {number} minJump + * @param {number} maxJump + * @return {boolean} + */ + canReach(s, minJump, maxJump) { + const n = s.length; + if (s[n - 1] === '1') { + return false; + } + + const dp = new Array(n).fill(false); + dp[0] = true; + let cnt = 0; + + for (let i = 1; i < n; i++) { + if (i >= minJump && dp[i - minJump]) { + cnt++; + } + if (i > maxJump && dp[i - maxJump - 1]) { + cnt--; + } + if (cnt > 0 && s[i] === '0') { + dp[i] = true; + } + } + + return dp[n - 1]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 4. Dynamic Programming (Two Pointers) + +::tabs-start + +```python +class Solution: + def canReach(self, s: str, minJump: int, maxJump: int) -> bool: + n = len(s) + if s[n - 1] == '1': + return False + + dp = [False] * n + dp[0] = True + j = 0 + for i in range(n): + if dp[i] == False: + continue + + j = max(j, i + minJump) + while j < min(i + maxJump + 1, n): + if s[j] == '0': + dp[j] = True + j += 1 + + return dp[n - 1] +``` + +```java +public class Solution { + public boolean canReach(String s, int minJump, int maxJump) { + int n = s.length(); + if (s.charAt(n - 1) == '1') { + return false; + } + + boolean[] dp = new boolean[n]; + dp[0] = true; + int j = 0; + + for (int i = 0; i < n; i++) { + if (!dp[i]) { + continue; + } + j = Math.max(j, i + minJump); + while (j < Math.min(i + maxJump + 1, n)) { + if (s.charAt(j) == '0') { + dp[j] = true; + } + j++; + } + } + + return dp[n - 1]; + } +} +``` + +```cpp +class Solution { +public: + bool canReach(string s, int minJump, int maxJump) { + int n = s.size(); + if (s[n - 1] == '1') { + return false; + } + + vector dp(n, false); + dp[0] = true; + int j = 0; + + for (int i = 0; i < n; i++) { + if (!dp[i]) { + continue; + } + j = max(j, i + minJump); + while (j < min(i + maxJump + 1, n)) { + if (s[j] == '0') { + dp[j] = true; + } + j++; + } + } + + return dp[n - 1]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @param {number} minJump + * @param {number} maxJump + * @return {boolean} + */ + canReach(s, minJump, maxJump) { + const n = s.length; + if (s[n - 1] === '1') { + return false; + } + + const dp = new Array(n).fill(false); + dp[0] = true; + let j = 0; + + for (let i = 0; i < n; i++) { + if (!dp[i]) { + continue; + } + j = Math.max(j, i + minJump); + while (j < Math.min(i + maxJump + 1, n)) { + if (s[j] === '0') { + dp[j] = true; + } + j++; + } + } + + return dp[n - 1]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/minimum-array-end.md b/articles/minimum-array-end.md new file mode 100644 index 000000000..2aba4edba --- /dev/null +++ b/articles/minimum-array-end.md @@ -0,0 +1,320 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def minEnd(self, n: int, x: int) -> int: + res = x + for i in range(n - 1): + res = (res + 1) | x + return res +``` + +```java +public class Solution { + public long minEnd(int n, int x) { + long res = x; + for (int i = 0; i < n - 1; i++) { + res = (res + 1) | x; + } + return res; + } +} +``` + +```cpp +class Solution { +public: + long long minEnd(int n, int x) { + long long res = x; + for (int i = 0; i < n - 1; i++) { + res = (res + 1) | x; + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number} x + * @return {number} + */ + minEnd(n, x) { + let res = BigInt(x); + for (let i = 0; i < n - 1; i++) { + res = (res + BigInt(1)) | BigInt(x); + } + return Number(res); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ + +--- + +## 2. Binary Representation And Bit Manipulation + +::tabs-start + +```python +class Solution: + def minEnd(self, n: int, x: int) -> int: + res = 0 + n -= 1 + + x_bin = [0] * 64 # Binary representation of x + n_bin = [0] * 64 # Binary representation of n-1 + + for i in range(32): + x_bin[i] = (x >> i) & 1 + n_bin[i] = (n >> i) & 1 + + i_x = 0 + i_n = 0 + while i_x < 63: + while i_x < 63 and x_bin[i_x] != 0: + i_x += 1 + x_bin[i_x] = n_bin[i_n] + i_x += 1 + i_n += 1 + + for i in range(64): + if x_bin[i] == 1: + res += (1 << i) + + return res +``` + +```java +public class Solution { + public long minEnd(int n, int x) { + long res = 0; + n -= 1; + + int[] x_bin = new int[64]; // Binary representation of x + int[] n_bin = new int[64]; // Binary representation of n-1 + + for (int i = 0; i < 32; i++) { + x_bin[i] = (x >> i) & 1; + n_bin[i] = (n >> i) & 1; + } + + int i_x = 0; + int i_n = 0; + while (i_x < 63) { + while (i_x < 63 && x_bin[i_x] != 0) { + i_x++; + } + x_bin[i_x] = n_bin[i_n]; + i_x++; + i_n++; + } + + for (int i = 0; i < 64; i++) { + if (x_bin[i] == 1) { + res += (1L << i); + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + long long minEnd(int n, int x) { + long long res = 0; + n -= 1; + + vector x_bin(64, 0); // Binary representation of x + vector n_bin(64, 0); // Binary representation of n-1 + + for (int i = 0; i < 32; i++) { + x_bin[i] = (x >> i) & 1; + n_bin[i] = (n >> i) & 1; + } + + int i_x = 0; + int i_n = 0; + while (i_x < 63) { + while (i_x < 63 && x_bin[i_x] != 0) { + i_x++; + } + x_bin[i_x] = n_bin[i_n]; + i_x++; + i_n++; + } + + for (int i = 0; i < 64; i++) { + if (x_bin[i] == 1) { + res += (1LL << i); + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number} x + * @return {number} + */ + minEnd(n, x) { + let res = 0n; + n -= 1; + + const x_bin = new Array(64).fill(0); // Binary representation of x + const n_bin = new Array(64).fill(0); // Binary representation of n-1 + + for (let i = 0; i < 32; i++) { + x_bin[i] = (x >> i) & 1; + n_bin[i] = (n >> i) & 1; + } + + let i_x = 0; + let i_n = 0; + while (i_x < 63) { + while (i_x < 63 && x_bin[i_x] !== 0) { + i_x++; + } + x_bin[i_x] = n_bin[i_n]; + i_x++; + i_n++; + } + + for (let i = 0; i < 64; i++) { + if (x_bin[i] === 1) { + res += BigInt(1) << BigInt(i); + } + } + + return Number(res); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(\log n)$ +* Space complexity: $O(\log n)$ + +--- + +## 3. Bit Manipulation + +::tabs-start + +```python +class Solution: + def minEnd(self, n: int, x: int) -> int: + res = x + i_x = 1 + i_n = 1 # for n-1 + + while i_n <= n - 1: + if i_x & x == 0: + if i_n & (n - 1): + res = res | i_x + i_n = i_n << 1 + i_x = i_x << 1 + + return res +``` + +```java +public class Solution { + public long minEnd(int n, int x) { + long res = x; + long i_x = 1; + long i_n = 1; // for n - 1 + + while (i_n <= n - 1) { + if ((i_x & x) == 0) { + if ((i_n & (n - 1)) != 0) { + res = res | i_x; + } + i_n = i_n << 1; + } + i_x = i_x << 1; + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + long long minEnd(int n, int x) { + long long res = x; + long long i_x = 1; + long long i_n = 1; // for n - 1 + + while (i_n <= n - 1) { + if ((i_x & x) == 0) { + if (i_n & (n - 1)) { + res = res | i_x; + } + i_n = i_n << 1; + } + i_x = i_x << 1; + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number} x + * @return {number} + */ + minEnd(n, x) { + let res = BigInt(x); + let i_x = 1n; + let i_n = 1n; + n = BigInt(n - 1); + + while (i_n <= n) { + if ((i_x & res) === 0n) { + if ((i_n & n) !== 0n) { + res = res | i_x; + } + i_n = i_n << 1n; + } + i_x = i_x << 1n; + } + + return Number(res); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(\log n)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/minimum-height-trees.md b/articles/minimum-height-trees.md new file mode 100644 index 000000000..19f63684e --- /dev/null +++ b/articles/minimum-height-trees.md @@ -0,0 +1,847 @@ +## 1. Brute Force (DFS) + +::tabs-start + +```python +class Solution: + def findMinHeightTrees(self, n: int, edges: List[List[int]]) -> List[int]: + adj = [[] for _ in range(n)] + for u, v in edges: + adj[u].append(v) + adj[v].append(u) + + def dfs(node, parent): + hgt = 0 + for nei in adj[node]: + if nei == parent: + continue + hgt = max(hgt, 1 + dfs(nei, node)) + return hgt + + minHgt = n + res = [] + for i in range(n): + curHgt = dfs(i, -1) + if curHgt == minHgt: + res.append(i) + elif curHgt < minHgt: + res = [i] + minHgt = curHgt + + return res +``` + +```java +public class Solution { + private List> adj; + + public List findMinHeightTrees(int n, int[][] edges) { + adj = new ArrayList<>(); + for (int i = 0; i < n; i++) { + adj.add(new ArrayList<>()); + } + for (int[] edge : edges) { + adj.get(edge[0]).add(edge[1]); + adj.get(edge[1]).add(edge[0]); + } + + int minHgt = n; + List result = new ArrayList<>(); + for (int i = 0; i < n; i++) { + int curHgt = dfs(i, -1); + if (curHgt == minHgt) { + result.add(i); + } else if (curHgt < minHgt) { + result = new ArrayList<>(); + result.add(i); + minHgt = curHgt; + } + } + return result; + } + + private int dfs(int node, int parent) { + int hgt = 0; + for (int nei : adj.get(node)) { + if (nei == parent) { + continue; + } + hgt = Math.max(hgt, 1 + dfs(nei, node)); + } + return hgt; + } +} +``` + +```cpp +class Solution { +private: + vector> adj; + + int dfs(int node, int parent) { + int hgt = 0; + for (int nei : adj[node]) { + if (nei == parent) + continue; + hgt = max(hgt, 1 + dfs(nei, node)); + } + return hgt; + } + +public: + vector findMinHeightTrees(int n, vector>& edges) { + adj.resize(n); + for (const auto& edge : edges) { + adj[edge[0]].push_back(edge[1]); + adj[edge[1]].push_back(edge[0]); + } + + int minHgt = n; + vector result; + for (int i = 0; i < n; i++) { + int curHgt = dfs(i, -1); + if (curHgt == minHgt) { + result.push_back(i); + } else if (curHgt < minHgt) { + result = {i}; + minHgt = curHgt; + } + } + return result; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number[][]} edges + * @return {number[]} + */ + findMinHeightTrees(n, edges) { + const adj = Array.from({ length: n }, () => []); + for (const [u, v] of edges) { + adj[u].push(v); + adj[v].push(u); + } + + const dfs = (node, parent) => { + let hgt = 0; + for (const nei of adj[node]) { + if (nei === parent) continue; + hgt = Math.max(hgt, 1 + dfs(nei, node)); + } + return hgt; + }; + + let minHgt = n; + const result = []; + for (let i = 0; i < n; i++) { + const curHgt = dfs(i, -1); + if (curHgt === minHgt) { + result.push(i); + } else if (curHgt < minHgt) { + result.length = 0; + result.push(i); + minHgt = curHgt; + } + } + return result; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(V * (V + E))$ +* Space complexity: $O(V)$ + +> Where $V$ is the number of vertices and $E$ is the number of edges. + +--- + +## 2. Dynamic Programming On Trees (Rerooting) + +::tabs-start + +```python +class Solution: + def findMinHeightTrees(self, n: int, edges: List[List[int]]) -> List[int]: + adj = [[] for _ in range(n)] + for u, v in edges: + adj[u].append(v) + adj[v].append(u) + + dp = [[0] * 2 for _ in range(n)] # top two heights for each node + + def dfs(node, parent): + for nei in adj[node]: + if nei == parent: + continue + dfs(nei, node) + curHgt = 1 + dp[nei][0] + if curHgt > dp[node][0]: + dp[node][1] = dp[node][0] + dp[node][0] = curHgt + elif curHgt > dp[node][1]: + dp[node][1] = curHgt + + def dfs1(node, parent, topHgt): + if topHgt > dp[node][0]: + dp[node][1] = dp[node][0] + dp[node][0] = topHgt + elif topHgt > dp[node][1]: + dp[node][1] = topHgt + + for nei in adj[node]: + if nei == parent: + continue + toChild = 1 + (dp[node][1] if dp[node][0] == 1 + dp[nei][0] else dp[node][0]) + dfs1(nei, node, toChild) + + dfs(0, -1) + dfs1(0, -1, 0) + + minHgt, res = n, [] + for i in range(n): + minHgt = min(minHgt, dp[i][0]) + for i in range(n): + if minHgt == dp[i][0]: + res.append(i) + return res +``` + +```java +class Solution { + private List> adj; + private int[][] dp; + + public List findMinHeightTrees(int n, int[][] edges) { + adj = new ArrayList<>(); + for (int i = 0; i < n; i++) { + adj.add(new ArrayList<>()); + } + for (int[] edge : edges) { + adj.get(edge[0]).add(edge[1]); + adj.get(edge[1]).add(edge[0]); + } + + dp = new int[n][2]; // top two heights for each node + dfs(0, -1); + dfs1(0, -1, 0); + + int minHgt = n; + List res = new ArrayList<>(); + for (int i = 0; i < n; i++) { + minHgt = Math.min(minHgt, dp[i][0]); + } + for (int i = 0; i < n; i++) { + if (minHgt == dp[i][0]) { + res.add(i); + } + } + return res; + } + + private void dfs(int node, int parent) { + for (int nei : adj.get(node)) { + if (nei == parent) continue; + dfs(nei, node); + int curHgt = 1 + dp[nei][0]; + if (curHgt > dp[node][0]) { + dp[node][1] = dp[node][0]; + dp[node][0] = curHgt; + } else if (curHgt > dp[node][1]) { + dp[node][1] = curHgt; + } + } + } + + private void dfs1(int node, int parent, int topHgt) { + if (topHgt > dp[node][0]) { + dp[node][1] = dp[node][0]; + dp[node][0] = topHgt; + } else if (topHgt > dp[node][1]) { + dp[node][1] = topHgt; + } + + for (int nei : adj.get(node)) { + if (nei == parent) continue; + int toChild = 1 + ((dp[node][0] == 1 + dp[nei][0]) ? dp[node][1] : dp[node][0]); + dfs1(nei, node, toChild); + } + } +} +``` + +```cpp +class Solution { +private: + vector> adj; + vector> dp; + + void dfs(int node, int parent) { + for (int nei : adj[node]) { + if (nei == parent) continue; + dfs(nei, node); + int curHgt = 1 + dp[nei][0]; + if (curHgt > dp[node][0]) { + dp[node][1] = dp[node][0]; + dp[node][0] = curHgt; + } else if (curHgt > dp[node][1]) { + dp[node][1] = curHgt; + } + } + } + + void dfs1(int node, int parent, int topHgt) { + if (topHgt > dp[node][0]) { + dp[node][1] = dp[node][0]; + dp[node][0] = topHgt; + } else if (topHgt > dp[node][1]) { + dp[node][1] = topHgt; + } + + for (int nei : adj[node]) { + if (nei == parent) continue; + int toChild = 1 + ((dp[node][0] == 1 + dp[nei][0]) ? dp[node][1] : dp[node][0]); + dfs1(nei, node, toChild); + } + } + +public: + vector findMinHeightTrees(int n, vector>& edges) { + adj.resize(n); + dp.assign(n, vector(2, 0)); // top two heights for each node + for (const auto& edge : edges) { + adj[edge[0]].push_back(edge[1]); + adj[edge[1]].push_back(edge[0]); + } + + dfs(0, -1); + dfs1(0, -1, 0); + + int minHgt = n; + vector res; + for (int i = 0; i < n; i++) { + minHgt = min(minHgt, dp[i][0]); + } + for (int i = 0; i < n; i++) { + if (minHgt == dp[i][0]) { + res.push_back(i); + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number[][]} edges + * @return {number[]} + */ + findMinHeightTrees(n, edges) { + const adj = Array.from({ length: n }, () => []); + for (const [u, v] of edges) { + adj[u].push(v); + adj[v].push(u); + } + + // top two heights for each node + const dp = Array.from({ length: n }, () => [0, 0]); + + const dfs = (node, parent) => { + for (const nei of adj[node]) { + if (nei === parent) continue; + dfs(nei, node); + const curHgt = 1 + dp[nei][0]; + if (curHgt > dp[node][0]) { + dp[node][1] = dp[node][0]; + dp[node][0] = curHgt; + } else if (curHgt > dp[node][1]) { + dp[node][1] = curHgt; + } + } + }; + + const dfs1 = (node, parent, topHgt) => { + if (topHgt > dp[node][0]) { + dp[node][1] = dp[node][0]; + dp[node][0] = topHgt; + } else if (topHgt > dp[node][1]) { + dp[node][1] = topHgt; + } + + for (const nei of adj[node]) { + if (nei === parent) continue; + const toChild = 1 + ((dp[node][0] === 1 + dp[nei][0]) ? dp[node][1] : dp[node][0]); + dfs1(nei, node, toChild); + } + }; + + dfs(0, -1); + dfs1(0, -1, 0); + + let minHgt = n; + const res = []; + for (let i = 0; i < n; i++) { + minHgt = Math.min(minHgt, dp[i][0]); + } + for (let i = 0; i < n; i++) { + if (minHgt === dp[i][0]) { + res.push(i); + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(V + E)$ +* Space complexity: $O(V)$ + +> Where $V$ is the number of vertices and $E$ is the number of edges. + +--- + +## 3. Find Centroids of the Tree (DFS) + +::tabs-start + +```python +class Solution: + def findMinHeightTrees(self, n: int, edges: List[List[int]]) -> List[int]: + if n == 1: + return [0] + + adj = [[] for _ in range(n)] + for u, v in edges: + adj[u].append(v) + adj[v].append(u) + + def dfs(node, parent): + farthest_node = node + max_distance = 0 + for nei in adj[node]: + if nei != parent: + nei_node, nei_distance = dfs(nei, node) + if nei_distance + 1 > max_distance: + max_distance = nei_distance + 1 + farthest_node = nei_node + return farthest_node, max_distance + + node_a, _ = dfs(0, -1) + node_b, diameter = dfs(node_a, -1) + + centroids = [] + + def find_centroids(node, parent): + if node == node_b: + centroids.append(node) + return True + for nei in adj[node]: + if nei != parent: + if find_centroids(nei, node): + centroids.append(node) + return True + return False + + find_centroids(node_a, -1) + L = len(centroids) + if diameter % 2 == 0: + return [centroids[L // 2]] + else: + return [centroids[L // 2 - 1], centroids[L // 2]] +``` + +```java +public class Solution { + private List[] adj; + private List centroids; + private int nodeB; + + public List findMinHeightTrees(int n, int[][] edges) { + if (n == 1) + return Collections.singletonList(0); + + adj = new ArrayList[n]; + for (int i = 0; i < n; ++i) + adj[i] = new ArrayList<>(); + + for (int[] edge : edges) { + adj[edge[0]].add(edge[1]); + adj[edge[1]].add(edge[0]); + } + + int nodeA = dfs(0, -1)[0]; + nodeB = dfs(nodeA, -1)[0]; + centroids = new ArrayList<>(); + findCentroids(nodeA, -1); + + int L = centroids.size(); + if (dfs(nodeA, -1)[1] % 2 == 0) { + return Collections.singletonList(centroids.get(L / 2)); + } else { + return Arrays.asList(centroids.get(L / 2 - 1), centroids.get(L / 2)); + } + } + + private int[] dfs(int node, int parent) { + int farthestNode = node, maxDistance = 0; + for (int neighbor : adj[node]) { + if (neighbor != parent) { + int[] res = dfs(neighbor, node); + if (res[1] + 1 > maxDistance) { + maxDistance = res[1] + 1; + farthestNode = res[0]; + } + } + } + return new int[] { farthestNode, maxDistance }; + } + + private boolean findCentroids(int node, int parent) { + if (node == nodeB) { + centroids.add(node); + return true; + } + for (int neighbor : adj[node]) { + if (neighbor != parent) { + if (findCentroids(neighbor, node)) { + centroids.add(node); + return true; + } + } + } + return false; + } +} +``` + +```cpp +class Solution { +public: + vector> adj; + vector centroids; + int nodeB; + + vector findMinHeightTrees(int n, vector>& edges) { + if (n == 1) + return {0}; + + adj.resize(n); + for (const auto& edge : edges) { + adj[edge[0]].push_back(edge[1]); + adj[edge[1]].push_back(edge[0]); + } + + int nodeA = dfs(0, -1).first; + nodeB = dfs(nodeA, -1).first; + findCentroids(nodeA, -1); + + int L = centroids.size(); + if (dfs(nodeA, -1).second % 2 == 0) + return {centroids[L / 2]}; + else + return {centroids[L / 2 - 1], centroids[L / 2]}; + } + +private: + pair dfs(int node, int parent) { + int farthestNode = node, maxDistance = 0; + for (int neighbor : adj[node]) { + if (neighbor != parent) { + auto res = dfs(neighbor, node); + if (res.second + 1 > maxDistance) { + maxDistance = res.second + 1; + farthestNode = res.first; + } + } + } + return {farthestNode, maxDistance}; + } + + bool findCentroids(int node, int parent) { + if (node == nodeB) { + centroids.push_back(node); + return true; + } + for (int neighbor : adj[node]) { + if (neighbor != parent) { + if (findCentroids(neighbor, node)) { + centroids.push_back(node); + return true; + } + } + } + return false; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number[][]} edges + * @return {number[]} + */ + findMinHeightTrees(n, edges) { + if (n === 1) return [0]; + + const adj = Array.from({ length: n }, () => []); + + for (const [u, v] of edges) { + adj[u].push(v); + adj[v].push(u); + } + + const dfs = (node, parent) => { + let farthestNode = node; + let maxDistance = 0; + + for (const neighbor of adj[node]) { + if (neighbor !== parent) { + const [farthest, dist] = dfs(neighbor, node); + if (dist + 1 > maxDistance) { + maxDistance = dist + 1; + farthestNode = farthest; + } + } + } + return [farthestNode, maxDistance]; + }; + + const findCentroids = (node, parent, centroids) => { + if (node === nodeB) { + centroids.push(node); + return true; + } + + for (const neighbor of adj[node]) { + if (neighbor !== parent) { + if (findCentroids(neighbor, node, centroids)) { + centroids.push(node); + return true; + } + } + } + return false; + }; + + const [nodeA] = dfs(0, -1); + const [nodeB, diameter] = dfs(nodeA, -1); + this.nodeB = nodeB; + + const centroids = []; + findCentroids(nodeA, -1, centroids); + + const L = centroids.length; + return diameter % 2 === 0 + ? [centroids[Math.floor(L / 2)]] + : [centroids[Math.floor(L / 2) - 1], centroids[Math.floor(L / 2)]]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(V + E)$ +* Space complexity: $O(V)$ + +> Where $V$ is the number of vertices and $E$ is the number of edges. + +--- + +## 4. Topological Sorting (BFS) + +::tabs-start + +```python +class Solution: + def findMinHeightTrees(self, n: int, edges: List[List[int]]) -> List[int]: + if n == 1: + return [0] + + adj = defaultdict(list) + for n1, n2 in edges: + adj[n1].append(n2) + adj[n2].append(n1) + + edge_cnt = {} + leaves = deque() + + for src, neighbors in adj.items(): + edge_cnt[src] = len(neighbors) + if len(neighbors) == 1: + leaves.append(src) + + while leaves: + if n <= 2: + return list(leaves) + for _ in range(len(leaves)): + node = leaves.popleft() + n -= 1 + for nei in adj[node]: + edge_cnt[nei] -= 1 + if edge_cnt[nei] == 1: + leaves.append(nei) +``` + +```java +public class Solution { + public List findMinHeightTrees(int n, int[][] edges) { + if (n == 1) return Collections.singletonList(0); + + List[] adj = new ArrayList[n]; + for (int i = 0; i < n; ++i) + adj[i] = new ArrayList<>(); + + for (int[] edge : edges) { + adj[edge[0]].add(edge[1]); + adj[edge[1]].add(edge[0]); + } + + int[] edge_cnt = new int[n]; + Queue leaves = new LinkedList<>(); + + for (int i = 0; i < n; i++) { + edge_cnt[i] = adj[i].size(); + if (adj[i].size() == 1) + leaves.offer(i); + } + + while (!leaves.isEmpty()) { + if (n <= 2) return new ArrayList<>(leaves); + int size = leaves.size(); + for (int i = 0; i < size; ++i) { + int node = leaves.poll(); + n--; + for (int nei : adj[node]) { + edge_cnt[nei]--; + if (edge_cnt[nei] == 1) + leaves.offer(nei); + } + } + } + + return new ArrayList<>(); + } +} +``` + +```cpp +class Solution { +public: + vector findMinHeightTrees(int n, vector>& edges) { + if (n == 1) return {0}; + + vector> adj(n); + for (auto& edge : edges) { + adj[edge[0]].push_back(edge[1]); + adj[edge[1]].push_back(edge[0]); + } + + vector edge_cnt(n); + queue leaves; + + for (int i = 0; i < n; ++i) { + edge_cnt[i] = adj[i].size(); + if (adj[i].size() == 1) + leaves.push(i); + } + + while (!leaves.empty()) { + if (n <= 2) { + vector result; + while (!leaves.empty()) { + result.push_back(leaves.front()); + leaves.pop(); + } + return result; + } + int size = leaves.size(); + for (int i = 0; i < size; ++i) { + int node = leaves.front(); + leaves.pop(); + --n; + for (int& nei : adj[node]) { + --edge_cnt[nei]; + if (edge_cnt[nei] == 1) + leaves.push(nei); + } + } + } + + return {}; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number[][]} edges + * @return {number[]} + */ + findMinHeightTrees(n, edges) { + if (n === 1) return [0]; + + const adj = Array.from({ length: n }, () => []); + + for (const [n1, n2] of edges) { + adj[n1].push(n2); + adj[n2].push(n1); + } + + const edgeCnt = Array(n).fill(0); + const leaves = new Queue(); + + for (let i = 0; i < n; i++) { + edgeCnt[i] = adj[i].length; + if (adj[i].length === 1) { + leaves.push(i); + } + } + + while (!leaves.isEmpty()) { + if (n <= 2) return leaves.toArray(); + const size = leaves.size(); + for (let i = 0; i < size; i++) { + const node = leaves.pop(); + n--; + for (const nei of adj[node]) { + edgeCnt[nei]--; + if (edgeCnt[nei] === 1) { + leaves.push(nei); + } + } + } + } + + return []; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(V + E)$ +* Space complexity: $O(V)$ + +> Where $V$ is the number of vertices and $E$ is the number of edges. \ No newline at end of file diff --git a/articles/path-with-minimum-effort.md b/articles/path-with-minimum-effort.md new file mode 100644 index 000000000..bccc0e7b9 --- /dev/null +++ b/articles/path-with-minimum-effort.md @@ -0,0 +1,851 @@ +## 1. Dijkstra's Algorithm + +::tabs-start + +```python +class Solution: + def minimumEffortPath(self, heights: List[List[int]]) -> int: + ROWS, COLS = len(heights), len(heights[0]) + minHeap = [[0, 0, 0]] # [diff, row, col] + visit = set() + directions = [[0, 1], [0, -1], [1, 0], [-1, 0]] + + while minHeap: + diff, r, c = heapq.heappop(minHeap) + + if (r, c) in visit: + continue + + visit.add((r, c)) + + if (r, c) == (ROWS - 1, COLS - 1): + return diff + + for dr, dc in directions: + newR, newC = r + dr, c + dc + if ( + newR < 0 or newC < 0 or + newR >= ROWS or newC >= COLS or + (newR, newC) in visit + ): + continue + + newDiff = max(diff, abs(heights[r][c] - heights[newR][newC])) + heapq.heappush(minHeap, [newDiff, newR, newC]) + + return 0 +``` + +```java +public class Solution { + public int minimumEffortPath(int[][] heights) { + int rows = heights.length; + int cols = heights[0].length; + int[][] dist = new int[rows][cols]; + for (int[] row : dist) { + Arrays.fill(row, Integer.MAX_VALUE); + } + dist[0][0] = 0; + + PriorityQueue minHeap = new PriorityQueue<>((a, b) -> a[0] - b[0]); + minHeap.offer(new int[]{0, 0, 0}); // {diff, row, col} + + int[][] directions = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}}; + + while (!minHeap.isEmpty()) { + int[] curr = minHeap.poll(); + int diff = curr[0], r = curr[1], c = curr[2]; + + if (r == rows - 1 && c == cols - 1) return diff; + if (dist[r][c] < diff) continue; + + for (int[] dir : directions) { + int newR = r + dir[0], newC = c + dir[1]; + if (newR < 0 || newC < 0 || newR >= rows || newC >= cols) { + continue; + } + + int newDiff = Math.max(diff, Math.abs(heights[r][c] - heights[newR][newC])); + if (newDiff < dist[newR][newC]) { + dist[newR][newC] = newDiff; + minHeap.offer(new int[]{newDiff, newR, newC}); + } + } + } + + return 0; + } +} +``` + +```cpp +class Solution { +public: + int minimumEffortPath(vector>& heights) { + int rows = heights.size(), cols = heights[0].size(); + vector> dist(rows, vector(cols, INT_MAX)); + dist[0][0] = 0; + + priority_queue, vector>, greater<>> minHeap; + minHeap.push({0, 0, 0}); // {diff, row, col} + + vector> directions = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}}; + + while (!minHeap.empty()) { + auto curr = minHeap.top(); + minHeap.pop(); + int diff = curr[0], r = curr[1], c = curr[2]; + + if (r == rows - 1 && c == cols - 1) return diff; + if (dist[r][c] < diff) continue; + + for (auto& dir : directions) { + int newR = r + dir[0], newC = c + dir[1]; + if (newR < 0 || newC < 0 || newR >= rows || newC >= cols) { + continue; + } + + int newDiff = max(diff, abs(heights[r][c] - heights[newR][newC])); + if (newDiff < dist[newR][newC]) { + dist[newR][newC] = newDiff; + minHeap.push({newDiff, newR, newC}); + } + } + } + + return 0; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} heights + * @return {number} + */ + minimumEffortPath(heights) { + const rows = heights.length; + const cols = heights[0].length; + const dist = Array.from({ length: rows }, () => + Array(cols).fill(Infinity) + ); + dist[0][0] = 0; + + const minHeap = new MinPriorityQueue({ priority: (a) => a[0] }); + minHeap.enqueue([0, 0, 0]); // [diff, row, col] + + const directions = [ + [0, 1], [0, -1], + [1, 0], [-1, 0], + ]; + + while (!minHeap.isEmpty()) { + const [diff, r, c] = minHeap.dequeue().element; + + if (r === rows - 1 && c === cols - 1) return diff; + if (dist[r][c] < diff) continue; + + for (const [dr, dc] of directions) { + const newR = r + dr; + const newC = c + dc; + if (newR < 0 || newC < 0 || newR >= rows || newC >= cols) { + continue; + } + + const newDiff = Math.max( + diff, + Math.abs(heights[r][c] - heights[newR][newC]) + ); + if (newDiff < dist[newR][newC]) { + dist[newR][newC] = newDiff; + minHeap.enqueue([newDiff, newR, newC]); + } + } + } + + return 0; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n * \log (m * n))$ +* Space complexity: $O(m * n)$ + +> Where $m$ is the number of rows and $n$ is the number of columns in the given matrix. + +--- + +## 2. Binary Search + DFS + +::tabs-start + +```python +class Solution: + def minimumEffortPath(self, heights: List[List[int]]) -> int: + ROWS, COLS = len(heights), len(heights[0]) + directions = [[0, 1], [0, -1], [1, 0], [-1, 0]] + + def dfs(r, c, limit, visited): + if r == ROWS - 1 and c == COLS - 1: + return True + + visited.add((r, c)) + for dr, dc in directions: + newR, newC = r + dr, c + dc + if (newR < 0 or newC < 0 or + newR >= ROWS or newC >= COLS or + (newR, newC) in visited or + abs(heights[newR][newC] - heights[r][c]) > limit): + continue + + if dfs(newR, newC, limit, visited): + return True + return False + + l, r = 0, 1000000 + res = r + + while l <= r: + mid = (l + r) // 2 + if dfs(0, 0, mid, set()): + res = mid + r = mid - 1 + else: + l = mid + 1 + + return res +``` + +```java +public class Solution { + private int ROWS, COLS; + private int[][] heights; + private int[][] directions = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}}; + private boolean[][] visited; + + public int minimumEffortPath(int[][] heights) { + this.heights = heights; + this.ROWS = heights.length; + this.COLS = heights[0].length; + this.visited = new boolean[ROWS][COLS]; + + int l = 0, r = 1_000_000, res = r; + + while (l <= r) { + int mid = (l + r) / 2; + for (boolean[] row : visited) Arrays.fill(row, false); + if (dfs(0, 0, mid)) { + res = mid; + r = mid - 1; + } else { + l = mid + 1; + } + } + + return res; + } + + private boolean dfs(int r, int c, int limit) { + if (r == ROWS - 1 && c == COLS - 1) { + return true; + } + + visited[r][c] = true; + for (int[] dir : directions) { + int newR = r + dir[0]; + int newC = c + dir[1]; + if (newR < 0 || newC < 0 || newR >= ROWS || newC >= COLS || visited[newR][newC]) { + continue; + } + if (Math.abs(heights[newR][newC] - heights[r][c]) > limit) { + continue; + } + if (dfs(newR, newC, limit)) { + return true; + } + } + return false; + } +} +``` + +```cpp +class Solution { +private: + int ROWS, COLS; + vector> heights; + vector> visited; + vector> directions = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}}; + +public: + int minimumEffortPath(vector>& heights) { + this->heights = heights; + this->ROWS = heights.size(); + this->COLS = heights[0].size(); + this->visited = vector>(ROWS, vector(COLS, false)); + + int l = 0, r = 1'000'000, res = r; + while (l <= r) { + int mid = (l + r) / 2; + for (auto& row : visited) { + fill(row.begin(), row.end(), false); + } + if (dfs(0, 0, mid)) { + res = mid; + r = mid - 1; + } else { + l = mid + 1; + } + } + return res; + } + +private: + bool dfs(int r, int c, int limit) { + if (r == ROWS - 1 && c == COLS - 1) { + return true; + } + + visited[r][c] = true; + for (const auto& dir : directions) { + int newR = r + dir[0]; + int newC = c + dir[1]; + if (newR < 0 || newC < 0 || newR >= ROWS || newC >= COLS || visited[newR][newC]) { + continue; + } + if (abs(heights[newR][newC] - heights[r][c]) > limit) { + continue; + } + if (dfs(newR, newC, limit)) { + return true; + } + } + return false; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} heights + * @return {number} + */ + minimumEffortPath(heights) { + const ROWS = heights.length; + const COLS = heights[0].length; + const directions = [ + [0, 1], [0, -1], + [1, 0], [-1, 0] + ]; + let visited = Array.from({ length: ROWS }, () => + Array(COLS).fill(false) + ); + + const dfs = (r, c, limit) => { + if (r === ROWS - 1 && c === COLS - 1) { + return true; + } + + visited[r][c] = true; + for (const [dr, dc] of directions) { + const newR = r + dr; + const newC = c + dc; + + if (newR < 0 || newC < 0 || + newR >= ROWS || newC >= COLS || + visited[newR][newC]) { + continue; + } + if (Math.abs(heights[newR][newC] - heights[r][c]) > limit) { + continue; + } + if (dfs(newR, newC, limit)) { + return true; + } + } + return false; + }; + + let l = 0, r = 1_000_000, res = r; + while (l <= r) { + const mid = Math.floor((l + r) / 2); + for (const row of visited) { + row.fill(false); + } + if (dfs(0, 0, mid)) { + res = mid; + r = mid - 1; + } else { + l = mid + 1; + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n * \log (m * n))$ +* Space complexity: $O(m * n)$ + +> Where $m$ is the number of rows and $n$ is the number of columns in the given matrix. + +--- + +## 3. Kruskal's Algorithm + +::tabs-start + +```python +class DSU: + def __init__(self, n): + self.Parent = list(range(n + 1)) + self.Size = [1] * (n + 1) + + def find(self, node): + if self.Parent[node] != node: + self.Parent[node] = self.find(self.Parent[node]) + return self.Parent[node] + + def union(self, u, v): + pu = self.find(u) + pv = self.find(v) + if pu == pv: + return False + if self.Size[pu] < self.Size[pv]: + pu, pv = pv, pu + self.Size[pu] += self.Size[pv] + self.Parent[pv] = pu + return True + + +class Solution: + def minimumEffortPath(self, heights: List[List[int]]) -> int: + ROWS, COLS = len(heights), len(heights[0]) + edges = [] + for r in range(ROWS): + for c in range(COLS): + if r + 1 < ROWS: + edges.append((abs(heights[r][c] - heights[r + 1][c]), r * COLS + c, (r + 1) * COLS + c)) + if c + 1 < COLS: + edges.append((abs(heights[r][c] - heights[r][c + 1]), r * COLS + c, r * COLS + c + 1)) + + edges.sort() + dsu = DSU(ROWS * COLS) + for weight, u, v in edges: + dsu.union(u, v) + if dsu.find(0) == dsu.find(ROWS * COLS - 1): + return weight + return 0 +``` + +```java +class DSU { + private int[] Parent; + private int[] Size; + + public DSU(int n) { + Parent = new int[n + 1]; + Size = new int[n + 1]; + for (int i = 0; i <= n; i++) { + Parent[i] = i; + Size[i] = 1; + } + } + + public int find(int node) { + if (Parent[node] != node) { + Parent[node] = find(Parent[node]); + } + return Parent[node]; + } + + public boolean union(int u, int v) { + int pu = find(u); + int pv = find(v); + if (pu == pv) { + return false; + } + if (Size[pu] < Size[pv]) { + int temp = pu; + pu = pv; + pv = temp; + } + Size[pu] += Size[pv]; + Parent[pv] = pu; + return true; + } +} + +public class Solution { + public int minimumEffortPath(int[][] heights) { + int ROWS = heights.length; + int COLS = heights[0].length; + List edges = new ArrayList<>(); + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + if (r + 1 < ROWS) { + edges.add(new int[]{Math.abs(heights[r][c] - heights[r + 1][c]), r * COLS + c, (r + 1) * COLS + c}); + } + if (c + 1 < COLS) { + edges.add(new int[]{Math.abs(heights[r][c] - heights[r][c + 1]), r * COLS + c, r * COLS + c + 1}); + } + } + } + + edges.sort(Comparator.comparingInt(a -> a[0])); + DSU dsu = new DSU(ROWS * COLS); + for (int[] edge : edges) { + int weight = edge[0], u = edge[1], v = edge[2]; + dsu.union(u, v); + if (dsu.find(0) == dsu.find(ROWS * COLS - 1)) { + return weight; + } + } + return 0; + } +} +``` + +```cpp +class DSU { +private: + vector Parent, Size; + +public: + DSU(int n) { + Parent.resize(n + 1); + Size.resize(n + 1, 1); + for (int i = 0; i <= n; i++) { + Parent[i] = i; + } + } + + int find(int node) { + if (Parent[node] != node) { + Parent[node] = find(Parent[node]); + } + return Parent[node]; + } + + bool unionSets(int u, int v) { + int pu = find(u); + int pv = find(v); + if (pu == pv) { + return false; + } + if (Size[pu] < Size[pv]) { + swap(pu, pv); + } + Size[pu] += Size[pv]; + Parent[pv] = pu; + return true; + } +}; + +class Solution { +public: + int minimumEffortPath(vector>& heights) { + int ROWS = heights.size(); + int COLS = heights[0].size(); + vector> edges; + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + if (r + 1 < ROWS) { + edges.push_back({abs(heights[r][c] - heights[r + 1][c]), r * COLS + c, (r + 1) * COLS + c}); + } + if (c + 1 < COLS) { + edges.push_back({abs(heights[r][c] - heights[r][c + 1]), r * COLS + c, r * COLS + c + 1}); + } + } + } + + sort(edges.begin(), edges.end()); + DSU dsu(ROWS * COLS); + for (auto& edge : edges) { + int weight, u, v; + tie(weight, u, v) = edge; + dsu.unionSets(u, v); + if (dsu.find(0) == dsu.find(ROWS * COLS - 1)) { + return weight; + } + } + return 0; + } +}; +``` + +```javascript +class DSU { + /** + * @constructor + * @param {number} n + */ + constructor(n) { + this.Parent = Array.from({ length: n + 1 }, (_, i) => i); + this.Size = Array(n + 1).fill(1); + } + + /** + * @param {number} node + * @return {number} + */ + find(node) { + if (this.Parent[node] !== node) { + this.Parent[node] = this.find(this.Parent[node]); + } + return this.Parent[node]; + } + + /** + * @param {number} u + * @param {number} v + * @return {boolean} + */ + union(u, v) { + let pu = this.find(u), pv = this.find(v); + if (pu === pv) return false; + if (this.Size[pu] < this.Size[pv]) [pu, pv] = [pv, pu]; + this.Size[pu] += this.Size[pv]; + this.Parent[pv] = pu; + return true; + } +} + +class Solution { + /** + * @param {number[][]} heights + * @return {number} + */ + minimumEffortPath(heights) { + const ROWS = heights.length; + const COLS = heights[0].length; + const edges = []; + for (let r = 0; r < ROWS; r++) { + for (let c = 0; c < COLS; c++) { + if (r + 1 < ROWS) { + edges.push([Math.abs(heights[r][c] - heights[r + 1][c]), r * COLS + c, (r + 1) * COLS + c]); + } + if (c + 1 < COLS) { + edges.push([Math.abs(heights[r][c] - heights[r][c + 1]), r * COLS + c, r * COLS + c + 1]); + } + } + } + + edges.sort((a, b) => a[0] - b[0]); + const dsu = new DSU(ROWS * COLS); + for (const [weight, u, v] of edges) { + dsu.union(u, v); + if (dsu.find(0) === dsu.find(ROWS * COLS - 1)) { + return weight; + } + } + return 0; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n * \log (m * n))$ +* Space complexity: $O(m * n)$ + +> Where $m$ is the number of rows and $n$ is the number of columns in the given matrix. + +--- + +## 4. Shortest Path Faster Algorithm + +::tabs-start + +```python +class Solution: + def minimumEffortPath(self, heights: List[List[int]]) -> int: + ROWS, COLS = len(heights), len(heights[0]) + dist = [float('inf')] * (ROWS * COLS) + dist[0] = 0 + in_queue = [False] * (ROWS * COLS) + + def index(r, c): + return r * COLS + c + + queue = deque([0]) + in_queue[0] = True + directions = [(0, 1), (0, -1), (1, 0), (-1, 0)] + + while queue: + u = queue.popleft() + in_queue[u] = False + r, c = divmod(u, COLS) + + for dr, dc in directions: + newR, newC = r + dr, c + dc + if 0 <= newR < ROWS and 0 <= newC < COLS: + v = index(newR, newC) + weight = abs(heights[r][c] - heights[newR][newC]) + new_dist = max(dist[u], weight) + if new_dist < dist[v]: + dist[v] = new_dist + if not in_queue[v]: + queue.append(v) + in_queue[v] = True + + return dist[ROWS * COLS - 1] +``` + +```java +public class Solution { + public int minimumEffortPath(int[][] heights) { + int ROWS = heights.length, COLS = heights[0].length; + int[] dist = new int[ROWS * COLS]; + Arrays.fill(dist, Integer.MAX_VALUE); + dist[0] = 0; + + boolean[] inQueue = new boolean[ROWS * COLS]; + Queue queue = new LinkedList<>(); + queue.offer(0); + inQueue[0] = true; + + int[][] directions = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}}; + + while (!queue.isEmpty()) { + int u = queue.poll(); + inQueue[u] = false; + + int r = u / COLS, c = u % COLS; + + for (int[] dir : directions) { + int newR = r + dir[0], newC = c + dir[1]; + if (newR >= 0 && newC >= 0 && newR < ROWS && newC < COLS) { + int v = newR * COLS + newC; + int weight = Math.abs(heights[r][c] - heights[newR][newC]); + int newDist = Math.max(dist[u], weight); + if (newDist < dist[v]) { + dist[v] = newDist; + if (!inQueue[v]) { + queue.offer(v); + inQueue[v] = true; + } + } + } + } + } + + return dist[ROWS * COLS - 1]; + } +} +``` + +```cpp +class Solution { +public: + int minimumEffortPath(vector>& heights) { + int ROWS = heights.size(), COLS = heights[0].size(); + vector dist(ROWS * COLS, INT_MAX); + dist[0] = 0; + + vector inQueue(ROWS * COLS, false); + queue q; + q.push(0); + inQueue[0] = true; + + vector> directions = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}}; + + while (!q.empty()) { + int u = q.front(); + q.pop(); + inQueue[u] = false; + + int r = u / COLS, c = u % COLS; + + for (const auto& dir : directions) { + int newR = r + dir[0], newC = c + dir[1]; + if (newR >= 0 && newC >= 0 && newR < ROWS && newC < COLS) { + int v = newR * COLS + newC; + int weight = abs(heights[r][c] - heights[newR][newC]); + int newDist = max(dist[u], weight); + if (newDist < dist[v]) { + dist[v] = newDist; + if (!inQueue[v]) { + q.push(v); + inQueue[v] = true; + } + } + } + } + } + + return dist[ROWS * COLS - 1]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} heights + * @return {number} + */ + minimumEffortPath(heights) { + const ROWS = heights.length, COLS = heights[0].length; + const dist = Array(ROWS * COLS).fill(Infinity); + dist[0] = 0; + + const inQueue = Array(ROWS * COLS).fill(false); + const queue = new Queue(); + queue.push(0); + inQueue[0] = true; + + const directions = [ + [0, 1], [0, -1], + [1, 0], [-1, 0] + ]; + + while (!queue.isEmpty()) { + const u = queue.pop(); + inQueue[u] = false; + + const r = Math.floor(u / COLS), c = u % COLS; + for (const [dr, dc] of directions) { + const newR = r + dr, newC = c + dc; + if (newR >= 0 && newC >= 0 && newR < ROWS && newC < COLS) { + const v = newR * COLS + newC; + const weight = Math.abs(heights[r][c] - heights[newR][newC]); + const newDist = Math.max(dist[u], weight); + if (newDist < dist[v]) { + dist[v] = newDist; + if (!inQueue[v]) { + queue.push(v); + inQueue[v] = true; + } + } + } + } + } + + return dist[ROWS * COLS - 1]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: + * $O(m * n)$ time in average case. + * $O(m ^ 2 * n ^ 2)$ time in worst case. +* Space complexity: $O(m * n)$ + +> Where $m$ is the number of rows and $n$ is the number of columns in the given matrix. \ No newline at end of file diff --git a/articles/reorganize-string.md b/articles/reorganize-string.md new file mode 100644 index 000000000..57d672327 --- /dev/null +++ b/articles/reorganize-string.md @@ -0,0 +1,546 @@ +## 1. Frequency Count + +::tabs-start + +```python +class Solution: + def reorganizeString(self, s: str) -> str: + freq = [0] * 26 + for char in s: + freq[ord(char) - ord('a')] += 1 + + max_freq = max(freq) + if max_freq > (len(s) + 1) // 2: + return "" + + res = [] + while len(res) < len(s): + maxIdx = freq.index(max(freq)) + char = chr(maxIdx + ord('a')) + res.append(char) + freq[maxIdx] -= 1 + if freq[maxIdx] == 0: + continue + + tmp = freq[maxIdx] + freq[maxIdx] = float("-inf") + nextMaxIdx = freq.index(max(freq)) + char = chr(nextMaxIdx + ord('a')) + res.append(char) + freq[maxIdx] = tmp + freq[nextMaxIdx] -= 1 + + return ''.join(res) +``` + +```java +public class Solution { + public String reorganizeString(String s) { + int[] freq = new int[26]; + for (char c : s.toCharArray()) { + freq[c - 'a']++; + } + + int maxFreq = Arrays.stream(freq).max().getAsInt(); + if (maxFreq > (s.length() + 1) / 2) { + return ""; + } + + StringBuilder res = new StringBuilder(); + while (res.length() < s.length()) { + int maxIdx = findMaxIndex(freq); + char maxChar = (char) (maxIdx + 'a'); + res.append(maxChar); + freq[maxIdx]--; + + if (freq[maxIdx] == 0) { + continue; + } + + int tmp = freq[maxIdx]; + freq[maxIdx] = Integer.MIN_VALUE; + int nextMaxIdx = findMaxIndex(freq); + char nextMaxChar = (char) (nextMaxIdx + 'a'); + res.append(nextMaxChar); + freq[maxIdx] = tmp; + freq[nextMaxIdx]--; + } + + return res.toString(); + } + + private int findMaxIndex(int[] freq) { + int maxIdx = 0; + for (int i = 1; i < freq.length; i++) { + if (freq[i] > freq[maxIdx]) { + maxIdx = i; + } + } + return maxIdx; + } +} +``` + +```cpp +class Solution { +public: + string reorganizeString(string s) { + vector freq(26, 0); + for (char c : s) { + freq[c - 'a']++; + } + + int maxFreq = *max_element(freq.begin(), freq.end()); + if (maxFreq > (s.size() + 1) / 2) { + return ""; + } + + string res; + while (res.size() < s.size()) { + int maxIdx = findMaxIndex(freq); + char maxChar = 'a' + maxIdx; + res += maxChar; + freq[maxIdx]--; + + if (freq[maxIdx] == 0) { + continue; + } + + int tmp = freq[maxIdx]; + freq[maxIdx] = INT_MIN; + int nextMaxIdx = findMaxIndex(freq); + char nextMaxChar = 'a' + nextMaxIdx; + res += nextMaxChar; + freq[maxIdx] = tmp; + freq[nextMaxIdx]--; + } + + return res; + } + +private: + int findMaxIndex(const vector& freq) { + int maxIdx = 0; + for (int i = 1; i < freq.size(); i++) { + if (freq[i] > freq[maxIdx]) { + maxIdx = i; + } + } + return maxIdx; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {string} + */ + reorganizeString(s) { + const freq = new Array(26).fill(0); + for (const char of s) { + freq[char.charCodeAt(0) - 'a'.charCodeAt(0)]++; + } + + const maxFreq = Math.max(...freq); + if (maxFreq > Math.floor((s.length + 1) / 2)) { + return ""; + } + + const findMaxIndex = () => { + let maxIdx = 0; + for (let i = 1; i < freq.length; i++) { + if (freq[i] > freq[maxIdx]) { + maxIdx = i; + } + } + return maxIdx; + }; + + const res = []; + while (res.length < s.length) { + const maxIdx = findMaxIndex(); + const maxChar = String.fromCharCode(maxIdx + 'a'.charCodeAt(0)); + res.push(maxChar); + freq[maxIdx]--; + + if (freq[maxIdx] === 0) { + continue; + } + + const tmp = freq[maxIdx]; + freq[maxIdx] = -Infinity; + const nextMaxIdx = findMaxIndex(); + const nextMaxChar = String.fromCharCode(nextMaxIdx + 'a'.charCodeAt(0)); + res.push(nextMaxChar); + freq[maxIdx] = tmp; + freq[nextMaxIdx]--; + } + + return res.join(''); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: + * $O(1)$ extra space, since we have at most $26$ different characters. + * $O(n)$ space for the output string. + +--- + +## 2. Frequency Count (Max-Heap) + +::tabs-start + +```python +class Solution: + def reorganizeString(self, s: str) -> str: + count = Counter(s) + maxHeap = [[-cnt, char] for char, cnt in count.items()] + heapq.heapify(maxHeap) + + prev = None + res = "" + while maxHeap or prev: + if prev and not maxHeap: + return "" + + cnt, char = heapq.heappop(maxHeap) + res += char + cnt += 1 + + if prev: + heapq.heappush(maxHeap, prev) + prev = None + + if cnt != 0: + prev = [cnt, char] + + return res +``` + +```java +public class Solution { + public String reorganizeString(String s) { + int[] freq = new int[26]; + for (char c : s.toCharArray()) { + freq[c - 'a']++; + } + + PriorityQueue maxHeap = new PriorityQueue<>((a, b) -> b[0] - a[0]); + for (int i = 0; i < 26; i++) { + if (freq[i] > 0) { + maxHeap.offer(new int[]{freq[i], i}); + } + } + + StringBuilder res = new StringBuilder(); + int[] prev = null; + while (!maxHeap.isEmpty() || prev != null) { + if (prev != null && maxHeap.isEmpty()) { + return ""; + } + + int[] curr = maxHeap.poll(); + res.append((char) (curr[1] + 'a')); + curr[0]--; + + if (prev != null) { + maxHeap.offer(prev); + prev = null; + } + + if (curr[0] > 0) { + prev = curr; + } + } + + return res.toString(); + } +} +``` + +```cpp +class Solution { +public: + string reorganizeString(string s) { + vector freq(26, 0); + for (char& c : s) { + freq[c - 'a']++; + } + + priority_queue> maxHeap; + for (int i = 0; i < 26; i++) { + if (freq[i] > 0) { + maxHeap.push({freq[i], 'a' + i}); + } + } + + string res = ""; + pair prev = {0, ' '}; + while (!maxHeap.empty() || prev.first > 0) { + if (prev.first > 0 && maxHeap.empty()) { + return ""; + } + + auto [count, char_] = maxHeap.top(); + maxHeap.pop(); + res += char_; + count--; + + if (prev.first > 0) { + maxHeap.push(prev); + } + + prev = {count, char_}; + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {string} + */ + reorganizeString(s) { + const freq = new Array(26).fill(0); + for (const char of s) { + freq[char.charCodeAt(0) - 'a'.charCodeAt(0)]++; + } + + const maxHeap = new MaxPriorityQueue({ + priority: ([count]) => count, + }); + for (let i = 0; i < 26; i++) { + if (freq[i] > 0) { + maxHeap.enqueue([freq[i], String.fromCharCode(i + 'a'.charCodeAt(0))]); + } + } + + let res = ''; + let prev = null; + + while (!maxHeap.isEmpty() || prev) { + if (prev && maxHeap.isEmpty()) { + return ''; + } + + const [count, char] = maxHeap.dequeue().element; + res += char; + + if (prev) { + maxHeap.enqueue(prev); + prev = null; + } + if (count > 1) { + prev = [count - 1, char]; + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: + * $O(1)$ extra space, since we have at most $26$ different characters. + * $O(n)$ space for the output string. + +--- + +## 3. Frequency Count (Greedy) + +::tabs-start + +```python +class Solution: + def reorganizeString(self, s: str) -> str: + freq = [0] * 26 + for char in s: + freq[ord(char) - ord('a')] += 1 + + max_idx = freq.index(max(freq)) + max_freq = freq[max_idx] + if max_freq > (len(s) + 1) // 2: + return "" + + res = [''] * len(s) + idx = 0 + max_char = chr(max_idx + ord('a')) + + while freq[max_idx] > 0: + res[idx] = max_char + idx += 2 + freq[max_idx] -= 1 + + for i in range(26): + while freq[i] > 0: + if idx >= len(s): + idx = 1 + res[idx] = chr(i + ord('a')) + idx += 2 + freq[i] -= 1 + + return ''.join(res) +``` + +```java +public class Solution { + public String reorganizeString(String s) { + int[] freq = new int[26]; + for (char c : s.toCharArray()) { + freq[c - 'a']++; + } + + int maxIdx = 0; + for (int i = 1; i < 26; i++) { + if (freq[i] > freq[maxIdx]) { + maxIdx = i; + } + } + + int maxFreq = freq[maxIdx]; + if (maxFreq > (s.length() + 1) / 2) { + return ""; + } + + char[] res = new char[s.length()]; + int idx = 0; + char maxChar = (char) (maxIdx + 'a'); + + while (freq[maxIdx] > 0) { + res[idx] = maxChar; + idx += 2; + freq[maxIdx]--; + } + + for (int i = 0; i < 26; i++) { + while (freq[i] > 0) { + if (idx >= s.length()) { + idx = 1; + } + res[idx] = (char) (i + 'a'); + idx += 2; + freq[i]--; + } + } + + return new String(res); + } +} +``` + +```cpp +class Solution { +public: + string reorganizeString(string s) { + vector freq(26, 0); + for (char& c : s) { + freq[c - 'a']++; + } + + int maxIdx = max_element(freq.begin(), freq.end()) - freq.begin(); + int maxFreq = freq[maxIdx]; + if (maxFreq > (s.size() + 1) / 2) { + return ""; + } + + string res(s.size(), ' '); + int idx = 0; + char maxChar = 'a' + maxIdx; + + while (freq[maxIdx] > 0) { + res[idx] = maxChar; + idx += 2; + freq[maxIdx]--; + } + + for (int i = 0; i < 26; i++) { + while (freq[i] > 0) { + if (idx >= s.size()) { + idx = 1; + } + res[idx] = 'a' + i; + idx += 2; + freq[i]--; + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {string} + */ + reorganizeString(s) { + const freq = new Array(26).fill(0); + for (const char of s) { + freq[char.charCodeAt(0) - 'a'.charCodeAt(0)]++; + } + + let maxIdx = 0; + for (let i = 1; i < 26; i++) { + if (freq[i] > freq[maxIdx]) { + maxIdx = i; + } + } + + const maxFreq = freq[maxIdx]; + if (maxFreq > Math.floor((s.length + 1) / 2)) { + return ''; + } + + const res = new Array(s.length).fill(''); + let idx = 0; + const maxChar = String.fromCharCode(maxIdx + 'a'.charCodeAt(0)); + + while (freq[maxIdx] > 0) { + res[idx] = maxChar; + idx += 2; + freq[maxIdx]--; + } + + for (let i = 0; i < 26; i++) { + while (freq[i] > 0) { + if (idx >= s.length) { + idx = 1; + } + res[idx] = String.fromCharCode(i + 'a'.charCodeAt(0)); + idx += 2; + freq[i]--; + } + } + + return res.join(''); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: + * $O(1)$ extra space, since we have at most $26$ different characters. + * $O(n)$ space for the output string. \ No newline at end of file diff --git a/articles/roman-to-integer.md b/articles/roman-to-integer.md new file mode 100644 index 000000000..b21b20e18 --- /dev/null +++ b/articles/roman-to-integer.md @@ -0,0 +1,95 @@ +## 1. Hash Map + +::tabs-start + +```python +class Solution: + def romanToInt(self, s: str) -> int: + roman = { + "I": 1, "V": 5, "X": 10, + "L": 50, "C": 100, "D": 500, "M": 1000 + } + res = 0 + for i in range(len(s)): + if i + 1 < len(s) and roman[s[i]] < roman[s[i + 1]]: + res -= roman[s[i]] + else: + res += roman[s[i]] + return res +``` + +```java +public class Solution { + public int romanToInt(String s) { + Map roman = new HashMap<>(); + roman.put('I', 1); roman.put('V', 5); + roman.put('X', 10); roman.put('L', 50); + roman.put('C', 100); roman.put('D', 500); + roman.put('M', 1000); + + int res = 0; + for (int i = 0; i < s.length(); i++) { + if (i + 1 < s.length() && roman.get(s.charAt(i)) < roman.get(s.charAt(i + 1))) { + res -= roman.get(s.charAt(i)); + } else { + res += roman.get(s.charAt(i)); + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int romanToInt(string s) { + unordered_map roman = { + {'I', 1}, {'V', 5}, {'X', 10}, + {'L', 50}, {'C', 100}, {'D', 500}, {'M', 1000} + }; + + int res = 0; + for (int i = 0; i < s.size(); i++) { + if (i + 1 < s.size() && roman[s[i]] < roman[s[i + 1]]) { + res -= roman[s[i]]; + } else { + res += roman[s[i]]; + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {number} + */ + romanToInt(s) { + const roman = { + "I": 1, "V": 5, "X": 10, + "L": 50, "C": 100, "D": 500, "M": 1000 + }; + + let res = 0; + for (let i = 0; i < s.length; i++) { + if (i + 1 < s.length && roman[s[i]] < roman[s[i + 1]]) { + res -= roman[s[i]]; + } else { + res += roman[s[i]]; + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ since we have $7$ characters in the hash map. \ No newline at end of file diff --git a/articles/stone-game-iii.md b/articles/stone-game-iii.md new file mode 100644 index 000000000..e95a8e508 --- /dev/null +++ b/articles/stone-game-iii.md @@ -0,0 +1,491 @@ +## 1. Dynamic Programming (Top-Down) - I + +::tabs-start + +```python +class Solution: + def stoneGameIII(self, stoneValue: List[int]) -> str: + n = len(stoneValue) + dp = [[None] * 2 for _ in range(n)] + + def dfs(i, alice): + if i >= n: + return 0 + if dp[i][alice] is not None: + return dp[i][alice] + + res = float("-inf") if alice == 1 else float("inf") + score = 0 + for j in range(i, min(i + 3, n)): + if alice == 1: + score += stoneValue[j] + res = max(res, score + dfs(j + 1, 0)) + else: + score -= stoneValue[j] + res = min(res, score + dfs(j + 1, 1)) + + dp[i][alice] = res + return res + + result = dfs(0, 1) + if result == 0: + return "Tie" + return "Alice" if result > 0 else "Bob" +``` + +```java +public class Solution { + private Integer[][] dp; + private int n; + + public String stoneGameIII(int[] stoneValue) { + n = stoneValue.length; + dp = new Integer[n][2]; + + int result = dfs(0, 1, stoneValue); + if (result == 0) return "Tie"; + return result > 0 ? "Alice" : "Bob"; + } + + private int dfs(int i, int alice, int[] stoneValue) { + if (i >= n) return 0; + if (dp[i][alice] != null) return dp[i][alice]; + + int res = alice == 1 ? Integer.MIN_VALUE : Integer.MAX_VALUE; + int score = 0; + for (int j = i; j < Math.min(i + 3, n); j++) { + if (alice == 1) { + score += stoneValue[j]; + res = Math.max(res, score + dfs(j + 1, 0, stoneValue)); + } else { + score -= stoneValue[j]; + res = Math.min(res, score + dfs(j + 1, 1, stoneValue)); + } + } + + dp[i][alice] = res; + return res; + } +} +``` + +```cpp +class Solution { + vector> dp; + int n; + +public: + string stoneGameIII(vector& stoneValue) { + n = stoneValue.size(); + dp.assign(n, vector(2, INT_MIN)); + + int result = dfs(0, 1, stoneValue); + if (result == 0) return "Tie"; + return result > 0 ? "Alice" : "Bob"; + } + +private: + int dfs(int i, int alice, vector& stoneValue) { + if (i >= n) return 0; + if (dp[i][alice] != INT_MIN) return dp[i][alice]; + + int res = alice == 1 ? INT_MIN : INT_MAX; + int score = 0; + for (int j = i; j < min(i + 3, n); j++) { + if (alice == 1) { + score += stoneValue[j]; + res = max(res, score + dfs(j + 1, 0, stoneValue)); + } else { + score -= stoneValue[j]; + res = min(res, score + dfs(j + 1, 1, stoneValue)); + } + } + + dp[i][alice] = res; + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} stoneValue + * @return {string} + */ + stoneGameIII(stoneValue) { + const n = stoneValue.length; + const dp = Array.from({ length: n }, () => [null, null]); + + const dfs = (i, alice) => { + if (i >= n) return 0; + if (dp[i][alice] !== null) return dp[i][alice]; + + let res = alice === 1 ? -Infinity : Infinity; + let score = 0; + for (let j = i; j < Math.min(i + 3, n); j++) { + if (alice === 1) { + score += stoneValue[j]; + res = Math.max(res, score + dfs(j + 1, 0)); + } else { + score -= stoneValue[j]; + res = Math.min(res, score + dfs(j + 1, 1)); + } + } + + dp[i][alice] = res; + return res; + }; + + const result = dfs(0, 1); + if (result === 0) return "Tie"; + return result > 0 ? "Alice" : "Bob"; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Dynamic Programming (Top-Down) - II + +::tabs-start + +```python +class Solution: + def stoneGameIII(self, stoneValue: List[int]) -> str: + n = len(stoneValue) + dp = {} + + def dfs(i): + if i >= n: + return 0 + if i in dp: + return dp[i] + + res, total = float("-inf"), 0 + for j in range(i, min(i + 3, n)): + total += stoneValue[j] + res = max(res, total - dfs(j + 1)) + + dp[i] = res + return res + + result = dfs(0) + if result == 0: + return "Tie" + return "Alice" if result > 0 else "Bob" +``` + +```java +public class Solution { + private int[] dp; + private int n; + + public String stoneGameIII(int[] stoneValue) { + this.n = stoneValue.length; + this.dp = new int[n]; + Arrays.fill(dp, Integer.MIN_VALUE); + + int result = dfs(0, stoneValue); + if (result == 0) return "Tie"; + return result > 0 ? "Alice" : "Bob"; + } + + private int dfs(int i, int[] stoneValue) { + if (i >= n) return 0; + if (dp[i] != Integer.MIN_VALUE) return dp[i]; + + int res = Integer.MIN_VALUE, total = 0; + for (int j = i; j < Math.min(i + 3, n); j++) { + total += stoneValue[j]; + res = Math.max(res, total - dfs(j + 1, stoneValue)); + } + + dp[i] = res; + return res; + } +} +``` + +```cpp +class Solution { +public: + string stoneGameIII(vector& stoneValue) { + n = stoneValue.size(); + dp.assign(n, INT_MIN); + + int result = dfs(0, stoneValue); + if (result == 0) return "Tie"; + return result > 0 ? "Alice" : "Bob"; + } + +private: + vector dp; + int n; + + int dfs(int i, vector& stoneValue) { + if (i >= n) return 0; + if (dp[i] != INT_MIN) return dp[i]; + + int res = INT_MIN, total = 0; + for (int j = i; j < min(i + 3, n); j++) { + total += stoneValue[j]; + res = max(res, total - dfs(j + 1, stoneValue)); + } + + dp[i] = res; + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} stoneValue + * @return {string} + */ + stoneGameIII(stoneValue) { + const n = stoneValue.length; + const dp = new Array(n); + + const dfs = (i) => { + if (i >= n) return 0; + if (dp[i] !== undefined) return dp[i]; + + let res = -Infinity, total = 0; + for (let j = i; j < Math.min(i + 3, n); j++) { + total += stoneValue[j]; + res = Math.max(res, total - dfs(j + 1)); + } + + dp[i] = res; + return res; + }; + + const result = dfs(0); + if (result === 0) return "Tie"; + return result > 0 ? "Alice" : "Bob"; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def stoneGameIII(self, stoneValue: List[int]) -> str: + n = len(stoneValue) + dp = [float("-inf")] * (n + 1) + dp[n] = 0 + + for i in range(n - 1, -1, -1): + total = 0 + for j in range(i, min(i + 3, n)): + total += stoneValue[j] + dp[i] = max(dp[i], total - dp[j + 1]) + + result = dp[0] + if result == 0: + return "Tie" + return "Alice" if result > 0 else "Bob" +``` + +```java +public class Solution { + public String stoneGameIII(int[] stoneValue) { + int n = stoneValue.length; + int[] dp = new int[n + 1]; + Arrays.fill(dp, Integer.MIN_VALUE); + dp[n] = 0; + + for (int i = n - 1; i >= 0; i--) { + int total = 0; + dp[i] = Integer.MIN_VALUE; + for (int j = i; j < Math.min(i + 3, n); j++) { + total += stoneValue[j]; + dp[i] = Math.max(dp[i], total - dp[j + 1]); + } + } + + int result = dp[0]; + if (result == 0) return "Tie"; + return result > 0 ? "Alice" : "Bob"; + } +} +``` + +```cpp +class Solution { +public: + string stoneGameIII(vector& stoneValue) { + int n = stoneValue.size(); + vector dp(n + 1, INT_MIN); + dp[n] = 0; + + for (int i = n - 1; i >= 0; i--) { + int total = 0; + dp[i] = INT_MIN; + for (int j = i; j < min(i + 3, n); j++) { + total += stoneValue[j]; + dp[i] = max(dp[i], total - dp[j + 1]); + } + } + + int result = dp[0]; + if (result == 0) return "Tie"; + return result > 0 ? "Alice" : "Bob"; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} stoneValue + * @return {string} + */ + stoneGameIII(stoneValue) { + const n = stoneValue.length; + const dp = new Array(n + 1).fill(-Infinity); + dp[n] = 0; + + for (let i = n - 1; i >= 0; i--) { + let total = 0; + dp[i] = -Infinity; + for (let j = i; j < Math.min(i + 3, n); j++) { + total += stoneValue[j]; + dp[i] = Math.max(dp[i], total - dp[j + 1]); + } + } + + const result = dp[0]; + if (result === 0) return "Tie"; + return result > 0 ? "Alice" : "Bob"; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 4. Dynamic Programming (Space Optimized) + +::tabs-start + +```python +class Solution: + def stoneGameIII(self, stoneValue: List[int]) -> str: + n = len(stoneValue) + dp = [0] * 4 + + for i in range(n - 1, -1, -1): + total = 0 + dp[i % 4] = float("-inf") + for j in range(i, min(i + 3, n)): + total += stoneValue[j] + dp[i % 4] = max(dp[i % 4], total - dp[(j + 1) % 4]) + + if dp[0] == 0: + return "Tie" + return "Alice" if dp[0] > 0 else "Bob" +``` + +```java +public class Solution { + public String stoneGameIII(int[] stoneValue) { + int n = stoneValue.length; + int[] dp = new int[4]; + + for (int i = n - 1; i >= 0; i--) { + int total = 0; + dp[i % 4] = Integer.MIN_VALUE; + for (int j = i; j < Math.min(i + 3, n); j++) { + total += stoneValue[j]; + dp[i % 4] = Math.max(dp[i % 4], total - dp[(j + 1) % 4]); + } + } + + if (dp[0] == 0) return "Tie"; + return dp[0] > 0 ? "Alice" : "Bob"; + } +} +``` + +```cpp +class Solution { +public: + string stoneGameIII(vector& stoneValue) { + int n = stoneValue.size(); + vector dp(4, 0); + + for (int i = n - 1; i >= 0; --i) { + int total = 0; + dp[i % 4] = INT_MIN; + for (int j = i; j < min(i + 3, n); ++j) { + total += stoneValue[j]; + dp[i % 4] = max(dp[i % 4], total - dp[(j + 1) % 4]); + } + } + + if (dp[0] == 0) return "Tie"; + return dp[0] > 0 ? "Alice" : "Bob"; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} stoneValue + * @return {string} + */ + stoneGameIII(stoneValue) { + const n = stoneValue.length; + const dp = new Array(4).fill(0); + + for (let i = n - 1; i >= 0; i--) { + let total = 0; + dp[i % 4] = -Infinity; + for (let j = i; j < Math.min(i + 3, n); j++) { + total += stoneValue[j]; + dp[i % 4] = Math.max(dp[i % 4], total - dp[(j + 1) % 4]); + } + } + + if (dp[0] === 0) return "Tie"; + return dp[0] > 0 ? "Alice" : "Bob"; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. \ No newline at end of file diff --git a/hints/cheapest-flight-path.md b/hints/cheapest-flight-path.md index 85fcd7ec4..b42275442 100644 --- a/hints/cheapest-flight-path.md +++ b/hints/cheapest-flight-path.md @@ -18,7 +18,7 @@
Hint 2

- We can use the Bellman-Ford algorithm. Initialize a prices array of size n with Infinity, setting prices[source] = 0. THese values describe the cost to reach a city from the source city. Iterate (k + 1) times (stops are 0-indexed), updating the cost to each city by extending paths from cities with valid costs. We only update the cost for a city if it is less than the previous cost. How would you implement this? + We can use the Bellman-Ford algorithm. Initialize a prices array of size n with Infinity, setting prices[source] = 0. These values describe the cost to reach a city from the source city. Iterate (k + 1) times (stops are 0-indexed), updating the cost to each city by extending paths from cities with valid costs. We only update the cost for a city if it is less than the previous cost. How would you implement this?

From 3891fddf35c4a9ce53eaf63909d222aa6e2cda5e Mon Sep 17 00:00:00 2001 From: Sri Hari <94112314+Srihari2222@users.noreply.github.com> Date: Thu, 9 Jan 2025 03:03:15 +0530 Subject: [PATCH 26/45] Batch-5/Neetcode-250/Added-articles (#3799) --- articles/build-a-matrix-with-conditions.md | 526 +++++++ articles/car-pooling.md | 469 ++++++ ...critical-edges-in-minimum-spanning-tree.md | 17 +- articles/greatest-common-divisor-traversal.md | 1305 +++++++++++++++++ articles/ipo.md | 374 +++++ articles/longest-happy-string.md | 458 ++++++ articles/meeting-rooms-iii.md | 466 ++++++ articles/single-threaded-cpu.md | 471 ++++++ 8 files changed, 4078 insertions(+), 8 deletions(-) create mode 100644 articles/build-a-matrix-with-conditions.md create mode 100644 articles/car-pooling.md create mode 100644 articles/greatest-common-divisor-traversal.md create mode 100644 articles/ipo.md create mode 100644 articles/longest-happy-string.md create mode 100644 articles/meeting-rooms-iii.md create mode 100644 articles/single-threaded-cpu.md diff --git a/articles/build-a-matrix-with-conditions.md b/articles/build-a-matrix-with-conditions.md new file mode 100644 index 000000000..8a4d3f610 --- /dev/null +++ b/articles/build-a-matrix-with-conditions.md @@ -0,0 +1,526 @@ +## 1. Topological Sort (DFS) + +::tabs-start + +```python +class Solution: + def buildMatrix(self, k: int, rowConditions: List[List[int]], colConditions: List[List[int]]) -> List[List[int]]: + def dfs(src, adj, visit, path, order): + if src in path: + return False + if src in visit: + return True + visit.add(src) + path.add(src) + for nei in adj[src]: + if not dfs(nei, adj, visit, path, order): + return False + path.remove(src) + order.append(src) + return True + + def topo_sort(edges): + adj = defaultdict(list) + for src, dst in edges: + adj[src].append(dst) + + visit, path = set(), set() + order = [] + for src in range(1, k + 1): + if src not in visit: + if not dfs(src, adj, visit, path, order): + return [] + return order[::-1] + + row_order = topo_sort(rowConditions) + if not row_order: return [] + + col_order = topo_sort(colConditions) + if not col_order: return [] + + val_to_row = {num: i for i, num in enumerate(row_order)} + val_to_col = {num: i for i, num in enumerate(col_order)} + res = [[0] * k for _ in range(k)] + for num in range(1, k + 1): + r, c = val_to_row[num], val_to_col[num] + res[r][c] = num + + return res +``` + +```java +public class Solution { + private Set visit; + private Set path; + private List order; + + public int[][] buildMatrix(int k, int[][] rowConditions, int[][] colConditions) { + int[] rowOrder = topoSort(k, rowConditions); + if (rowOrder == null) return new int[0][0]; + int[] colOrder = topoSort(k, colConditions); + if (colOrder == null) return new int[0][0]; + + Map valToRow = new HashMap<>(); + for (int i = 0; i < rowOrder.length; i++) { + valToRow.put(rowOrder[i], i); + } + Map valToCol = new HashMap<>(); + for (int i = 0; i < colOrder.length; i++) { + valToCol.put(colOrder[i], i); + } + + int[][] res = new int[k][k]; + for (int num = 1; num <= k; num++) { + int r = valToRow.get(num); + int c = valToCol.get(num); + res[r][c] = num; + } + return res; + } + + private int[] topoSort(int k, int[][] edges) { + Map> adj = new HashMap<>(); + for (int i = 1; i <= k; i++) { + adj.put(i, new ArrayList<>()); + } + for (int[] edge : edges) { + adj.get(edge[0]).add(edge[1]); + } + + visit = new HashSet<>(); + path = new HashSet<>(); + order = new ArrayList<>(); + + for (int i = 1; i <= k; i++) { + if (!visit.contains(i)) { + if (!dfs(i, adj)) { + return null; + } + } + } + + Collections.reverse(order); + return order.stream().mapToInt(i -> i).toArray(); + } + + private boolean dfs(int src, Map> adj) { + if (path.contains(src)) return false; + if (visit.contains(src)) return true; + + visit.add(src); + path.add(src); + for (int nei : adj.get(src)) { + if (!dfs(nei, adj)) { + return false; + } + } + path.remove(src); + order.add(src); + return true; + } +} +``` + +```cpp +class Solution { +public: + vector> buildMatrix(int k, vector>& rowConditions, vector>& colConditions) { + vector rowOrder = topoSort(k, rowConditions); + if (rowOrder.empty()) return {}; + vector colOrder = topoSort(k, colConditions); + if (colOrder.empty()) return {}; + + unordered_map valToRow, valToCol; + for (int i = 0; i < rowOrder.size(); i++) { + valToRow[rowOrder[i]] = i; + } + for (int i = 0; i < colOrder.size(); i++) { + valToCol[colOrder[i]] = i; + } + + vector> res(k, vector(k, 0)); + for (int num = 1; num <= k; num++) { + int r = valToRow[num]; + int c = valToCol[num]; + res[r][c] = num; + } + return res; + } + +private: + unordered_set visit; + unordered_set path; + vector order; + + vector topoSort(int k, vector>& edges) { + unordered_map> adj; + for (int i = 1; i <= k; i++) { + adj[i] = {}; + } + for (auto& edge : edges) { + adj[edge[0]].push_back(edge[1]); + } + + visit.clear(); + path.clear(); + order.clear(); + for (int i = 1; i <= k; i++) { + if (visit.find(i) == visit.end()) { + if (!dfs(i, adj)) { + return {}; + } + } + } + + reverse(order.begin(), order.end()); + return order; + } + + bool dfs(int src, unordered_map>& adj) { + if (path.find(src) != path.end()) return false; + if (visit.find(src) != visit.end()) return true; + + visit.insert(src); + path.insert(src); + for (int nei : adj[src]) { + if (!dfs(nei, adj)) { + return false; + } + } + path.erase(src); + order.push_back(src); + return true; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} k + * @param {number[][]} rowConditions + * @param {number[][]} colConditions + * @return {number[][]} + */ + buildMatrix(k, rowConditions, colConditions) { + const rowOrder = this.topoSort(k, rowConditions); + if (!rowOrder) return []; + const colOrder = this.topoSort(k, colConditions); + if (!colOrder) return []; + + const valToRow = {}; + rowOrder.forEach((num, i) => { + valToRow[num] = i; + }); + + const valToCol = {}; + colOrder.forEach((num, i) => { + valToCol[num] = i; + }); + + const res = Array.from({ length: k }, () => Array(k).fill(0)); + for (let num = 1; num <= k; num++) { + const r = valToRow[num]; + const c = valToCol[num]; + res[r][c] = num; + } + return res; + } + + /** + * @param {number} k + * @param {number[][]} edges + * @return {number[]} + */ + topoSort(k, edges) { + const adj = Array.from({ length: k + 1 }, () => []); + edges.forEach(([src, dst]) => { + adj[src].push(dst); + }); + + const visit = new Set(); + const path = new Set(); + const order = []; + + const dfs = (src) => { + if (path.has(src)) return false; + if (visit.has(src)) return true; + + visit.add(src); + path.add(src); + for (const nei of adj[src]) { + if (!dfs(nei)) { + return false; + } + } + path.delete(src); + order.push(src); + return true; + }; + + for (let src = 1; src <= k; src++) { + if (!visit.has(src)) { + if (!dfs(src)) { + return null; + } + } + } + return order.reverse(); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(k ^ 2 + n + m)$ +* Space complexity: + * $O(k + n + m)$ extra space. + * $O(k ^ 2)$ space for the output matrix. + +> Where $n$ is the size of the array $rowConditions$, $m$ is the size of the array $colConditions$, and $k$ is the size of the output matrix. + +--- + +## 2. Topological Sort (Kahn's Algorithm) + +::tabs-start + +```python +class Solution: + def buildMatrix(self, k: int, rowConditions: List[List[int]], colConditions: List[List[int]]) -> List[List[int]]: + def topo_sort(edges): + indegree = [0] * (k + 1) + adj = [[] for _ in range(k + 1)] + for u, v in edges: + adj[u].append(v) + indegree[v] += 1 + + order = [] + q = deque() + for i in range(1, k + 1): + if not indegree[i]: + q.append(i) + + while q: + node = q.popleft() + order.append(node) + for nei in adj[node]: + indegree[nei] -= 1 + if not indegree[nei]: + q.append(nei) + + return order + + row_order = topo_sort(rowConditions) + if len(row_order) != k: return [] + + col_order = topo_sort(colConditions) + if len(col_order) != k: return [] + + res = [[0] * k for _ in range(k)] + colIndex = [0] * (k + 1) + for i in range(k): + colIndex[col_order[i]] = i + + for i in range(k): + res[i][colIndex[row_order[i]]] = row_order[i] + return res +``` + +```java +public class Solution { + public int[][] buildMatrix(int k, int[][] rowConditions, int[][] colConditions) { + int[] rowOrder = topoSort(k, rowConditions); + if (rowOrder.length != k) return new int[0][0]; + + int[] colOrder = topoSort(k, colConditions); + if (colOrder.length != k) return new int[0][0]; + + int[][] res = new int[k][k]; + int[] colIndex = new int[k + 1]; + for (int i = 0; i < k; i++) { + colIndex[colOrder[i]] = i; + } + + for (int i = 0; i < k; i++) { + res[i][colIndex[rowOrder[i]]] = rowOrder[i]; + } + + return res; + } + + private int[] topoSort(int k, int[][] edges) { + int[] indegree = new int[k + 1]; + List> adj = new ArrayList<>(); + for (int i = 0; i <= k; i++) { + adj.add(new ArrayList<>()); + } + + for (int[] edge : edges) { + adj.get(edge[0]).add(edge[1]); + indegree[edge[1]]++; + } + + Queue queue = new LinkedList<>(); + int[] order = new int[k]; + int idx = 0; + for (int i = 1; i <= k; i++) { + if (indegree[i] == 0) { + queue.offer(i); + } + } + + while (!queue.isEmpty()) { + int node = queue.poll(); + order[idx++] = node; + for (int nei : adj.get(node)) { + indegree[nei]--; + if (indegree[nei] == 0) { + queue.offer(nei); + } + } + } + + if (idx != k) return new int[0]; + return order; + } +} +``` + +```cpp +class Solution { +public: + vector> buildMatrix(int k, vector>& rowConditions, vector>& colConditions) { + vector rowOrder = topoSort(k, rowConditions); + if (rowOrder.size() != k) return {}; + + vector colOrder = topoSort(k, colConditions); + if (colOrder.size() != k) return {}; + + vector> res(k, vector(k, 0)); + vector colIndex(k + 1); + for (int i = 0; i < k; i++) { + colIndex[colOrder[i]] = i; + } + for (int i = 0; i < k; i++) { + res[i][colIndex[rowOrder[i]]] = rowOrder[i]; + } + return res; + } + +private: + vector topoSort(int k, vector>& edges) { + vector indegree(k + 1, 0); + vector> adj(k + 1); + for (const auto& edge : edges) { + adj[edge[0]].push_back(edge[1]); + indegree[edge[1]]++; + } + + queue q; + vector order; + for (int i = 1; i <= k; i++) { + if (indegree[i] == 0) { + q.push(i); + } + } + + while (!q.empty()) { + int node = q.front(); + q.pop(); + order.push_back(node); + + for (int nei : adj[node]) { + indegree[nei]--; + if (indegree[nei] == 0) { + q.push(nei); + } + } + } + + if (order.size() != k) return {}; + return order; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} k + * @param {number[][]} rowConditions + * @param {number[][]} colConditions + * @return {number[][]} + */ + buildMatrix(k, rowConditions, colConditions) { + const rowOrder = this.topoSort(k, rowConditions); + if (rowOrder.length !== k) return []; + + const colOrder = this.topoSort(k, colConditions); + if (colOrder.length !== k) return []; + + const res = Array.from({ length: k }, () => Array(k).fill(0)); + const colIndex = Array(k + 1).fill(0); + + for (let i = 0; i < k; i++) { + colIndex[colOrder[i]] = i; + } + + for (let i = 0; i < k; i++) { + res[i][colIndex[rowOrder[i]]] = rowOrder[i]; + } + + return res; + } + + /** + * @param {number} k + * @param {number[][]} edges + * @return {number[]} + */ + topoSort(k, edges) { + const indegree = Array(k + 1).fill(0); + const adj = Array.from({ length: k + 1 }, () => []); + + for (const [u, v] of edges) { + adj[u].push(v); + indegree[v]++; + } + + const queue = []; + const order = []; + + for (let i = 1; i <= k; i++) { + if (indegree[i] === 0) { + queue.push(i); + } + } + + while (queue.length) { + const node = queue.shift(); + order.push(node); + for (const nei of adj[node]) { + indegree[nei]--; + if (indegree[nei] === 0) { + queue.push(nei); + } + } + } + + return order.length === k ? order : []; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(k ^ 2 + n + m)$ +* Space complexity: + * $O(k + n + m)$ extra space. + * $O(k ^ 2)$ space for the output matrix. + +> Where $n$ is the size of the array $rowConditions$, $m$ is the size of the array $colConditions$, and $k$ is the size of the output matrix. \ No newline at end of file diff --git a/articles/car-pooling.md b/articles/car-pooling.md new file mode 100644 index 000000000..3eac0695d --- /dev/null +++ b/articles/car-pooling.md @@ -0,0 +1,469 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def carPooling(self, trips: List[List[int]], capacity: int) -> bool: + trips.sort(key=lambda x: x[1]) + + for i in range(len(trips)): + curPass = trips[i][0] + for j in range(i): + if trips[j][2] > trips[i][1]: + curPass += trips[j][0] + if curPass > capacity: + return False + + return True +``` + +```java +public class Solution { + public boolean carPooling(int[][] trips, int capacity) { + Arrays.sort(trips, (a, b) -> Integer.compare(a[1], b[1])); + + for (int i = 0; i < trips.length; i++) { + int curPass = trips[i][0]; + for (int j = 0; j < i; j++) { + if (trips[j][2] > trips[i][1]) { + curPass += trips[j][0]; + } + } + if (curPass > capacity) { + return false; + } + } + + return true; + } +} +``` + +```cpp +class Solution { +public: + bool carPooling(vector>& trips, int capacity) { + sort(trips.begin(), trips.end(), [](const vector& a, const vector& b) { + return a[1] < b[1]; + }); + + for (int i = 0; i < trips.size(); i++) { + int curPass = trips[i][0]; + for (int j = 0; j < i; j++) { + if (trips[j][2] > trips[i][1]) { + curPass += trips[j][0]; + } + } + if (curPass > capacity) { + return false; + } + } + + return true; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} trips + * @param {number} capacity + * @return {boolean} + */ + carPooling(trips, capacity) { + trips.sort((a, b) => a[1] - b[1]); + + for (let i = 0; i < trips.length; i++) { + let curPass = trips[i][0]; + for (let j = 0; j < i; j++) { + if (trips[j][2] > trips[i][1]) { + curPass += trips[j][0]; + } + } + if (curPass > capacity) { + return false; + } + } + + return true; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(1)$ or $O(n)$ depending on the sorting algorithm. + +--- + +## 2. Min-Heap + +::tabs-start + +```python +class Solution: + def carPooling(self, trips: List[List[int]], capacity: int) -> bool: + trips.sort(key=lambda t: t[1]) + + minHeap = [] # pair of [end, numPassengers] + curPass = 0 + + for numPass, start, end in trips: + while minHeap and minHeap[0][0] <= start: + curPass -= heapq.heappop(minHeap)[1] + + curPass += numPass + if curPass > capacity: + return False + + heapq.heappush(minHeap, [end, numPass]) + + return True +``` + +```java +public class Solution { + public boolean carPooling(int[][] trips, int capacity) { + Arrays.sort(trips, Comparator.comparingInt(a -> a[1])); + + PriorityQueue minHeap = new PriorityQueue<>(Comparator.comparingInt(a -> a[0])); // [end, numPassengers] + int curPass = 0; + + for (int[] trip : trips) { + int numPass = trip[0], start = trip[1], end = trip[2]; + + while (!minHeap.isEmpty() && minHeap.peek()[0] <= start) { + curPass -= minHeap.poll()[1]; + } + + curPass += numPass; + if (curPass > capacity) { + return false; + } + + minHeap.offer(new int[]{end, numPass}); + } + + return true; + } +} +``` + +```cpp +class Solution { +public: + bool carPooling(vector>& trips, int capacity) { + sort(trips.begin(), trips.end(), [](const vector& a, const vector& b) { + return a[1] < b[1]; + }); + + priority_queue, vector>, greater<>> minHeap; // [end, numPassengers] + int curPass = 0; + + for (const auto& trip : trips) { + int numPass = trip[0], start = trip[1], end = trip[2]; + + while (!minHeap.empty() && minHeap.top().first <= start) { + curPass -= minHeap.top().second; + minHeap.pop(); + } + + curPass += numPass; + if (curPass > capacity) { + return false; + } + + minHeap.emplace(end, numPass); + } + + return true; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} trips + * @param {number} capacity + * @return {boolean} + */ + carPooling(trips, capacity) { + trips.sort((a, b) => a[1] - b[1]); + + const minHeap = new MinPriorityQueue({ priority: x => x[0] }); // [end, numPassengers] + let curPass = 0; + + for (const [numPass, start, end] of trips) { + while (!minHeap.isEmpty() && minHeap.front().element[0] <= start) { + curPass -= minHeap.dequeue().element[1]; + } + + curPass += numPass; + if (curPass > capacity) { + return false; + } + + minHeap.enqueue([end, numPass]); + } + + return true; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Line Sweep - I + +::tabs-start + +```python +class Solution: + def carPooling(self, trips: List[List[int]], capacity: int) -> bool: + points = [] + for passengers, start, end in trips: + points.append([start, passengers]) + points.append([end, -passengers]) + + points.sort() + curPass = 0 + for point, passengers in points: + curPass += passengers + if curPass > capacity: + return False + + return True +``` + +```java +public class Solution { + public boolean carPooling(int[][] trips, int capacity) { + List points = new ArrayList<>(); + for (int[] trip : trips) { + int passengers = trip[0], start = trip[1], end = trip[2]; + points.add(new int[]{start, passengers}); + points.add(new int[]{end, -passengers}); + } + + points.sort((a, b) -> a[0] == b[0] ? Integer.compare(a[1], b[1]) : Integer.compare(a[0], b[0])); + + int curPass = 0; + for (int[] point : points) { + curPass += point[1]; + if (curPass > capacity) { + return false; + } + } + + return true; + } +} +``` + +```cpp +class Solution { +public: + bool carPooling(vector>& trips, int capacity) { + vector> points; + for (const auto& trip : trips) { + int passengers = trip[0], start = trip[1], end = trip[2]; + points.emplace_back(start, passengers); + points.emplace_back(end, -passengers); + } + + sort(points.begin(), points.end(), [](const pair& a, const pair& b) { + return a.first == b.first ? a.second < b.second : a.first < b.first; + }); + + int curPass = 0; + for (const auto& point : points) { + curPass += point.second; + if (curPass > capacity) { + return false; + } + } + + return true; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} trips + * @param {number} capacity + * @return {boolean} + */ + carPooling(trips, capacity) { + const points = []; + for (const [passengers, start, end] of trips) { + points.push([start, passengers]); + points.push([end, -passengers]); + } + + points.sort((a, b) => a[0] === b[0] ? a[1] - b[1] : a[0] - b[0]); + + let curPass = 0; + for (const [point, passengers] of points) { + curPass += passengers; + if (curPass > capacity) { + return false; + } + } + + return true; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ + +--- + +## 4. Line Sweep - II + +::tabs-start + +```python +class Solution: + def carPooling(self, trips: List[List[int]], capacity: int) -> bool: + L, R = float("inf"), float("-inf") + for _, start, end in trips: + L = min(L, start) + R = max(R, end) + + N = R - L + 1 + passChange = [0] * (N + 1) + for passengers, start, end in trips: + passChange[start - L] += passengers + passChange[end - L] -= passengers + + curPass = 0 + for change in passChange: + curPass += change + if curPass > capacity: + return False + + return True +``` + +```java +public class Solution { + public boolean carPooling(int[][] trips, int capacity) { + int L = Integer.MAX_VALUE, R = Integer.MIN_VALUE; + for (int[] trip : trips) { + L = Math.min(L, trip[1]); + R = Math.max(R, trip[2]); + } + + int N = R - L + 1; + int[] passChange = new int[N + 1]; + for (int[] trip : trips) { + passChange[trip[1] - L] += trip[0]; + passChange[trip[2] - L] -= trip[0]; + } + + int curPass = 0; + for (int change : passChange) { + curPass += change; + if (curPass > capacity) { + return false; + } + } + + return true; + } +} +``` + +```cpp +class Solution { +public: + bool carPooling(vector>& trips, int capacity) { + int L = INT_MAX, R = INT_MIN; + for (const auto& trip : trips) { + L = min(L, trip[1]); + R = max(R, trip[2]); + } + + int N = R - L + 1; + vector passChange(N + 1, 0); + for (const auto& trip : trips) { + passChange[trip[1] - L] += trip[0]; + passChange[trip[2] - L] -= trip[0]; + } + + int curPass = 0; + for (int change : passChange) { + curPass += change; + if (curPass > capacity) { + return false; + } + } + + return true; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} trips + * @param {number} capacity + * @return {boolean} + */ + carPooling(trips, capacity) { + let L = Infinity, R = -Infinity; + for (const [passengers, start, end] of trips) { + L = Math.min(L, start); + R = Math.max(R, end); + } + + const N = R - L + 1; + const passChange = Array(N + 1).fill(0); + for (const [passengers, start, end] of trips) { + passChange[start - L] += passengers; + passChange[end - L] -= passengers; + } + + let curPass = 0; + for (const change of passChange) { + curPass += change; + if (curPass > capacity) { + return false; + } + } + + return true; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n + N)$ +* Space complexity: $O(N)$ + +> Where $n$ is the size of the array $trips$ and $N$ is the difference between the rightmost location and the leftmost location. \ No newline at end of file diff --git a/articles/find-critical-and-pseudo-critical-edges-in-minimum-spanning-tree.md b/articles/find-critical-and-pseudo-critical-edges-in-minimum-spanning-tree.md index dd184b948..b20fb8a09 100644 --- a/articles/find-critical-and-pseudo-critical-edges-in-minimum-spanning-tree.md +++ b/articles/find-critical-and-pseudo-critical-edges-in-minimum-spanning-tree.md @@ -578,16 +578,16 @@ class UnionFind { * @param {number} n */ constructor(n) { - this.n = n; this.Parent = Array.from({ length: n + 1 }, (_, i) => i); this.Size = Array(n + 1).fill(1); + this.n = n; } /** - * @param {number} v1 + * @param {number} node * @return {number} */ - find(v1) { + find(node) { if (this.Parent[node] !== node) { this.Parent[node] = this.find(this.Parent[node]); } @@ -595,11 +595,11 @@ class UnionFind { } /** - * @param {number} v1 - * @param {number} v2 + * @param {number} u + * @param {number} v * @return {boolean} */ - union(v1, v2) { + union(u, v) { let pu = this.find(u); let pv = this.find(v); if (pu === pv) return false; @@ -612,6 +612,9 @@ class UnionFind { return true; } + /** + * @return {number} + */ isConnected() { return this.n === 1; } @@ -960,8 +963,6 @@ class Solution: if edges[i][2] == edges[ind][2]: pseudo.add(i) pseudo.add(ind) - mstEdge.add(i) - mstEdge.add(ind) return [list(mstEdge - pseudo), list(pseudo)] ``` diff --git a/articles/greatest-common-divisor-traversal.md b/articles/greatest-common-divisor-traversal.md new file mode 100644 index 000000000..79c11cf70 --- /dev/null +++ b/articles/greatest-common-divisor-traversal.md @@ -0,0 +1,1305 @@ +## 1. Brute Force (DFS) + +::tabs-start + +```python +class Solution: + def canTraverseAllPairs(self, nums: List[int]) -> bool: + n = len(nums) + visit = [False] * n + adj = [[] for _ in range(n)] + for i in range(n): + for j in range(i + 1, n): + if gcd(nums[i], nums[j]) > 1: + adj[i].append(j) + adj[j].append(i) + + def dfs(node): + visit[node] = True + for nei in adj[node]: + if not visit[nei]: + dfs(nei) + + dfs(0) + for node in visit: + if not node: + return False + return True +``` + +```java +public class Solution { + public boolean canTraverseAllPairs(int[] nums) { + int n = nums.length; + boolean[] visit = new boolean[n]; + List> adj = new ArrayList<>(); + for (int i = 0; i < n; i++) { + adj.add(new ArrayList<>()); + } + + for (int i = 0; i < n; i++) { + for (int j = i + 1; j < n; j++) { + if (gcd(nums[i], nums[j]) > 1) { + adj.get(i).add(j); + adj.get(j).add(i); + } + } + } + + dfs(0, adj, visit); + for (boolean node : visit) { + if (!node) { + return false; + } + } + return true; + } + + private void dfs(int node, List> adj, boolean[] visit) { + visit[node] = true; + for (int nei : adj.get(node)) { + if (!visit[nei]) { + dfs(nei, adj, visit); + } + } + } + + private int gcd(int a, int b) { + return b == 0 ? a : gcd(b, a % b); + } +} +``` + +```cpp +class Solution { +public: + bool canTraverseAllPairs(vector& nums) { + int n = nums.size(); + vector visit(n, false); + vector> adj(n); + for (int i = 0; i < n; i++) { + for (int j = i + 1; j < n; j++) { + if (__gcd(nums[i], nums[j]) > 1) { + adj[i].push_back(j); + adj[j].push_back(i); + } + } + } + + dfs(0, adj, visit); + for (bool node : visit) { + if (!node) { + return false; + } + } + return true; + } + +private: + void dfs(int node, vector>& adj, vector& visit) { + visit[node] = true; + for (int& nei : adj[node]) { + if (!visit[nei]) { + dfs(nei, adj, visit); + } + } + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {boolean} + */ + canTraverseAllPairs(nums) { + const n = nums.length; + const visit = new Array(n).fill(false); + const adj = Array.from({ length: n }, () => []); + + const gcd = (a, b) => (b === 0 ? a : gcd(b, a % b)); + + for (let i = 0; i < n; i++) { + for (let j = i + 1; j < n; j++) { + if (gcd(nums[i], nums[j]) > 1) { + adj[i].push(j); + adj[j].push(i); + } + } + } + + const dfs = (node) => { + visit[node] = true; + for (const nei of adj[node]) { + if (!visit[nei]) { + dfs(nei); + } + } + }; + + dfs(0); + return visit.every((node) => node); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2 \log n)$ +* Space complexity: $O(n ^ 2)$ + +--- + +## 2. Disjoint Set Union + +::tabs-start + +```python +class UnionFind: + def __init__(self, n): + self.n = n + self.Parent = list(range(n + 1)) + self.Size = [1] * (n + 1) + + def find(self, node): + if self.Parent[node] != node: + self.Parent[node] = self.find(self.Parent[node]) + return self.Parent[node] + + def union(self, u, v): + pu = self.find(u) + pv = self.find(v) + if pu == pv: + return False + self.n -= 1 + if self.Size[pu] < self.Size[pv]: + pu, pv = pv, pu + self.Size[pu] += self.Size[pv] + self.Parent[pv] = pu + return True + + def isConnected(self): + return self.n == 1 + +class Solution: + def canTraverseAllPairs(self, nums: List[int]) -> bool: + uf = UnionFind(len(nums)) + + factor_index = {} # f -> index of value with factor f + for i, num in enumerate(nums): + f = 2 + while f * f <= n: + if n % f == 0: + if f in factor_index: + uf.union(i, factor_index[f]) + else: + factor_index[f] = i + while n % f == 0: + n = n // f + f += 1 + if n > 1: + if n in factor_index: + uf.union(i, factor_index[n]) + else: + factor_index[n] = i + + return uf.isConnected() +``` + +```java +class UnionFind { + private int n; + private int[] Parent; + private int[] Size; + + public UnionFind(int n) { + this.n = n; + this.Parent = new int[n + 1]; + this.Size = new int[n + 1]; + for (int i = 0; i <= n; i++) { + this.Parent[i] = i; + this.Size[i] = 1; + } + } + + public int find(int node) { + if (Parent[node] != node) { + Parent[node] = find(Parent[node]); + } + return Parent[node]; + } + + public boolean union(int u, int v) { + int pu = find(u); + int pv = find(v); + if (pu == pv) return false; + n--; + if (Size[pu] < Size[pv]) { + int temp = pu; + pu = pv; + pv = temp; + } + Size[pu] += Size[pv]; + Parent[pv] = pu; + return true; + } + + public boolean isConnected() { + return n == 1; + } +} + +public class Solution { + public boolean canTraverseAllPairs(int[] nums) { + int n = nums.length; + UnionFind uf = new UnionFind(n); + Map factorIndex = new HashMap<>(); + + for (int i = 0; i < n; i++) { + int num = nums[i]; + int f = 2; + while (f * f <= num) { + if (num % f == 0) { + if (factorIndex.containsKey(f)) { + uf.union(i, factorIndex.get(f)); + } else { + factorIndex.put(f, i); + } + while (num % f == 0) { + num /= f; + } + } + f++; + } + if (num > 1) { + if (factorIndex.containsKey(num)) { + uf.union(i, factorIndex.get(num)); + } else { + factorIndex.put(num, i); + } + } + } + + return uf.isConnected(); + } +} +``` + +```cpp +class UnionFind { +private: + int n; + vector Parent; + vector Size; + +public: + UnionFind(int n) : n(n), Parent(n + 1), Size(n + 1, 1) { + for (int i = 0; i <= n; i++) { + Parent[i] = i; + } + } + + int find(int node) { + if (Parent[node] != node) { + Parent[node] = find(Parent[node]); + } + return Parent[node]; + } + + bool unionNodes(int u, int v) { + int pu = find(u); + int pv = find(v); + if (pu == pv) return false; + n--; + if (Size[pu] < Size[pv]) swap(pu, pv); + Size[pu] += Size[pv]; + Parent[pv] = pu; + return true; + } + + bool isConnected() { + return n == 1; + } +}; + +class Solution { +public: + bool canTraverseAllPairs(vector& nums) { + int n = nums.size(); + UnionFind uf(n); + + unordered_map factor_index; + + for (int i = 0; i < n; i++) { + int num = nums[i]; + int f = 2; + while (f * f <= num) { + if (num % f == 0) { + if (factor_index.count(f)) { + uf.unionNodes(i, factor_index[f]); + } else { + factor_index[f] = i; + } + while (num % f == 0) { + num /= f; + } + } + f++; + } + if (num > 1) { + if (factor_index.count(num)) { + uf.unionNodes(i, factor_index[num]); + } else { + factor_index[num] = i; + } + } + } + + return uf.isConnected(); + } +}; +``` + +```javascript +class UnionFind { + /** + * @constructor + * @param {number} n + */ + constructor(n) { + this.Parent = Array.from({ length: n + 1 }, (_, i) => i); + this.Size = Array(n + 1).fill(1); + this.n = n; + } + + /** + * @param {number} node + * @return {number} + */ + find(node) { + if (this.Parent[node] !== node) { + this.Parent[node] = this.find(this.Parent[node]); + } + return this.Parent[node]; + } + + /** + * @param {number} u + * @param {number} v + * @return {boolean} + */ + union(u, v) { + let pu = this.find(u); + let pv = this.find(v); + if (pu === pv) return false; + this.n--; + if (this.Size[pu] < this.Size[pv]) { + [pu, pv] = [pv, pu]; + } + this.Size[pu] += this.Size[pv]; + this.Parent[pv] = pu; + return true; + } + + /** + * @return {number} + */ + isConnected() { + return this.n === 1; + } +} + +class Solution { + /** + * @param {number[]} nums + * @return {boolean} + */ + canTraverseAllPairs(nums) { + const n = nums.length; + const uf = new UnionFind(n); + const factor_index = new Map(); + + for (let i = 0; i < n; i++) { + let num = nums[i]; + let f = 2; + while (f * f <= num) { + if (num % f === 0) { + if (factor_index.has(f)) { + uf.union(i, factor_index.get(f)); + } else { + factor_index.set(f, i); + } + while (num % f === 0) { + num = Math.floor(num / f); + } + } + f++; + } + if (num > 1) { + if (factor_index.has(num)) { + uf.union(i, factor_index.get(num)); + } else { + factor_index.set(num, i); + } + } + } + + return uf.isConnected(); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m + n\sqrt {m})$ +* Space complexity: $O(n \log m)$ + +> Where $n$ is the size of the array $nums$ and $m$ is the maximum value in the array. + +--- + +## 3. Sieve of Eratosthenes + DSU + +::tabs-start + +```python +class UnionFind: + def __init__(self, n): + self.Parent = list(range(n + 1)) + self.Size = [1] * (n + 1) + + def find(self, node): + if self.Parent[node] != node: + self.Parent[node] = self.find(self.Parent[node]) + return self.Parent[node] + + def union(self, u, v): + pu = self.find(u) + pv = self.find(v) + if pu == pv: + return False + if self.Size[pu] < self.Size[pv]: + pu, pv = pv, pu + self.Size[pu] += self.Size[pv] + self.Parent[pv] = pu + return True + +class Solution: + def canTraverseAllPairs(self, nums: List[int]) -> bool: + N = len(nums) + if N == 1: + return True + if any(num == 1 for num in nums): + return False + + MAX = max(nums) + sieve = [0] * (MAX + 1) + p = 2 + while p * p <= MAX: + if sieve[p] == 0: + for composite in range(p * p, MAX + 1, p): + sieve[composite] = p + p += 1 + + uf = UnionFind(N + MAX + 1) + for i in range(N): + num = nums[i] + if sieve[num] == 0: # num is prime + uf.union(i, N + num) + continue + + while num > 1: + prime = sieve[num] if sieve[num] != 0 else num + uf.union(i, N + prime) + while num % prime == 0: + num //= prime + + root = uf.find(0) + for i in range(1, N): + if uf.find(i) != root: + return False + return True +``` + +```java +class UnionFind { + private int[] Parent; + private int[] Size; + + public UnionFind(int n) { + Parent = new int[n + 1]; + Size = new int[n + 1]; + for (int i = 0; i <= n; i++) { + Parent[i] = i; + Size[i] = 1; + } + } + + public int find(int node) { + if (Parent[node] != node) { + Parent[node] = find(Parent[node]); + } + return Parent[node]; + } + + public boolean union(int u, int v) { + int pu = find(u); + int pv = find(v); + if (pu == pv) { + return false; + } + if (Size[pu] < Size[pv]) { + int temp = pu; + pu = pv; + pv = temp; + } + Size[pu] += Size[pv]; + Parent[pv] = pu; + return true; + } +} + +public class Solution { + public boolean canTraverseAllPairs(int[] nums) { + int N = nums.length; + if (N == 1) { + return true; + } + int MAX = 0; + for (int num : nums) { + MAX = Math.max(MAX, num); + if (num == 1) { + return false; + } + } + + int[] sieve = new int[MAX + 1]; + for (int p = 2; p * p <= MAX; p++) { + if (sieve[p] == 0) { + for (int composite = p * p; composite <= MAX; composite += p) { + sieve[composite] = p; + } + } + } + + UnionFind uf = new UnionFind(N + MAX + 1); + for (int i = 0; i < N; i++) { + int num = nums[i]; + if (sieve[num] == 0) { // num is prime + uf.union(i, N + num); + continue; + } + + while (num > 1) { + int prime = sieve[num] != 0 ? sieve[num] : num; + uf.union(i, N + prime); + while (num % prime == 0) { + num /= prime; + } + } + } + + int root = uf.find(0); + for (int i = 1; i < N; i++) { + if (uf.find(i) != root) { + return false; + } + } + return true; + } +} +``` + +```cpp +class UnionFind { +private: + vector Parent, Size; + +public: + UnionFind(int n) { + Parent.resize(n + 1); + Size.resize(n + 1, 1); + for (int i = 0; i <= n; i++) { + Parent[i] = i; + } + } + + int find(int node) { + if (Parent[node] != node) { + Parent[node] = find(Parent[node]); + } + return Parent[node]; + } + + bool unionSet(int u, int v) { + int pu = find(u); + int pv = find(v); + if (pu == pv) { + return false; + } + if (Size[pu] < Size[pv]) { + swap(pu, pv); + } + Size[pu] += Size[pv]; + Parent[pv] = pu; + return true; + } +}; + +class Solution { +public: + bool canTraverseAllPairs(vector& nums) { + int N = nums.size(); + if (N == 1) { + return true; + } + for (int num : nums) { + if (num == 1) { + return false; + } + } + + int MAX = *max_element(nums.begin(), nums.end()); + vector sieve(MAX + 1, 0); + for (int p = 2; p * p <= MAX; p++) { + if (sieve[p] == 0) { + for (int composite = p * p; composite <= MAX; composite += p) { + sieve[composite] = p; + } + } + } + + UnionFind uf(N + MAX + 1); + for (int i = 0; i < N; i++) { + int num = nums[i]; + if (sieve[num] == 0) { // num is prime + uf.unionSet(i, N + num); + continue; + } + + while (num > 1) { + int prime = sieve[num] != 0 ? sieve[num] : num; + uf.unionSet(i, N + prime); + while (num % prime == 0) { + num /= prime; + } + } + } + + int root = uf.find(0); + for (int i = 1; i < N; i++) { + if (uf.find(i) != root) { + return false; + } + } + return true; + } +}; +``` + +```javascript +class UnionFind { + /** + * @constructor + * @param {number} n + */ + constructor(n) { + this.Parent = Array.from({ length: n + 1 }, (_, i) => i); + this.Size = Array(n + 1).fill(1); + } + + /** + * @param {number} node + * @return {number} + */ + find(node) { + if (this.Parent[node] !== node) { + this.Parent[node] = this.find(this.Parent[node]); + } + return this.Parent[node]; + } + + /** + * @param {number} u + * @param {number} v + * @return {boolean} + */ + union(u, v) { + let pu = this.find(u); + let pv = this.find(v); + if (pu === pv) return false; + if (this.Size[pu] < this.Size[pv]) { + [pu, pv] = [pv, pu]; + } + this.Size[pu] += this.Size[pv]; + this.Parent[pv] = pu; + return true; + } +} + +class Solution { + /** + * @param {number[]} nums + * @return {boolean} + */ + canTraverseAllPairs(nums) { + const N = nums.length; + if (N === 1) return true; + if (nums.includes(1)) return false; + + const MAX = Math.max(...nums); + const sieve = Array(MAX + 1).fill(0); + for (let p = 2; p * p <= MAX; p++) { + if (sieve[p] === 0) { + for (let composite = p * p; composite <= MAX; composite += p) { + sieve[composite] = p; + } + } + } + + const uf = new UnionFind(N + MAX + 1); + for (let i = 0; i < N; i++) { + let num = nums[i]; + if (sieve[num] === 0) { // num is prime + uf.union(i, N + num); + continue; + } + + while (num > 1) { + const prime = sieve[num] !== 0 ? sieve[num] : num; + uf.union(i, N + prime); + while (num % prime === 0) { + num = Math.floor(num / prime); + } + } + } + + const root = uf.find(0); + for (let i = 1; i < N; i++) { + if (uf.find(i) !== root) { + return false; + } + } + return true; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m + n \log m)$ +* Space complexity: $O(n + m)$ + +> Where $n$ is the size of the array $nums$ and $m$ is the maximum value in the array. + +--- + +## 4. Sieve of Eratosthenes + DFS + +::tabs-start + +```python +class Solution: + def canTraverseAllPairs(self, nums: List[int]) -> bool: + N = len(nums) + if N == 1: + return True + if any(num == 1 for num in nums): + return False + + MAX = max(nums) + sieve = [0] * (MAX + 1) + p = 2 + while p * p <= MAX: + if sieve[p] == 0: + for composite in range(p * p, MAX + 1, p): + sieve[composite] = p + p += 1 + + adj = defaultdict(list) + for i in range(N): + num = nums[i] + if sieve[num] == 0: # num is prime + adj[i].append(N + num) + adj[N + num].append(i) + continue + + while num > 1: + prime = sieve[num] if sieve[num] != 0 else num + adj[i].append(N + prime) + adj[N + prime].append(i) + while num % prime == 0: + num //= prime + + visited = set() + def dfs(node): + visited.add(node) + for nei in adj[node]: + if nei not in visited: + dfs(nei) + + dfs(0) + for i in range(N): + if i not in visited: + return False + return True +``` + +```java +public class Solution { + public boolean canTraverseAllPairs(int[] nums) { + int N = nums.length; + if (N == 1) return true; + for (int num : nums) { + if (num == 1) return false; + } + + int MAX = Arrays.stream(nums).max().getAsInt(); + int[] sieve = new int[MAX + 1]; + for (int p = 2; p * p <= MAX; p++) { + if (sieve[p] == 0) { + for (int composite = p * p; composite <= MAX; composite += p) { + sieve[composite] = p; + } + } + } + + Map> adj = new HashMap<>(); + for (int i = 0; i < N; i++) { + int num = nums[i]; + adj.putIfAbsent(i, new ArrayList<>()); + + if (sieve[num] == 0) { + adj.putIfAbsent(N + num, new ArrayList<>()); + adj.get(i).add(N + num); + adj.get(N + num).add(i); + continue; + } + + while (num > 1) { + int prime = (sieve[num] == 0) ? num : sieve[num]; + adj.putIfAbsent(N + prime, new ArrayList<>()); + adj.get(i).add(N + prime); + adj.get(N + prime).add(i); + while (num % prime == 0) num /= prime; + } + } + + Set visited = new HashSet<>(); + dfs(0, adj, visited); + for (int i = 0; i < N; i++) { + if (!visited.contains(i)) return false; + } + return true; + } + + private void dfs(int node, Map> adj, Set visited) { + visited.add(node); + for (int neighbor : adj.get(node)) { + if (!visited.contains(neighbor)) { + dfs(neighbor, adj, visited); + } + } + } +} +``` + +```cpp +class Solution { +public: + bool canTraverseAllPairs(vector& nums) { + int N = nums.size(); + if (N == 1) return true; + if (find(nums.begin(), nums.end(), 1) != nums.end()) return false; + + int MAX = *max_element(nums.begin(), nums.end()); + vector sieve(MAX + 1, 0); + for (int p = 2; p * p <= MAX; p++) { + if (sieve[p] == 0) { + for (int composite = p * p; composite <= MAX; composite += p) { + sieve[composite] = p; + } + } + } + + unordered_map> adj; + for (int i = 0; i < N; i++) { + int num = nums[i]; + if (!adj.count(i)) adj[i] = {}; + + if (sieve[num] == 0) { + adj[N + num].push_back(i); + adj[i].push_back(N + num); + continue; + } + + while (num > 1) { + int prime = (sieve[num] == 0) ? num : sieve[num]; + adj[N + prime].push_back(i); + adj[i].push_back(N + prime); + while (num % prime == 0) num /= prime; + } + } + + unordered_set visited; + dfs(0, adj, visited); + for (int i = 0; i < N; i++) { + if (visited.find(i) == visited.end()) return false; + } + return true; + } + +private: + void dfs(int node, unordered_map>& adj, unordered_set& visited) { + visited.insert(node); + for (int& neighbor : adj[node]) { + if (visited.find(neighbor) == visited.end()) { + dfs(neighbor, adj, visited); + } + } + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {boolean} + */ + canTraverseAllPairs(nums) { + const N = nums.length; + if (N === 1) return true; + if (nums.includes(1)) return false; + + const MAX = Math.max(...nums); + const sieve = new Array(MAX + 1).fill(0); + for (let p = 2; p * p <= MAX; p++) { + if (sieve[p] === 0) { + for (let composite = p * p; composite <= MAX; composite += p) { + sieve[composite] = p; + } + } + } + + const adj = new Map(); + for (let i = 0; i < N; i++) { + if (!adj.has(i)) adj.set(i, []); + let num = nums[i]; + + if (sieve[num] === 0) { + if (!adj.has(N + num)) adj.set(N + num, []); + adj.get(i).push(N + num); + adj.get(N + num).push(i); + continue; + } + + while (num > 1) { + const prime = sieve[num] === 0 ? num : sieve[num]; + if (!adj.has(N + prime)) adj.set(N + prime, []); + adj.get(i).push(N + prime); + adj.get(N + prime).push(i); + while (num % prime === 0) num = Math.floor(num / prime); + } + } + + const visited = new Set(); + const dfs = (node) => { + visited.add(node); + for (const neighbor of adj.get(node) || []) { + if (!visited.has(neighbor)) { + dfs(neighbor); + } + } + }; + + dfs(0); + for (let i = 0; i < N; i++) { + if (!visited.has(i)) return false; + } + return true; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m + n \log m)$ +* Space complexity: $O(n + m)$ + +> Where $n$ is the size of the array $nums$ and $m$ is the maximum value in the array. + +--- + +## 5. Sieve of Eratosthenes + BFS + +::tabs-start + +```python +class Solution: + def canTraverseAllPairs(self, nums: List[int]) -> bool: + N = len(nums) + if N == 1: + return True + if any(num == 1 for num in nums): + return False + + MAX = max(nums) + sieve = [0] * (MAX + 1) + p = 2 + while p * p <= MAX: + if sieve[p] == 0: + for composite in range(p * p, MAX + 1, p): + sieve[composite] = p + p += 1 + + adj = defaultdict(list) + for i in range(N): + num = nums[i] + if sieve[num] == 0: # num is prime + adj[i].append(N + num) + adj[N + num].append(i) + continue + + while num > 1: + prime = sieve[num] if sieve[num] != 0 else num + adj[i].append(N + prime) + adj[N + prime].append(i) + while num % prime == 0: + num //= prime + + visited = set() + queue = deque([0]) + visited.add(0) + while queue: + node = queue.popleft() + for nei in adj[node]: + if nei not in visited: + visited.add(nei) + queue.append(nei) + + for i in range(N): + if i not in visited: + return False + return True +``` + +```java +public class Solution { + public boolean canTraverseAllPairs(int[] nums) { + int N = nums.length; + if (N == 1) return true; + for (int num : nums) { + if (num == 1) return false; + } + + int MAX = Arrays.stream(nums).max().getAsInt(); + int[] sieve = new int[MAX + 1]; + int p = 2; + while (p * p <= MAX) { + if (sieve[p] == 0) { + for (int composite = p * p; composite <= MAX; composite += p) { + sieve[composite] = p; + } + } + p++; + } + + Map> adj = new HashMap<>(); + for (int i = 0; i < N; i++) { + int num = nums[i]; + if (sieve[num] == 0) { // num is prime + adj.computeIfAbsent(i, k -> new ArrayList<>()).add(N + num); + adj.computeIfAbsent(N + num, k -> new ArrayList<>()).add(i); + continue; + } + + while (num > 1) { + int prime = (sieve[num] != 0) ? sieve[num] : num; + adj.computeIfAbsent(i, k -> new ArrayList<>()).add(N + prime); + adj.computeIfAbsent(N + prime, k -> new ArrayList<>()).add(i); + while (num % prime == 0) { + num /= prime; + } + } + } + + Set visited = new HashSet<>(); + Queue queue = new LinkedList<>(); + queue.add(0); + visited.add(0); + + while (!queue.isEmpty()) { + int node = queue.poll(); + for (int nei : adj.getOrDefault(node, new ArrayList<>())) { + if (!visited.contains(nei)) { + visited.add(nei); + queue.add(nei); + } + } + } + + for (int i = 0; i < N; i++) { + if (!visited.contains(i)) { + return false; + } + } + return true; + } +} +``` + +```cpp +class Solution { +public: + bool canTraverseAllPairs(vector& nums) { + int N = nums.size(); + if (N == 1) return true; + if (find(nums.begin(), nums.end(), 1) != nums.end()) return false; + + int MAX = *max_element(nums.begin(), nums.end()); + vector sieve(MAX + 1, 0); + int p = 2; + while (p * p <= MAX) { + if (sieve[p] == 0) { + for (int composite = p * p; composite <= MAX; composite += p) { + sieve[composite] = p; + } + } + p++; + } + + unordered_map> adj; + for (int i = 0; i < N; i++) { + int num = nums[i]; + if (sieve[num] == 0) { // num is prime + adj[i].push_back(N + num); + adj[N + num].push_back(i); + continue; + } + + while (num > 1) { + int prime = (sieve[num] != 0) ? sieve[num] : num; + adj[i].push_back(N + prime); + adj[N + prime].push_back(i); + while (num % prime == 0) { + num /= prime; + } + } + } + + unordered_set visited; + queue q; + q.push(0); + visited.insert(0); + + while (!q.empty()) { + int node = q.front(); + q.pop(); + for (int& nei : adj[node]) { + if (visited.find(nei) == visited.end()) { + visited.insert(nei); + q.push(nei); + } + } + } + + for (int i = 0; i < N; i++) { + if (visited.find(i) == visited.end()) { + return false; + } + } + return true; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {boolean} + */ + canTraverseAllPairs(nums) { + const N = nums.length; + if (N === 1) return true; + if (nums.includes(1)) return false; + + const MAX = Math.max(...nums); + const sieve = Array(MAX + 1).fill(0); + let p = 2; + while (p * p <= MAX) { + if (sieve[p] === 0) { + for (let composite = p * p; composite <= MAX; composite += p) { + sieve[composite] = p; + } + } + p++; + } + + const adj = new Map(); + for (let i = 0; i < N; i++) { + let num = nums[i]; + if (sieve[num] === 0) { // num is prime + if (!adj.has(i)) adj.set(i, []); + if (!adj.has(N + num)) adj.set(N + num, []); + adj.get(i).push(N + num); + adj.get(N + num).push(i); + continue; + } + + while (num > 1) { + const prime = sieve[num] !== 0 ? sieve[num] : num; + if (!adj.has(i)) adj.set(i, []); + if (!adj.has(N + prime)) adj.set(N + prime, []); + adj.get(i).push(N + prime); + adj.get(N + prime).push(i); + while (num % prime === 0) { + num = Math.floor(num / prime); + } + } + } + + const visited = new Set(); + const queue = new Queue([0]); + visited.add(0); + + while (!queue.isEmpty()) { + const node = queue.pop(); + if (adj.has(node)) { + for (const nei of adj.get(node)) { + if (!visited.has(nei)) { + visited.add(nei); + queue.push(nei); + } + } + } + } + + for (let i = 0; i < N; i++) { + if (!visited.has(i)) { + return false; + } + } + return true; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m + n \log m)$ +* Space complexity: $O(n + m)$ + +> Where $n$ is the size of the array $nums$ and $m$ is the maximum value in the array. \ No newline at end of file diff --git a/articles/ipo.md b/articles/ipo.md new file mode 100644 index 000000000..e71ba4c34 --- /dev/null +++ b/articles/ipo.md @@ -0,0 +1,374 @@ +## 1. Two Heaps + +::tabs-start + +```python +class Solution: + def findMaximizedCapital(self, k: int, w: int, profits: List[int], capital: List[int]) -> int: + maxProfit = [] # Max heap + minCapital = [(c, p) for c, p in zip(capital, profits)] # Min heap + heapq.heapify(minCapital) + + for _ in range(k): + while minCapital and minCapital[0][0] <= w: + c, p = heapq.heappop(minCapital) + heapq.heappush(maxProfit, -p) + + if not maxProfit: + break + + w += -heapq.heappop(maxProfit) + + return w +``` + +```java +public class Solution { + public int findMaximizedCapital(int k, int w, int[] profits, int[] capital) { + PriorityQueue minCapital = new PriorityQueue<>((a, b) -> a[0] - b[0]); // Min heap + PriorityQueue maxProfit = new PriorityQueue<>((a, b) -> b - a); // Max heap + + for (int i = 0; i < capital.length; i++) { + minCapital.offer(new int[]{capital[i], profits[i]}); + } + + for (int i = 0; i < k; i++) { + while (!minCapital.isEmpty() && minCapital.peek()[0] <= w) { + maxProfit.offer(minCapital.poll()[1]); + } + if (maxProfit.isEmpty()) { + break; + } + w += maxProfit.poll(); + } + + return w; + } +} +``` + +```cpp +class Solution { +public: + int findMaximizedCapital(int k, int w, vector& profits, vector& capital) { + priority_queue maxProfit; // Max heap + priority_queue, vector>, greater<>> minCapital; // Min heap + + for (int i = 0; i < capital.size(); i++) { + minCapital.emplace(capital[i], profits[i]); + } + + for (int i = 0; i < k; i++) { + while (!minCapital.empty() && minCapital.top().first <= w) { + maxProfit.push(minCapital.top().second); + minCapital.pop(); + } + if (maxProfit.empty()) { + break; + } + w += maxProfit.top(); + maxProfit.pop(); + } + + return w; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} k + * @param {number} w + * @param {number[]} profits + * @param {number[]} capital + * @return {number} + */ + findMaximizedCapital(k, w, profits, capital) { + const minCapital = new MinPriorityQueue({ compare: (a, b) => a[0] - b[0] }); // Min heap + const maxProfit = new MaxPriorityQueue({ compare: (a, b) => b - a }); // Max heap + + for (let i = 0; i < capital.length; i++) { + minCapital.enqueue([capital[i], profits[i]]); + } + + for (let i = 0; i < k; i++) { + while (!minCapital.isEmpty() && minCapital.front()[0] <= w) { + maxProfit.enqueue(minCapital.dequeue()[1]); + } + if (maxProfit.isEmpty()) { + break; + } + w += maxProfit.dequeue(); + } + + return w; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Two Heaps (Optimal) + +::tabs-start + +```python +class Solution: + def findMaximizedCapital(self, k: int, w: int, profits: List[int], capital: List[int]) -> int: + class Node: + def __init__(self, idx): + self.idx = idx + + def __lt__(self, other): + if capital[self.idx] != capital[other.idx]: + return capital[self.idx] < capital[other.idx] + return self.idx < other.idx + + minCapital = [] + maxProfit = [] + for i in range(len(capital)): + heapq.heappush(minCapital, Node(i)) + + for _ in range(k): + while minCapital and capital[minCapital[0].idx] <= w: + idx = heapq.heappop(minCapital).idx + heapq.heappush(maxProfit, -profits[idx]) + + if not maxProfit: + break + w += -heapq.heappop(maxProfit) + + return w +``` + +```java +public class Solution { + public int findMaximizedCapital(int k, int w, int[] profits, int[] capital) { + PriorityQueue minCapital = new PriorityQueue<>((a, b) -> capital[a] - capital[b]); + PriorityQueue maxProfit = new PriorityQueue<>((a, b) -> profits[b] - profits[a]); + + for (int i = 0; i < capital.length; i++) { + minCapital.offer(i); + } + + for (int i = 0; i < k; i++) { + while (!minCapital.isEmpty() && capital[minCapital.peek()] <= w) { + maxProfit.offer(minCapital.poll()); + } + if (maxProfit.isEmpty()) { + break; + } + w += profits[maxProfit.poll()]; + } + + return w; + } +} +``` + +```cpp +class Solution { +public: + int findMaximizedCapital(int k, int w, vector& profits, vector& capital) { + auto cmpCapital = [&](int a, int b) { return capital[a] > capital[b]; }; + auto cmpProfit = [&](int a, int b) { return profits[a] < profits[b]; }; + + priority_queue, decltype(cmpCapital)> minCapital(cmpCapital); + priority_queue, decltype(cmpProfit)> maxProfit(cmpProfit); + + for (int i = 0; i < capital.size(); i++) { + minCapital.push(i); + } + + for (int i = 0; i < k; i++) { + while (!minCapital.empty() && capital[minCapital.top()] <= w) { + maxProfit.push(minCapital.top()); + minCapital.pop(); + } + if (maxProfit.empty()) { + break; + } + w += profits[maxProfit.top()]; + maxProfit.pop(); + } + + return w; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} k + * @param {number} w + * @param {number[]} profits + * @param {number[]} capital + * @return {number} + */ + findMaximizedCapital(k, w, profits, capital) { + const minCapital = new MinPriorityQueue({ + compare: (a, b) => capital[a] - capital[b], + }); + const maxProfit = new MaxPriorityQueue({ + compare: (a, b) => profits[b] - profits[a], + }); + + for (let i = 0; i < capital.length; i++) { + minCapital.enqueue(i); + } + + for (let i = 0; i < k; i++) { + while (!minCapital.isEmpty() && capital[minCapital.front()] <= w) { + maxProfit.enqueue(minCapital.dequeue()); + } + if (maxProfit.isEmpty()) { + break; + } + w += profits[maxProfit.dequeue()]; + } + + return w; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Sorting + Max-Heap + +::tabs-start + +```python +class Solution: + def findMaximizedCapital(self, k: int, w: int, profits: List[int], capital: List[int]) -> int: + n = len(profits) + indices = list(range(n)) + indices.sort(key=lambda i: capital[i]) + + maxProfit, idx = [], 0 + for _ in range(k): + while idx < n and capital[indices[idx]] <= w: + heapq.heappush(maxProfit, -profits[indices[idx]]) + idx += 1 + + if not maxProfit: + break + w += -heapq.heappop(maxProfit) + + return w +``` + +```java +public class Solution { + public int findMaximizedCapital(int k, int w, int[] profits, int[] capital) { + int n = profits.length; + Integer[] indices = new Integer[n]; + for (int i = 0; i < n; i++) { + indices[i] = i; + } + Arrays.sort(indices, (a, b) -> Integer.compare(capital[a], capital[b])); + + PriorityQueue maxProfit = new PriorityQueue<>(Collections.reverseOrder()); + int idx = 0; + for (int i = 0; i < k; i++) { + while (idx < n && capital[indices[idx]] <= w) { + maxProfit.add(profits[indices[idx]]); + idx++; + } + if (maxProfit.isEmpty()) { + break; + } + w += maxProfit.poll(); + } + + return w; + } +} +``` + +```cpp +class Solution { +public: + int findMaximizedCapital(int k, int w, vector& profits, vector& capital) { + int n = profits.size(); + vector indices(n); + for (int i = 0; i < n; i++) { + indices[i] = i; + } + sort(indices.begin(), indices.end(), [&](int a, int b) { + return capital[a] < capital[b]; + }); + + priority_queue maxProfit; + int idx = 0; + for (int i = 0; i < k; i++) { + while (idx < n && capital[indices[idx]] <= w) { + maxProfit.push(profits[indices[idx]]); + idx++; + } + if (maxProfit.empty()) { + break; + } + w += maxProfit.top(); + maxProfit.pop(); + } + + return w; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} k + * @param {number} w + * @param {number[]} profits + * @param {number[]} capital + * @return {number} + */ + findMaximizedCapital(k, w, profits, capital) { + const n = profits.length; + const indices = Array.from({ length: n }, (_, i) => i); + indices.sort((a, b) => capital[a] - capital[b]); + + const maxProfit = new MaxPriorityQueue(); + let idx = 0; + for (let i = 0; i < k; i++) { + while (idx < n && capital[indices[idx]] <= w) { + maxProfit.enqueue(profits[indices[idx]]); + idx++; + } + if (maxProfit.isEmpty()) { + break; + } + w += maxProfit.dequeue().element; + } + + return w; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/longest-happy-string.md b/articles/longest-happy-string.md new file mode 100644 index 000000000..e5f2e0573 --- /dev/null +++ b/articles/longest-happy-string.md @@ -0,0 +1,458 @@ +## 1. Greedy + +::tabs-start + +```python +class Solution: + def longestDiverseString(self, a: int, b: int, c: int) -> str: + count = [a, b, c] + res = [] + + def getMax(repeated): + idx = -1 + maxCnt = 0 + for i in range(3): + if i == repeated or count[i] == 0: + continue + if maxCnt < count[i]: + maxCnt = count[i] + idx = i + return idx + + repeated = -1 + while True: + maxChar = getMax(repeated) + if maxChar == -1: + break + res.append(chr(maxChar + ord('a'))) + count[maxChar] -= 1 + if len(res) > 1 and res[-1] == res[-2]: + repeated = maxChar + else: + repeated = -1 + + return ''.join(res) +``` + +```java +public class Solution { + public String longestDiverseString(int a, int b, int c) { + int[] count = {a, b, c}; + StringBuilder res = new StringBuilder(); + + int repeated = -1; + while (true) { + int maxChar = getMax(count, repeated); + if (maxChar == -1) { + break; + } + res.append((char) (maxChar + 'a')); + count[maxChar]--; + + if (res.length() > 1 && res.charAt(res.length() - 1) == res.charAt(res.length() - 2)) { + repeated = maxChar; + } else { + repeated = -1; + } + } + + return res.toString(); + } + + private int getMax(int[] count, int repeated) { + int idx = -1, maxCnt = 0; + for (int i = 0; i < 3; i++) { + if (i == repeated || count[i] == 0) { + continue; + } + if (maxCnt < count[i]) { + maxCnt = count[i]; + idx = i; + } + } + return idx; + } +} +``` + +```cpp +class Solution { +public: + string longestDiverseString(int a, int b, int c) { + vector count = {a, b, c}; + string res; + + int repeated = -1; + while (true) { + int maxChar = getMax(count, repeated); + if (maxChar == -1) { + break; + } + res += (char)(maxChar + 'a'); + count[maxChar]--; + + if (res.size() > 1 && res.back() == res[res.size() - 2]) { + repeated = maxChar; + } else { + repeated = -1; + } + } + + return res; + } + +private: + int getMax(const vector& count, int repeated) { + int idx = -1, maxCnt = 0; + for (int i = 0; i < 3; i++) { + if (i == repeated || count[i] == 0) { + continue; + } + if (maxCnt < count[i]) { + maxCnt = count[i]; + idx = i; + } + } + return idx; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} a + * @param {number} b + * @param {number} c + * @return {string} + */ + longestDiverseString(a, b, c) { + const count = [a, b, c]; + const res = []; + + const getMax = (repeated) => { + let idx = -1; + let maxCnt = 0; + for (let i = 0; i < 3; i++) { + if (i === repeated || count[i] === 0) { + continue; + } + if (maxCnt < count[i]) { + maxCnt = count[i]; + idx = i; + } + } + return idx; + }; + + let repeated = -1; + while (true) { + const maxChar = getMax(repeated); + if (maxChar === -1) { + break; + } + res.push(String.fromCharCode(maxChar + 97)); + count[maxChar]--; + + if (res.length > 1 && res[res.length - 1] === res[res.length - 2]) { + repeated = maxChar; + } else { + repeated = -1; + } + } + + return res.join(''); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: + * $O(1)$ extra space. + * $O(n)$ space for the output string. + +--- + +## 2. Greedy (Max-Heap) + +::tabs-start + +```python +class Solution: + def longestDiverseString(self, a: int, b: int, c: int) -> str: + res = "" + maxHeap = [] + for count, char in [(-a, "a"), (-b, "b"), (-c, "c")]: + if count != 0: + heapq.heappush(maxHeap, (count, char)) + + while maxHeap: + count, char = heapq.heappop(maxHeap) + if len(res) > 1 and res[-1] == res[-2] == char: + if not maxHeap: + break + count2, char2 = heapq.heappop(maxHeap) + res += char2 + count2 += 1 + if count2: + heapq.heappush(maxHeap, (count2, char2)) + heapq.heappush(maxHeap, (count, char)) + else: + res += char + count += 1 + if count: + heapq.heappush(maxHeap, (count, char)) + + return res +``` + +```java +public class Solution { + public String longestDiverseString(int a, int b, int c) { + StringBuilder res = new StringBuilder(); + PriorityQueue maxHeap = new PriorityQueue<>((x, y) -> y[0] - x[0]); + + if (a > 0) maxHeap.offer(new int[]{a, 'a'}); + if (b > 0) maxHeap.offer(new int[]{b, 'b'}); + if (c > 0) maxHeap.offer(new int[]{c, 'c'}); + + while (!maxHeap.isEmpty()) { + int[] first = maxHeap.poll(); + if (res.length() > 1 && res.charAt(res.length() - 1) == first[1] && res.charAt(res.length() - 2) == first[1]) { + if (maxHeap.isEmpty()) break; + int[] second = maxHeap.poll(); + res.append((char) second[1]); + second[0]--; + if (second[0] > 0) maxHeap.offer(second); + maxHeap.offer(first); + } else { + res.append((char) first[1]); + first[0]--; + if (first[0] > 0) maxHeap.offer(first); + } + } + + return res.toString(); + } +} +``` + +```cpp +class Solution { +public: + string longestDiverseString(int a, int b, int c) { + string res; + priority_queue> maxHeap; + if (a > 0) maxHeap.push({a, 'a'}); + if (b > 0) maxHeap.push({b, 'b'}); + if (c > 0) maxHeap.push({c, 'c'}); + + while (!maxHeap.empty()) { + auto [count, ch] = maxHeap.top(); + maxHeap.pop(); + + if (res.size() > 1 && res[res.size() - 1] == ch && res[res.size() - 2] == ch) { + if (maxHeap.empty()) break; + auto [count2, ch2] = maxHeap.top(); + maxHeap.pop(); + res += ch2; + if (--count2 > 0) maxHeap.push({count2, ch2}); + maxHeap.push({count, ch}); + } else { + res += ch; + if (--count > 0) maxHeap.push({count, ch}); + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} a + * @param {number} b + * @param {number} c + * @return {string} + */ + longestDiverseString(a, b, c) { + const res = []; + const maxHeap = new MaxPriorityQueue({ priority: (x) => x[0] }); + + if (a > 0) maxHeap.enqueue([a, 'a']); + if (b > 0) maxHeap.enqueue([b, 'b']); + if (c > 0) maxHeap.enqueue([c, 'c']); + + while (!maxHeap.isEmpty()) { + const [count, char] = maxHeap.dequeue().element; + + if (res.length > 1 && res[res.length - 1] === char && res[res.length - 2] === char) { + if (maxHeap.isEmpty()) break; + const [count2, char2] = maxHeap.dequeue().element; + res.push(char2); + if (count2 - 1 > 0) maxHeap.enqueue([count2 - 1, char2]); + maxHeap.enqueue([count, char]); + } else { + res.push(char); + if (count - 1 > 0) maxHeap.enqueue([count - 1, char]); + } + } + + return res.join(''); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: + * $O(1)$ extra space. + * $O(n)$ space for the output string. + +--- + +## 3. Greedy (Recursion) + +::tabs-start + +```python +class Solution: + def longestDiverseString(self, a: int, b: int, c: int) -> str: + def rec(max1, max2, max3, char1, char2, char3): + if max1 < max2: + return rec(max2, max1, max3, char2, char1, char3) + if max2 < max3: + return rec(max1, max3, max2, char1, char3, char2) + if max2 == 0: + return [char1] * min(2, max1) + + use1 = min(2, max1) + use2 = 1 if max1 - use1 >= max2 else 0 + res = [char1] * use1 + [char2] * use2 + return res + rec(max1 - use1, max2 - use2, max3, char1, char2, char3) + + return ''.join(rec(a, b, c, 'a', 'b', 'c')) +``` + +```java +public class Solution { + public String longestDiverseString(int a, int b, int c) { + return String.join("", rec(a, b, c, 'a', 'b', 'c')); + } + + private List rec(int max1, int max2, int max3, char char1, char char2, char char3) { + if (max1 < max2) { + return rec(max2, max1, max3, char2, char1, char3); + } + if (max2 < max3) { + return rec(max1, max3, max2, char1, char3, char2); + } + if (max2 == 0) { + List result = new ArrayList<>(); + for (int i = 0; i < Math.min(2, max1); i++) { + result.add(String.valueOf(char1)); + } + return result; + } + + int use1 = Math.min(2, max1); + int use2 = (max1 - use1 >= max2) ? 1 : 0; + + List res = new ArrayList<>(); + for (int i = 0; i < use1; i++) { + res.add(String.valueOf(char1)); + } + for (int i = 0; i < use2; i++) { + res.add(String.valueOf(char2)); + } + + res.addAll(rec(max1 - use1, max2 - use2, max3, char1, char2, char3)); + return res; + } +} +``` + +```cpp +class Solution { +public: + string longestDiverseString(int a, int b, int c) { + vector res = rec(a, b, c, 'a', 'b', 'c'); + return string(res.begin(), res.end()); + } + +private: + vector rec(int max1, int max2, int max3, char char1, char char2, char char3) { + if (max1 < max2) { + return rec(max2, max1, max3, char2, char1, char3); + } + if (max2 < max3) { + return rec(max1, max3, max2, char1, char3, char2); + } + if (max2 == 0) { + vector result(min(2, max1), char1); + return result; + } + + int use1 = min(2, max1); + int use2 = (max1 - use1 >= max2) ? 1 : 0; + + vector res(use1, char1); + res.insert(res.end(), use2, char2); + + vector rest = rec(max1 - use1, max2 - use2, max3, char1, char2, char3); + res.insert(res.end(), rest.begin(), rest.end()); + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} a + * @param {number} b + * @param {number} c + * @return {string} + */ + longestDiverseString(a, b, c) { + const rec = (max1, max2, max3, char1, char2, char3) => { + if (max1 < max2) { + return rec(max2, max1, max3, char2, char1, char3); + } + if (max2 < max3) { + return rec(max1, max3, max2, char1, char3, char2); + } + if (max2 === 0) { + return Array(Math.min(2, max1)).fill(char1); + } + + const use1 = Math.min(2, max1); + const use2 = max1 - use1 >= max2 ? 1 : 0; + + const res = Array(use1).fill(char1).concat(Array(use2).fill(char2)); + return res.concat(rec(max1 - use1, max2 - use2, max3, char1, char2, char3)); + }; + + return rec(a, b, c, 'a', 'b', 'c').join(''); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: + * $O(n)$ for recursion stack. + * $O(n)$ space for the output string. \ No newline at end of file diff --git a/articles/meeting-rooms-iii.md b/articles/meeting-rooms-iii.md new file mode 100644 index 000000000..d8d33bcdb --- /dev/null +++ b/articles/meeting-rooms-iii.md @@ -0,0 +1,466 @@ +## 1. Sorting + Brute Force + +::tabs-start + +```python +class Solution: + def mostBooked(self, n: int, meetings: List[List[int]]) -> int: + meetings.sort() + rooms = [0] * n # end time of meetings in rooms + meeting_count = [0] * n + + for s, e in meetings: + min_room = 0 + found = False + for i in range(n): + if rooms[i] <= s: + found = True + meeting_count[i] += 1 + rooms[i] = e + break + + if rooms[min_room] > rooms[i]: + min_room = i + if found: + continue + meeting_count[min_room] += 1 + rooms[min_room] += e - s + + return meeting_count.index(max(meeting_count)) +``` + +```java +public class Solution { + public int mostBooked(int n, int[][] meetings) { + Arrays.sort(meetings, (a, b) -> Integer.compare(a[0], b[0])); + long[] rooms = new long[n]; // end times of meetings in rooms + int[] meetingCount = new int[n]; + + for (int[] meeting : meetings) { + int start = meeting[0], end = meeting[1]; + int minRoom = 0; + boolean found = false; + + for (int i = 0; i < n; i++) { + if (rooms[i] <= start) { + meetingCount[i]++; + rooms[i] = end; + found = true; + break; + } + if (rooms[minRoom] > rooms[i]) { + minRoom = i; + } + } + if (found) continue; + + meetingCount[minRoom]++; + rooms[minRoom] += end - start; + } + + int maxIndex = 0; + for (int i = 1; i < n; i++) { + if (meetingCount[i] > meetingCount[maxIndex]) { + maxIndex = i; + } + } + return maxIndex; + } +} +``` + +```cpp +class Solution { +public: + int mostBooked(int n, vector>& meetings) { + sort(meetings.begin(), meetings.end()); + vector rooms(n, 0); // end times of meetings in rooms + vector meetingCount(n, 0); + + for (const auto& meeting : meetings) { + int start = meeting[0], end = meeting[1]; + int minRoom = 0; + bool found = false; + + for (int i = 0; i < n; i++) { + if (rooms[i] <= start) { + meetingCount[i]++; + rooms[i] = end; + found = true; + break; + } + if (rooms[minRoom] > rooms[i]) { + minRoom = i; + } + } + + if (found) continue; + meetingCount[minRoom]++; + rooms[minRoom] += end - start; + } + + int maxIndex = 0; + for (int i = 1; i < n; i++) { + if (meetingCount[i] > meetingCount[maxIndex]) { + maxIndex = i; + } + } + return maxIndex; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number[][]} meetings + * @return {number} + */ + mostBooked(n, meetings) { + meetings.sort((a, b) => a[0] - b[0]); + const rooms = new Array(n).fill(0); // end times of meetings in rooms + const meetingCount = new Array(n).fill(0); + + for (const [start, end] of meetings) { + let minRoom = 0; + let found = false; + + for (let i = 0; i < n; i++) { + if (rooms[i] <= start) { + meetingCount[i]++; + rooms[i] = end; + found = true; + break; + } + if (rooms[minRoom] > rooms[i]) { + minRoom = i; + } + } + + if (found) continue; + meetingCount[minRoom]++; + rooms[minRoom] += end - start; + } + + return meetingCount.indexOf(Math.max(...meetingCount)); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m\log m + n * m)$ +* Space complexity: $O(n)$ + +> Where $n$ is the number of rooms and $m$ is the number of meetings. + +--- + +## 2. Two Min-Heaps + +::tabs-start + +```python +class Solution: + def mostBooked(self, n: int, meetings: List[List[int]]) -> int: + meetings.sort() + available = [i for i in range(n)] # Min heap for available rooms + used = [] # Min heap for used rooms [(end_time, room_number)] + count = [0] * n # Count of meetings for each room + + for start, end in meetings: + while used and used[0][0] <= start: + _, room = heapq.heappop(used) + heapq.heappush(available, room) + + if not available: + end_time, room = heapq.heappop(used) + end = end_time + (end - start) + heapq.heappush(available, room) + + room = heapq.heappop(available) + heapq.heappush(used, (end, room)) + count[room] += 1 + + return count.index(max(count)) +``` + +```java +public class Solution { + public int mostBooked(int n, int[][] meetings) { + Arrays.sort(meetings, (a, b) -> Long.compare(a[0], b[0])); + PriorityQueue available = new PriorityQueue<>(); + PriorityQueue used = new PriorityQueue<>((a, b) -> + a[0] == b[0] ? Long.compare(a[1], b[1]) : Long.compare(a[0], b[0]) + ); + for (int i = 0; i < n; i++) { + available.offer(i); + } + int[] count = new int[n]; + + for (int[] meeting : meetings) { + long start = meeting[0]; + long end = meeting[1]; + while (!used.isEmpty() && used.peek()[0] <= start) { + int room = (int) used.poll()[1]; + available.offer(room); + } + if (available.isEmpty()) { + long[] current = used.poll(); + int room = (int) current[1]; + end = current[0] + (end - start); + available.offer(room); + } + + int room = available.poll(); + used.offer(new long[]{end, room}); + count[room]++; + } + + int maxRoom = 0; + for (int i = 1; i < n; i++) { + if (count[i] > count[maxRoom]) { + maxRoom = i; + } + } + return maxRoom; + } +} +``` + +```cpp +class Solution { +public: + int mostBooked(int n, vector>& meetings) { + sort(meetings.begin(), meetings.end(), [](const vector& a, const vector& b) { + return (long long)a[0] < (long long)b[0]; + }); + priority_queue, greater> available; + priority_queue, vector>, greater>> used; + for (int i = 0; i < n; i++) { + available.push(i); + } + vector count(n); + + for (const auto& meeting : meetings) { + long long start = meeting[0]; + long long end = meeting[1]; + while (!used.empty() && used.top().first <= start) { + int room = used.top().second; + used.pop(); + available.push(room); + } + if (available.empty()) { + auto current = used.top(); + used.pop(); + end = current.first + (end - start); + available.push(current.second); + } + + int room = available.top(); + available.pop(); + used.push({end, room}); + count[room]++; + } + + int maxRoom = 0; + for (int i = 1; i < n; i++) { + if (count[i] > count[maxRoom]) { + maxRoom = i; + } + } + return maxRoom; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number[][]} meetings + * @return {number} + */ + mostBooked(n, meetings) { + meetings.sort((a, b) => a[0] - b[0]); + const available = new MinPriorityQueue({ compare: (a, b) => a - b }); + const used = new MinPriorityQueue({ + compare: (a, b) => (a[0] === b[0] ? a[1] - b[1] : a[0] - b[0]), + }); + for (let i = 0; i < n; i++) { + available.enqueue(i); + } + const count = new Array(n).fill(0); + + for (const [start, end] of meetings) { + while (!used.isEmpty() && used.front()[0] <= start) { + const room = used.dequeue()[1]; + available.enqueue(room); + } + + let room; + let newEnd = end; + if (available.isEmpty()) { + const [endTime, usedRoom] = used.dequeue(); + newEnd = endTime + (end - start); + available.enqueue(usedRoom); + } + room = available.dequeue(); + used.enqueue([newEnd, room]); + count[room]++; + } + + return count.indexOf(Math.max(...count)); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m\log m + m \log n)$ +* Space complexity: $O(n)$ + +> Where $n$ is the number of rooms and $m$ is the number of meetings. + +--- + +## 3. One Min-Heap + +::tabs-start + +```python +class Solution: + def mostBooked(self, n: int, meetings: List[List[int]]) -> int: + meetings.sort() + available = [] + count = [0] * n + + for i in range(n): + heapq.heappush(available, (0, i)) + + for start, end in meetings: + while available and available[0][0] < start: + end_time, room = heapq.heappop(available) + heapq.heappush(available, (start, room)) + + end_time, room = heapq.heappop(available) + heapq.heappush(available, (end_time + (end - start), room)) + count[room] += 1 + + return count.index(max(count)) +``` + +```java +public class Solution { + public int mostBooked(int n, int[][] meetings) { + Arrays.sort(meetings, (a, b) -> Integer.compare(a[0], b[0])); + PriorityQueue available = new PriorityQueue<>((a, b) -> + a[0] == b[0] ? Long.compare(a[1], b[1]) : Long.compare(a[0], b[0]) + ); + for (int i = 0; i < n; i++) { + available.offer(new long[]{0, i}); + } + int[] count = new int[n]; + + for (int[] meeting : meetings) { + int start = meeting[0], end = meeting[1]; + while (!available.isEmpty() && available.peek()[0] < start) { + long[] earliest = available.poll(); + available.offer(new long[]{start, earliest[1]}); + } + + long[] room = available.poll(); + long endTime = room[0] + (end - start); + available.offer(new long[]{endTime, room[1]}); + count[(int) room[1]]++; + } + + int maxRoom = 0; + for (int i = 1; i < n; i++) { + if (count[i] > count[maxRoom]) { + maxRoom = i; + } + } + return maxRoom; + } +} +``` + +```cpp +class Solution { +public: + int mostBooked(int n, vector>& meetings) { + sort(meetings.begin(), meetings.end()); + priority_queue, vector>, greater>> available; + for (int i = 0; i < n; i++) { + available.push({0, i}); + } + vector count(n); + + for (const auto& meeting : meetings) { + int start = meeting[0], end = meeting[1]; + while (!available.empty() && available.top().first < start) { + auto [end_time, room] = available.top(); + available.pop(); + available.push({start, room}); + } + + auto [end_time, room] = available.top(); + available.pop(); + available.push({end_time + (end - start), room}); + count[room]++; + } + + return max_element(count.begin(), count.end()) - count.begin(); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number[][]} meetings + * @return {number} + */ + mostBooked(n, meetings) { + meetings.sort((a, b) => a[0] - b[0]); + const available = new MinPriorityQueue({ + compare: (a, b) => (a[0] === b[0] ? a[1] - b[1] : a[0] - b[0]) + }); + for (let i = 0; i < n; i++) { + available.enqueue([0, i]); + } + const count = new Array(n).fill(0); + + for (const [start, end] of meetings) { + while (!available.isEmpty() && available.front()[0] < start) { + const [endTime, room] = available.dequeue(); + available.enqueue([start, room]); + } + + const [endTime, room] = available.dequeue(); + available.enqueue([endTime + (end - start), room]); + count[room]++; + } + + return count.indexOf(Math.max(...count)); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: + * $O(m \log m + m \log n)$ time in average case. + * $O(m \log m + m * n)$ time in worst case. +* Space complexity: $O(n)$ + +> Where $n$ is the number of rooms and $m$ is the number of meetings. \ No newline at end of file diff --git a/articles/single-threaded-cpu.md b/articles/single-threaded-cpu.md new file mode 100644 index 000000000..5b7005989 --- /dev/null +++ b/articles/single-threaded-cpu.md @@ -0,0 +1,471 @@ +## 1. Two Min-Heaps + +::tabs-start + +```python +class Solution: + def getOrder(self, tasks: List[List[int]]) -> List[int]: + available = [] + pending = [] + for i, (enqueueTime, processTime) in enumerate(tasks): + heapq.heappush(pending, (enqueueTime, processTime, i)) + + time = 0 + res = [] + while pending or available: + while pending and pending[0][0] <= time: + enqueueTime, processTime, i = heapq.heappop(pending) + heapq.heappush(available, (processTime, i)) + + if not available: + time = pending[0][0] + continue + + processTime, i = heapq.heappop(available) + time += processTime + res.append(i) + + return res +``` + +```java +public class Solution { + public int[] getOrder(int[][] tasks) { + PriorityQueue available = new PriorityQueue<>((a, b) -> + a[0] == b[0] ? Integer.compare(a[1], b[1]) : Integer.compare(a[0], b[0]) + ); + PriorityQueue pending = new PriorityQueue<>(Comparator.comparingInt(a -> a[0])); + + int n = tasks.length; + for (int i = 0; i < n; i++) { + pending.offer(new int[]{tasks[i][0], tasks[i][1], i}); + } + + long time = 0; + int idx = 0; + int[] res = new int[n]; + while (!pending.isEmpty() || !available.isEmpty()) { + while (!pending.isEmpty() && pending.peek()[0] <= time) { + int[] task = pending.poll(); + available.offer(new int[]{task[1], task[2]}); + } + + if (available.isEmpty()) { + time = pending.peek()[0]; + continue; + } + + int[] task = available.poll(); + time += task[0]; + res[idx++] = task[1]; + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector getOrder(vector>& tasks) { + priority_queue, vector>, greater<>> available; + priority_queue, vector>, greater<>> pending; + + int n = tasks.size(); + for (int i = 0; i < n; ++i) { + pending.push({tasks[i][0], tasks[i][1], i}); + } + + vector res; + long long time = 0; + while (!pending.empty() || !available.empty()) { + while (!pending.empty() && pending.top()[0] <= time) { + auto [enqueueTime, processTime, index] = pending.top(); + pending.pop(); + available.push({processTime, index}); + } + + if (available.empty()) { + time = pending.top()[0]; + continue; + } + + auto [processTime, index] = available.top(); + available.pop(); + time += processTime; + res.push_back(index); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} tasks + * @return {number[]} + */ + getOrder(tasks) { + const available = new MinPriorityQueue({ + compare: (a, b) => a[0] === b[0] ? a[1] - b[1] : a[0] - b[0] + }); + const pending = new MinPriorityQueue({ + compare: (a, b) => a[0] - b[0] + }); + + tasks.forEach(([enqueueTime, processTime], i) => { + pending.enqueue([enqueueTime, processTime, i]); + }); + + let time = 0; + const res = []; + while (!pending.isEmpty() || !available.isEmpty()) { + while (!pending.isEmpty() && pending.front()[0] <= time) { + const [enqueueTime, processTime, i] = pending.dequeue(); + available.enqueue([processTime, i]); + } + + if (available.isEmpty()) { + time = pending.front()[0]; + continue; + } + + const [processTime, i] = available.dequeue(); + time += processTime; + res.push(i); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Sorting + Min-Heap + +::tabs-start + +```python +class Solution: + def getOrder(self, tasks: List[List[int]]) -> List[int]: + for i, t in enumerate(tasks): + t.append(i) + tasks.sort(key=lambda t: t[0]) + + res, minHeap = [], [] + i, time = 0, tasks[0][0] + + while minHeap or i < len(tasks): + while i < len(tasks) and time >= tasks[i][0]: + heapq.heappush(minHeap, [tasks[i][1], tasks[i][2]]) + i += 1 + if not minHeap: + time = tasks[i][0] + else: + procTime, index = heapq.heappop(minHeap) + time += procTime + res.append(index) + return res +``` + +```java +public class Solution { + public int[] getOrder(int[][] tasks) { + int n = tasks.length; + for (int i = 0; i < n; i++) { + tasks[i] = new int[] {tasks[i][0], tasks[i][1], i}; + } + Arrays.sort(tasks, Comparator.comparingInt(t -> t[0])); + + int[] res = new int[n]; + PriorityQueue minHeap = new PriorityQueue<>((a, b) -> + a[0] == b[0] ? Integer.compare(a[1], b[1]) : Integer.compare(a[0], b[0]) + ); + + int i = 0, idx = 0; + long time = tasks[0][0]; + while (!minHeap.isEmpty() || i < n) { + while (i < n && time >= tasks[i][0]) { + minHeap.offer(new int[]{tasks[i][1], tasks[i][2]}); + i++; + } + if (minHeap.isEmpty()) { + time = tasks[i][0]; + } else { + int[] task = minHeap.poll(); + time += task[0]; + res[idx++] = task[1]; + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + vector getOrder(vector>& tasks) { + int n = tasks.size(); + for (int i = 0; i < n; ++i) { + tasks[i].push_back(i); + } + sort(tasks.begin(), tasks.end()); + + vector res; + priority_queue, vector>, greater<>> minHeap; + + int i = 0; + long long time = tasks[0][0]; + while (!minHeap.empty() || i < n) { + while (i < n && time >= tasks[i][0]) { + minHeap.push({tasks[i][1], tasks[i][2]}); + i++; + } + if (minHeap.empty()) { + time = tasks[i][0]; + } else { + auto [procTime, index] = minHeap.top(); + minHeap.pop(); + time += procTime; + res.push_back(index); + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} tasks + * @return {number[]} + */ + getOrder(tasks) { + const n = tasks.length; + tasks = tasks.map((t, i) => [...t, i]); + tasks.sort((a, b) => a[0] - b[0]); + + const res = []; + const minHeap = new MinPriorityQueue({ compare: (a, b) => + a[0] === b[0] ? a[1] - b[1] : a[0] - b[0] + }); + + let i = 0, time = tasks[0][0]; + while (minHeap.size() || i < n) { + while (i < n && time >= tasks[i][0]) { + minHeap.enqueue([tasks[i][1], tasks[i][2]]); + i++; + } + if (minHeap.isEmpty()) { + time = tasks[i][0]; + } else { + const [procTime, index] = minHeap.dequeue(); + time += procTime; + res.push(index); + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Sorting + Min-Heap (Optimal) + +::tabs-start + +```python +class Solution: + def getOrder(self, tasks: List[List[int]]) -> List[int]: + n = len(tasks) + indices = list(range(n)) + indices.sort(key=lambda i: (tasks[i][0], i)) + + class Task: + def __init__(self, idx): + self.idx = idx + + def __lt__(self, other): + if tasks[self.idx][1] != tasks[other.idx][1]: + return tasks[self.idx][1] < tasks[other.idx][1] + return self.idx < other.idx + + minHeap = [] + res = [] + time = i = 0 + while minHeap or i < n: + while i < n and tasks[indices[i]][0] <= time: + heapq.heappush(minHeap, Task(indices[i])) + i += 1 + + if not minHeap: + time = tasks[indices[i]][0] + else: + next_task = heapq.heappop(minHeap) + time += tasks[next_task.idx][1] + res.append(next_task.idx) + + return res +``` + +```java +public class Solution { + public int[] getOrder(int[][] tasks) { + int n = tasks.length; + Integer[] indices = new Integer[n]; + for (int i = 0; i < n; i++) { + indices[i] = i; + } + + Arrays.sort(indices, (a, b) -> + tasks[a][0] != tasks[b][0] ? tasks[a][0] - tasks[b][0] : a - b + ); + + PriorityQueue minHeap = new PriorityQueue<>((a, b) -> + tasks[a][1] != tasks[b][1] ? tasks[a][1] - tasks[b][1] : a - b + ); + + int[] result = new int[n]; + long time = 0; + int i = 0, resIndex = 0; + while (!minHeap.isEmpty() || i < n) { + while (i < n && tasks[indices[i]][0] <= time) { + minHeap.offer(indices[i]); + i++; + } + + if (minHeap.isEmpty()) { + time = tasks[indices[i]][0]; + } else { + int nextIndex = minHeap.poll(); + time += tasks[nextIndex][1]; + result[resIndex++] = nextIndex; + } + } + + return result; + } +} +``` + +```cpp +// C++ Solution +class Solution { +public: + vector getOrder(vector>& tasks) { + int n = tasks.size(); + vector indices(n); + iota(indices.begin(), indices.end(), 0); + + sort(indices.begin(), indices.end(), [&](int a, int b) { + return tasks[a][0] < tasks[b][0] || + (tasks[a][0] == tasks[b][0] && a < b); + }); + + auto comp = [&](int a, int b) { + return tasks[a][1] > tasks[b][1] || + (tasks[a][1] == tasks[b][1] && a > b); + }; + priority_queue, decltype(comp)> minHeap(comp); + + vector result; + long long time = 0; + int i = 0; + + while (!minHeap.empty() || i < n) { + while (i < n && tasks[indices[i]][0] <= time) { + minHeap.push(indices[i]); + i++; + } + + if (minHeap.empty()) { + time = tasks[indices[i]][0]; + } else { + int nextIndex = minHeap.top(); + minHeap.pop(); + time += tasks[nextIndex][1]; + result.push_back(nextIndex); + } + } + + return result; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} tasks + * @return {number[]} + */ + getOrder(tasks) { + const n = tasks.length; + const indices = Array.from({ length: n }, (_, i) => i); + indices.sort((a, b) => { + if (tasks[a][0] !== tasks[b][0]) { + return tasks[a][0] - tasks[b][0]; + } + return a - b; + }); + + const minHeap = new MinPriorityQueue({ + compare: (a, b) => { + if (tasks[a][1] !== tasks[b][1]) { + return tasks[a][1] - tasks[b][1]; + } + return a - b; + } + }); + + const res = []; + let time = 0; + let i = 0; + + while (!minHeap.isEmpty() || i < n) { + while (i < n && tasks[indices[i]][0] <= time) { + minHeap.enqueue(indices[i]); + i++; + } + + if (minHeap.size() === 0) { + time = tasks[indices[i]][0]; + } else { + const nextIndex = minHeap.dequeue(); + time += tasks[nextIndex][1]; + res.push(nextIndex); + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ \ No newline at end of file From d908bc0760c05b9f1ac3b414561251582060455f Mon Sep 17 00:00:00 2001 From: Sri Hari <94112314+Srihari2222@users.noreply.github.com> Date: Wed, 15 Jan 2025 18:26:58 +0530 Subject: [PATCH 27/45] Batch-5/Neetcode-Courses/Added-articles (#3808) --- articles/build-a-matrix-with-conditions.md | 6 +- articles/first-bad-version.md | 373 ++++++++ articles/linked-list-cycle-ii.md | 264 ++++++ articles/longest-increasing-subsequence-ii.md | 746 ++++++++++++++++ articles/my-calendar-i.md | 405 +++++++++ articles/prefix-and-suffix-search.md | 596 +++++++++++++ articles/search-in-a-binary-search-tree.md | 205 +++++ articles/shortest-common-supersequence.md | 803 ++++++++++++++++++ articles/sliding-window-median.md | 465 ++++++++++ ...items-by-groups-respecting-dependencies.md | 562 ++++++++++++ 10 files changed, 4422 insertions(+), 3 deletions(-) create mode 100644 articles/first-bad-version.md create mode 100644 articles/linked-list-cycle-ii.md create mode 100644 articles/longest-increasing-subsequence-ii.md create mode 100644 articles/my-calendar-i.md create mode 100644 articles/prefix-and-suffix-search.md create mode 100644 articles/search-in-a-binary-search-tree.md create mode 100644 articles/shortest-common-supersequence.md create mode 100644 articles/sliding-window-median.md create mode 100644 articles/sort-items-by-groups-respecting-dependencies.md diff --git a/articles/build-a-matrix-with-conditions.md b/articles/build-a-matrix-with-conditions.md index 8a4d3f610..cd7d0bfd9 100644 --- a/articles/build-a-matrix-with-conditions.md +++ b/articles/build-a-matrix-with-conditions.md @@ -489,7 +489,7 @@ class Solution { indegree[v]++; } - const queue = []; + const queue = new Queue(); const order = []; for (let i = 1; i <= k; i++) { @@ -498,8 +498,8 @@ class Solution { } } - while (queue.length) { - const node = queue.shift(); + while (!queue.isEmpty()) { + const node = queue.pop(); order.push(node); for (const nei of adj[node]) { indegree[nei]--; diff --git a/articles/first-bad-version.md b/articles/first-bad-version.md new file mode 100644 index 000000000..12305c833 --- /dev/null +++ b/articles/first-bad-version.md @@ -0,0 +1,373 @@ +## 1. Brute Force (Linear Search) + +::tabs-start + +```python +# The isBadVersion API is already defined for you. +# def isBadVersion(version: int) -> bool: + +class Solution: + def firstBadVersion(self, n: int) -> int: + for i in range(1, n): + if isBadVersion(i): + return i + return n +``` + +```java +/* The isBadVersion API is defined in the parent class VersionControl. + boolean isBadVersion(int version); */ + +public class Solution extends VersionControl { + public int firstBadVersion(int n) { + for (int i = 1; i < n; i++) { + if (isBadVersion(i)) { + return i; + } + } + return n; + } +} +``` + +```cpp +// The API isBadVersion is defined for you. +// bool isBadVersion(int version); + +class Solution { +public: + int firstBadVersion(int n) { + for (int i = 1; i < n; i++) { + if (isBadVersion(i)) { + return i; + } + } + return n; + } +}; +``` + +```javascript +// The isBadVersion API is already defined in the VersionControl class. +// isBadVersion(version: number): boolean + +class Solution extends VersionControl { + /** + * @param {number} n Total versions + * @return {number} The first bad version + */ + firstBadVersion(n) { + for (let i = 1; i < n; i++) { + if (this.isBadVersion(i)) { + return i; + } + } + return n; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. + +--- + +## 2. Recursive Binary Search + +::tabs-start + +```python +# The isBadVersion API is already defined for you. +# def isBadVersion(version: int) -> bool: + +class Solution: + def firstBadVersion(self, n: int) -> int: + def helper(l, r): + if l > r: + return l + m = l + (r - l) // 2 + if isBadVersion(m): + return helper(l, m - 1) + else: + return helper(m + 1, r) + + return helper(1, n) +``` + +```java +/* The isBadVersion API is defined in the parent class VersionControl. + boolean isBadVersion(int version); */ + +public class Solution extends VersionControl { + public int firstBadVersion(int n) { + return helper(1, n); + } + + private int helper(int l, int r) { + if (l > r) { + return l; + } + int m = l + (r - l) / 2; + if (isBadVersion(m)) { + return helper(l, m - 1); + } else { + return helper(m + 1, r); + } + } +} +``` + +```cpp +// The API isBadVersion is defined for you. +// bool isBadVersion(int version); + +class Solution { +public: + int firstBadVersion(int n) { + return helper(1, n); + } + +private: + int helper(int l, int r) { + if (l > r) { + return l; + } + int m = l + (r - l) / 2; + if (isBadVersion(m)) { + return helper(l, m - 1); + } else { + return helper(m + 1, r); + } + } +}; +``` + +```javascript +// The isBadVersion API is already defined in the VersionControl class. +// isBadVersion(version: number): boolean + +class Solution extends VersionControl { + /** + * @param {number} n Total versions + * @return {number} The first bad version + */ + firstBadVersion(n) { + const helper = (l, r) => { + if (l > r) { + return l; + } + const m = Math.floor(l + (r - l) / 2); + if (this.isBadVersion(m)) { + return helper(l, m - 1); + } else { + return helper(m + 1, r); + } + }; + return helper(1, n); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(\log n)$ +* Space complexity: $O(\log n)$ for recursion stack. + +--- + +## 3. Iterative Binary Search + +::tabs-start + +```python +# The isBadVersion API is already defined for you. +# def isBadVersion(version: int) -> bool: + +class Solution: + def firstBadVersion(self, n: int) -> int: + l, r = 1, n + res = -1 + while l <= r: + m = l + (r - l) // 2 + if isBadVersion(m): + res = m + r = m - 1 + else: + l = m + 1 + return res +``` + +```java +/* The isBadVersion API is defined in the parent class VersionControl. + boolean isBadVersion(int version); */ + +public class Solution extends VersionControl { + public int firstBadVersion(int n) { + int l = 1, r = n, res = -1; + while (l <= r) { + int m = l + (r - l) / 2; + if (isBadVersion(m)) { + res = m; + r = m - 1; + } else { + l = m + 1; + } + } + return res; + } +} +``` + +```cpp +// The API isBadVersion is defined for you. +// bool isBadVersion(int version); + +class Solution { +public: + int firstBadVersion(int n) { + int l = 1, r = n, res = -1; + while (l <= r) { + int m = l + (r - l) / 2; + if (isBadVersion(m)) { + res = m; + r = m - 1; + } else { + l = m + 1; + } + } + return res; + } +}; +``` + +```javascript +// The isBadVersion API is already defined in the VersionControl class. +// isBadVersion(version: number): boolean + +class Solution extends VersionControl { + /** + * @param {number} n Total versions + * @return {number} The first bad version + */ + firstBadVersion(n) { + let l = 1, r = n, res = -1; + while (l <= r) { + const m = Math.floor(l + (r - l) / 2); + if (this.isBadVersion(m)) { + res = m; + r = m - 1; + } else { + l = m + 1; + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(\log n)$ +* Space complexity: $O(1)$ + +--- + +## 4. Iterative Binary Search (Lower Bound) + +::tabs-start + +```python +# The isBadVersion API is already defined for you. +# def isBadVersion(version: int) -> bool: + +class Solution: + def firstBadVersion(self, n: int) -> int: + l, r = 1, n + while l < r: + m = l + (r - l) // 2 + if isBadVersion(m): + r = m + else: + l = m + 1 + return l +``` + +```java +/* The isBadVersion API is defined in the parent class VersionControl. + boolean isBadVersion(int version); */ + +public class Solution extends VersionControl { + public int firstBadVersion(int n) { + int l = 1, r = n; + while (l < r) { + int m = l + (r - l) / 2; + if (isBadVersion(m)) { + r = m; + } else { + l = m + 1; + } + } + return r; + } +} +``` + +```cpp +// The API isBadVersion is defined for you. +// bool isBadVersion(int version); + +class Solution { +public: + int firstBadVersion(int n) { + int l = 1, r = n; + while (l < r) { + int m = l + (r - l) / 2; + if (isBadVersion(m)) { + r = m; + } else { + l = m + 1; + } + } + return r; + } +}; +``` + +```javascript +// The isBadVersion API is already defined in the VersionControl class. +// isBadVersion(version: number): boolean + +class Solution extends VersionControl { + /** + * @param {number} n Total versions + * @return {number} The first bad version + */ + firstBadVersion(n) { + let l = 1, r = n; + while (l < r) { + const m = Math.floor(l + (r - l) / 2); + if (this.isBadVersion(m)) { + r = m; + } else { + l = m + 1; + } + } + return r; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(\log n)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/linked-list-cycle-ii.md b/articles/linked-list-cycle-ii.md new file mode 100644 index 000000000..2964377b8 --- /dev/null +++ b/articles/linked-list-cycle-ii.md @@ -0,0 +1,264 @@ +## 1. Hash Set + +::tabs-start + +```python +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, x): +# self.val = x +# self.next = None + +class Solution: + def detectCycle(self, head: Optional[ListNode]) -> Optional[ListNode]: + seen = set() + cur = head + while cur: + if cur in seen: + return cur + seen.add(cur) + cur = cur.next + return None +``` + +```java +/** + * Definition for singly-linked list. + * class ListNode { + * int val; + * ListNode next; + * ListNode(int x) { + * val = x; + * next = null; + * } + * } + */ +public class Solution { + public ListNode detectCycle(ListNode head) { + Set seen = new HashSet<>(); + ListNode cur = head; + while (cur != null) { + if (seen.contains(cur)) { + return cur; + } + seen.add(cur); + cur = cur.next; + } + return null; + } +} +``` + +```cpp +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode(int x) : val(x), next(NULL) {} + * }; + */ +class Solution { +public: + ListNode* detectCycle(ListNode* head) { + unordered_set seen; + ListNode* cur = head; + while (cur) { + if (seen.find(cur) != seen.end()) { + return cur; + } + seen.insert(cur); + cur = cur->next; + } + return nullptr; + } +}; +``` + +```javascript +/** + * Definition for singly-linked list. + * class ListNode { + * constructor(val = 0, next = null) { + * this.val = val; + * this.next = next; + * } + * } + */ +class Solution { + /** + * @param {ListNode} head + * @return {ListNode} + */ + detectCycle(head) { + const seen = new Set(); + let cur = head; + while (cur) { + if (seen.has(cur)) { + return cur; + } + seen.add(cur); + cur = cur.next; + } + return null; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Fast & Slow Pointers + +::tabs-start + +```python +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, x): +# self.val = x +# self.next = None + +class Solution: + def detectCycle(self, head: Optional[ListNode]) -> Optional[ListNode]: + if not head or not head.next: + return None + + slow, fast = head, head + while fast and fast.next: + slow = slow.next + fast = fast.next.next + if slow == fast: + slow = head + while slow != fast: + slow = slow.next + fast = fast.next + return slow + return None +``` + +```java +/** + * Definition for singly-linked list. + * class ListNode { + * int val; + * ListNode next; + * ListNode(int x) { + * val = x; + * next = null; + * } + * } + */ +public class Solution { + public ListNode detectCycle(ListNode head) { + if (head == null || head.next == null) { + return null; + } + + ListNode slow = head, fast = head; + while (fast != null && fast.next != null) { + slow = slow.next; + fast = fast.next.next; + if (slow == fast) { + slow = head; + while (slow != fast) { + slow = slow.next; + fast = fast.next; + } + return slow; + } + } + return null; + } +} +``` + +```cpp +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode(int x) : val(x), next(NULL) {} + * }; + */ +class Solution { +public: + ListNode* detectCycle(ListNode* head) { + if (!head || !head->next) { + return nullptr; + } + + ListNode* slow = head; + ListNode* fast = head; + + while (fast && fast->next) { + slow = slow->next; + fast = fast->next->next; + + if (slow == fast) { + slow = head; + while (slow != fast) { + slow = slow->next; + fast = fast->next; + } + return slow; + } + } + return nullptr; + } +}; +``` + +```javascript +/** + * Definition for singly-linked list. + * class ListNode { + * constructor(val = 0, next = null) { + * this.val = val; + * this.next = next; + * } + * } + */ +class Solution { + /** + * @param {ListNode} head + * @return {ListNode} + */ + detectCycle(head) { + if (!head || !head.next) { + return null; + } + + let slow = head, fast = head; + + while (fast && fast.next) { + slow = slow.next; + fast = fast.next.next; + + if (slow === fast) { + slow = head; + while (slow !== fast) { + slow = slow.next; + fast = fast.next; + } + return slow; + } + } + return null; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. \ No newline at end of file diff --git a/articles/longest-increasing-subsequence-ii.md b/articles/longest-increasing-subsequence-ii.md new file mode 100644 index 000000000..1d7a0030e --- /dev/null +++ b/articles/longest-increasing-subsequence-ii.md @@ -0,0 +1,746 @@ +## 1. Brute Force (Recursion) + +::tabs-start + +```python +class Solution: + def lengthOfLIS(self, nums: List[int], k: int) -> int: + def dfs(i): + res = 1 + for j in range(i + 1, len(nums)): + if nums[j] <= nums[i]: + continue + if nums[j] - nums[i] <= k: + res = max(res, 1 + dfs(j)) + return res + + res = 0 + for i in range(len(nums)): + res = max(res, dfs(i)) + return res +``` + +```java +public class Solution { + public int lengthOfLIS(int[] nums, int k) { + int n = nums.length; + int res = 0; + + for (int i = 0; i < n; i++) { + res = Math.max(res, dfs(nums, k, i)); + } + + return res; + } + + private int dfs(int[] nums, int k, int i) { + int res = 1; + + for (int j = i + 1; j < nums.length; j++) { + if (nums[j] <= nums[i]) continue; + if (nums[j] - nums[i] <= k) { + res = Math.max(res, 1 + dfs(nums, k, j)); + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int lengthOfLIS(vector& nums, int k) { + int n = nums.size(); + int res = 0; + + for (int i = 0; i < n; i++) { + res = max(res, dfs(nums, k, i)); + } + + return res; + } + +private: + int dfs(vector& nums, int k, int i) { + int res = 1; + + for (int j = i + 1; j < nums.size(); j++) { + if (nums[j] <= nums[i]) continue; + if (nums[j] - nums[i] <= k) { + res = max(res, 1 + dfs(nums, k, j)); + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {number} + */ + lengthOfLIS(nums, k) { + const dfs = (i) => { + let res = 1; + + for (let j = i + 1; j < nums.length; j++) { + if (nums[j] <= nums[i]) continue; + if (nums[j] - nums[i] <= k) { + res = Math.max(res, 1 + dfs(j)); + } + } + + return res; + }; + + let res = 0; + for (let i = 0; i < nums.length; i++) { + res = Math.max(res, dfs(i)); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * 2 ^ n)$ +* Space complexity: $O(n)$ for recursion stack. + +--- + +## 2. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def lengthOfLIS(self, nums: List[int], k: int) -> int: + n, res = len(nums), 0 + dp = [1] * n + for i in range(n): + for j in range(i): + if nums[j] >= nums[i]: + continue + if nums[i] - nums[j] <= k: + dp[i] = max(dp[i], 1 + dp[j]) + res = max(res, dp[i]) + return res +``` + +```java +public class Solution { + public int lengthOfLIS(int[] nums, int k) { + int n = nums.length, res = 0; + int[] dp = new int[n]; + Arrays.fill(dp, 1); + + for (int i = 0; i < n; i++) { + for (int j = 0; j < i; j++) { + if (nums[j] >= nums[i]) continue; + if (nums[i] - nums[j] <= k) { + dp[i] = Math.max(dp[i], 1 + dp[j]); + } + } + res = Math.max(res, dp[i]); + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int lengthOfLIS(vector& nums, int k) { + int n = nums.size(), res = 0; + vector dp(n, 1); + + for (int i = 0; i < n; i++) { + for (int j = 0; j < i; j++) { + if (nums[j] >= nums[i]) continue; + if (nums[i] - nums[j] <= k) { + dp[i] = max(dp[i], 1 + dp[j]); + } + } + res = max(res, dp[i]); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {number} + */ + lengthOfLIS(nums, k) { + const n = nums.length; + let res = 0; + const dp = new Array(n).fill(1); + + for (let i = 0; i < n; i++) { + for (let j = 0; j < i; j++) { + if (nums[j] >= nums[i]) continue; + if (nums[i] - nums[j] <= k) { + dp[i] = Math.max(dp[i], 1 + dp[j]); + } + } + res = Math.max(res, dp[i]); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n)$ + +--- + +## 3. Dynamic Programming + Segment Tree (Coordinate Compression) + +::tabs-start + +```python +class SegmentTree: + def __init__(self, N): + self.n = N + while (self.n & (self.n - 1)) != 0: + self.n += 1 + self.tree = [0] * (2 * self.n) + + def update(self, i, val): + if val <= self.tree[self.n + i]: + return + self.tree[self.n + i] = val + j = (self.n + i) >> 1 + while j >= 1: + self.tree[j] = max(self.tree[j << 1], self.tree[j << 1 | 1]) + j >>= 1 + + def query(self, ql, qh): + l = ql + self.n + r = qh + self.n + 1 + res = 0 + while l < r: + if l & 1: + res = max(res, self.tree[l]) + l += 1 + if r & 1: + r -= 1 + res = max(res, self.tree[r]) + l >>= 1 + r >>= 1 + return res + + +class Solution: + def lengthOfLIS(self, nums: List[int], k: int) -> int: + n = len(nums) + mp = {} + tmp = set([0]) + for num in nums: + if num - k > 0: + tmp.add(num - k) + if num - 1 > 0: + tmp.add(num - 1) + tmp.add(num) + + index = 0 + for value in sorted(tmp): + mp[value] = index + index += 1 + + ST = SegmentTree(index) + res = 0 + for num in nums: + l = mp.get(num - k, 0) + r = mp.get(num - 1, 0) + curr = ST.query(l, r) + 1 + res = max(res, curr) + ST.update(mp[num], curr) + + return res +``` + +```java +class SegmentTree { + private int n; + private int[] tree; + + public SegmentTree(int N) { + this.n = N; + while (Integer.bitCount(n) != 1) { + n++; + } + tree = new int[2 * n]; + } + + public void update(int i, int val) { + if (val <= tree[n + i]) return; + tree[n + i] = val; + for (int j = (n + i) >> 1; j >= 1; j >>= 1) { + tree[j] = Math.max(tree[j << 1], tree[(j << 1) + 1]); + } + } + + public int query(int ql, int qh) { + int l = ql + n, r = qh + n + 1, res = 0; + while (l < r) { + if ((l & 1) == 1) res = Math.max(res, tree[l++]); + if ((r & 1) == 1) res = Math.max(res, tree[--r]); + l >>= 1; + r >>= 1; + } + return res; + } +} + +public class Solution { + public int lengthOfLIS(int[] nums, int k) { + int n = nums.length; + TreeSet tmp = new TreeSet<>(); + tmp.add(0); + for (int num : nums) { + if (num - k > 0) tmp.add(num - k); + if (num - 1 > 0) tmp.add(num - 1); + tmp.add(num); + } + + Map mp = new HashMap<>(); + int index = 0; + for (int val : tmp) { + mp.put(val, index++); + } + + SegmentTree ST = new SegmentTree(index); + int res = 0; + for (int num : nums) { + int l = mp.getOrDefault(num - k, 0); + int r = mp.getOrDefault(num - 1, 0); + int curr = ST.query(l, r) + 1; + res = Math.max(res, curr); + ST.update(mp.get(num), curr); + } + return res; + } +} +``` + +```cpp +class SegmentTree { +public: + int n; + vector tree; + + SegmentTree(int N) { + this->n = N; + while (__builtin_popcount(n) != 1) { + n++; + } + tree.resize(2 * n, 0); + } + + void update(int i, int val) { + if (val <= tree[n + i]) return; + tree[n + i] = val; + for (int j = (n + i) >> 1; j >= 1; j >>= 1) { + tree[j] = max(tree[j << 1], tree[(j << 1) + 1]); + } + } + + int query(int ql, int qh) { + int l = ql + n, r = qh + n + 1, res = 0; + while (l < r) { + if (l & 1) res = max(res, tree[l++]); + if (r & 1) res = max(res, tree[--r]); + l >>= 1; + r >>= 1; + } + return res; + } +}; + +class Solution { +public: + int lengthOfLIS(vector& nums, int k) { + int n = nums.size(); + unordered_map mp; + set tmp = {0}; + + for (int num : nums) { + if (num - k > 0) tmp.insert(num - k); + if (num - 1 > 0) tmp.insert(num - 1); + tmp.insert(num); + } + + int index = 0; + for (const int& val : tmp) { + mp[val] = index++; + } + + SegmentTree ST(index); + int ans = 0; + for (int& num : nums) { + int l = mp[num - k]; + int r = mp[num - 1]; + int curr = ST.query(l, r) + 1; + ans = max(ans, curr); + ST.update(mp[num], curr); + } + + return ans; + } +}; +``` + +```javascript +class SegmentTree { + /** + * @constructor + * @param {number} N + */ + constructor(N) { + this.n = N; + while ((this.n & (this.n - 1)) !== 0) { + this.n++; + } + this.tree = Array(2 * this.n).fill(0); + } + + /** + * @param {number} i + * @param {number} val + * @return {void} + */ + update(i, val) { + if (val <= this.tree[this.n + i]) return; + this.tree[this.n + i] = val; + for (let j = (this.n + i) >> 1; j >= 1; j >>= 1) { + this.tree[j] = Math.max(this.tree[j << 1], this.tree[(j << 1) + 1]); + } + } + + /** + * @param {number} ql + * @param {number} qh + * @return {number} + */ + query(ql, qh) { + let l = ql + this.n, r = qh + this.n + 1, res = 0; + while (l < r) { + if (l & 1) res = Math.max(res, this.tree[l++]); + if (r & 1) res = Math.max(res, this.tree[--r]); + l >>= 1; + r >>= 1; + } + return res; + } +} + +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {number} + */ + lengthOfLIS(nums, k) { + const n = nums.length; + const tmp = new Set([0]); + for (const num of nums) { + if (num - k > 0) tmp.add(num - k); + if (num - 1 > 0) tmp.add(num - 1); + tmp.add(num); + } + + const mp = new Map(); + let index = 0; + Array.from(tmp).sort((a, b) => a - b).forEach(val => mp.set(val, index++)); + + const ST = new SegmentTree(index); + let ans = 0; + for (const num of nums) { + const l = mp.has(num - k) ? mp.get(num - k) : 0; + const r = mp.has(num - 1) ? mp.get(num - 1) : 0; + const curr = ST.query(l, r) + 1; + ans = Math.max(ans, curr); + ST.update(mp.get(num), curr); + } + return ans; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ + +--- + +## 4. Dynamic Programming + Segment Tree + +::tabs-start + +```python +class SegmentTree: + def __init__(self, N): + self.n = N + while (self.n & (self.n - 1)) != 0: + self.n += 1 + self.tree = [0] * (2 * self.n) + + def update(self, i, val): + if val <= self.tree[self.n + i]: + return + self.tree[self.n + i] = val + j = (self.n + i) >> 1 + while j >= 1: + self.tree[j] = max(self.tree[j << 1], self.tree[j << 1 | 1]) + j >>= 1 + + def query(self, ql, qh): + l = ql + self.n + r = qh + self.n + 1 + res = 0 + while l < r: + if l & 1: + res = max(res, self.tree[l]) + l += 1 + if r & 1: + r -= 1 + res = max(res, self.tree[r]) + l >>= 1 + r >>= 1 + return res + + +class Solution: + def lengthOfLIS(self, nums: List[int], k: int) -> int: + max_val = max(nums) + ST = SegmentTree(max_val + 1) + res = 0 + for num in nums: + l = max(0, num - k) + r = max(0, num - 1) + curr = ST.query(l, r) + 1 + res = max(res, curr) + ST.update(num, curr) + + return res +``` + +```java +class SegmentTree { + private int[] tree; + private int n; + + public SegmentTree(int N) { + this.n = N; + while ((this.n & (this.n - 1)) != 0) { + this.n++; + } + this.tree = new int[2 * this.n]; + } + + public void update(int i, int val) { + if (val <= tree[n + i]) return; + tree[n + i] = val; + for (int j = (n + i) >> 1; j >= 1; j >>= 1) { + tree[j] = Math.max(tree[j << 1], tree[(j << 1) + 1]); + } + } + + public int query(int l, int r) { + l += n; + r += n + 1; + int res = 0; + while (l < r) { + if ((l & 1) == 1) { + res = Math.max(res, tree[l++]); + } + if ((r & 1) == 1) { + res = Math.max(res, tree[--r]); + } + l >>= 1; + r >>= 1; + } + return res; + } +} + +public class Solution { + public int lengthOfLIS(int[] nums, int k) { + int maxVal = 0; + for (int num : nums) { + maxVal = Math.max(maxVal, num); + } + + SegmentTree ST = new SegmentTree(maxVal + 1); + int res = 0; + for (int num : nums) { + int l = Math.max(0, num - k); + int r = Math.max(0, num - 1); + int curr = ST.query(l, r) + 1; + res = Math.max(res, curr); + ST.update(num, curr); + } + return res; + } +} +``` + +```cpp +class SegmentTree { +public: + int n; + vector tree; + + SegmentTree(int N) { + n = N; + while ((n & (n - 1)) != 0) { + n++; + } + tree.resize(2 * n, 0); + } + + void update(int i, int val) { + if (val <= tree[n + i]) return; + tree[n + i] = val; + for (int j = (n + i) >> 1; j >= 1; j >>= 1) { + tree[j] = max(tree[j << 1], tree[(j << 1) + 1]); + } + } + + int query(int l, int r) { + l += n; + r += n + 1; + int res = 0; + while (l < r) { + if (l & 1) { + res = max(res, tree[l++]); + } + if (r & 1) { + res = max(res, tree[--r]); + } + l >>= 1; + r >>= 1; + } + return res; + } +}; + +class Solution { +public: + int lengthOfLIS(vector& nums, int k) { + int maxVal = *max_element(nums.begin(), nums.end()); + SegmentTree ST(maxVal + 1); + int res = 0; + for (int& num : nums) { + int l = max(0, num - k); + int r = max(0, num - 1); + int curr = ST.query(l, r) + 1; + res = max(res, curr); + ST.update(num, curr); + } + return res; + } +}; +``` + +```javascript +class SegmentTree { + /** + * @constructor + * @param {number} N + */ + constructor(N) { + this.n = N; + while ((this.n & (this.n - 1)) !== 0) { + this.n++; + } + this.tree = Array(2 * this.n).fill(0); + } + + /** + * @param {number} i + * @param {number} val + * @return {void} + */ + update(i, val) { + if (val <= this.tree[this.n + i]) return; + this.tree[this.n + i] = val; + for (let j = (this.n + i) >> 1; j >= 1; j >>= 1) { + this.tree[j] = Math.max(this.tree[j << 1], this.tree[(j << 1) + 1]); + } + } + + /** + * @param {number} ql + * @param {number} qh + * @return {number} + */ + query(ql, qh) { + let l = ql + this.n, r = qh + this.n + 1, res = 0; + while (l < r) { + if (l & 1) res = Math.max(res, this.tree[l++]); + if (r & 1) res = Math.max(res, this.tree[--r]); + l >>= 1; + r >>= 1; + } + return res; + } +} + +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {number} + */ + lengthOfLIS(nums, k) { + const maxVal = Math.max(...nums); + const ST = new SegmentTree(maxVal + 1); + let res = 0; + for (const num of nums) { + const l = Math.max(0, num - k); + const r = Math.max(0, num - 1); + const curr = ST.query(l, r) + 1; + res = Math.max(res, curr); + ST.update(num, curr); + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n\log m)$ +* Space complexity: $O(m)$ + +> Where $n$ is the size of the array $nums$ and $m$ is the maximum element in the array. \ No newline at end of file diff --git a/articles/my-calendar-i.md b/articles/my-calendar-i.md new file mode 100644 index 000000000..f926c9e13 --- /dev/null +++ b/articles/my-calendar-i.md @@ -0,0 +1,405 @@ +## 1. Iteration + +::tabs-start + +```python +class MyCalendar: + + def __init__(self): + self.events = [] + + def book(self, startTime: int, endTime: int) -> bool: + for start, end in self.events: + if startTime < end and start < endTime: + return False + + self.events.append((startTime, endTime)) + return True +``` + +```java +public class MyCalendar { + private List events; + + public MyCalendar() { + events = new ArrayList<>(); + } + + public boolean book(int startTime, int endTime) { + for (int[] event : events) { + if (startTime < event[1] && event[0] < endTime) { + return false; + } + } + events.add(new int[]{startTime, endTime}); + return true; + } +} +``` + +```cpp +class MyCalendar { +private: + vector> events; + +public: + MyCalendar() {} + + bool book(int startTime, int endTime) { + for (const auto& event : events) { + if (startTime < event.second && event.first < endTime) { + return false; + } + } + events.push_back({startTime, endTime}); + return true; + } +}; +``` + +```javascript +class MyCalendar { + constructor() { + this.events = []; + } + + /** + * @param {number} startTime + * @param {number} endTime + * @return {boolean} + */ + book(startTime, endTime) { + for (const [start, end] of this.events) { + if (startTime < end && start < endTime) { + return false; + } + } + this.events.push([startTime, endTime]); + return true; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ for each $book()$ function call. +* Space complexity: $O(n)$ + +--- + +## 2. Binary Search Tree + +::tabs-start + +```python +class TreeNode: + def __init__(self, start: int, end: int): + self.start = start + self.end = end + self.left = None + self.right = None + +class MyCalendar: + + def __init__(self): + self.root = None + + def _insert(self, node: TreeNode, start: int, end: int) -> bool: + if end <= node.start: + if not node.left: + node.left = TreeNode(start, end) + return True + return self._insert(node.left, start, end) + elif start >= node.end: + if not node.right: + node.right = TreeNode(start, end) + return True + return self._insert(node.right, start, end) + else: + return False + + def book(self, startTime: int, endTime: int) -> bool: + if not self.root: + self.root = TreeNode(startTime, endTime) + return True + return self._insert(self.root, startTime, endTime) +``` + +```java +class TreeNode { + int start, end; + TreeNode left, right; + + TreeNode(int start, int end) { + this.start = start; + this.end = end; + this.left = null; + this.right = null; + } +} + +public class MyCalendar { + private TreeNode root; + + public MyCalendar() { + root = null; + } + + private boolean insert(TreeNode node, int start, int end) { + if (end <= node.start) { + if (node.left == null) { + node.left = new TreeNode(start, end); + return true; + } + return insert(node.left, start, end); + } else if (start >= node.end) { + if (node.right == null) { + node.right = new TreeNode(start, end); + return true; + } + return insert(node.right, start, end); + } + return false; + } + + public boolean book(int startTime, int endTime) { + if (root == null) { + root = new TreeNode(startTime, endTime); + return true; + } + return insert(root, startTime, endTime); + } +} +``` + +```cpp +class MyCalendar { +private: + struct TreeNode { + int start, end; + TreeNode *left, *right; + + TreeNode(int start, int end) : start(start), end(end), left(nullptr), right(nullptr) {} + }; + TreeNode *root; + + bool insert(TreeNode *node, int start, int end) { + if (end <= node->start) { + if (!node->left) { + node->left = new TreeNode(start, end); + return true; + } + return insert(node->left, start, end); + } else if (start >= node->end) { + if (!node->right) { + node->right = new TreeNode(start, end); + return true; + } + return insert(node->right, start, end); + } + return false; + } + +public: + MyCalendar() : root(nullptr) {} + + bool book(int startTime, int endTime) { + if (!root) { + root = new TreeNode(startTime, endTime); + return true; + } + return insert(root, startTime, endTime); + } +}; +``` + +```javascript +class TreeNode { + /** + * @constructor + * @param {number} start + * @param {number} end + */ + constructor(start, end) { + this.start = start; + this.end = end; + this.left = null; + this.right = null; + } +} + +class MyCalendar { + constructor() { + this.root = null; + } + + /** + * @param {TreeNode} node + * @param {number} start + * @param {number} end + * @return {boolean} + */ + insert(node, start, end) { + if (end <= node.start) { + if (!node.left) { + node.left = new TreeNode(start, end); + return true; + } + return this.insert(node.left, start, end); + } else if (start >= node.end) { + if (!node.right) { + node.right = new TreeNode(start, end); + return true; + } + return this.insert(node.right, start, end); + } + return false; + } + + /** + * @param {number} startTime + * @param {number} endTime + * @return {boolean} + */ + book(startTime, endTIme) { + if (!this.root) { + this.root = new TreeNode(startTime, endTime); + return true; + } + return this.insert(this.root, startTime, endTime); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(\log n)$ in average case, $O(n)$ in worst case for each $book()$ function call. +* Space complexity: $O(n)$ + +--- + +## 3. Binary Search + Ordered Set + +::tabs-start + +```python +class MyCalendar: + + def __init__(self): + self.events = SortedList() + + def book(self, startTime: int, endTime: int) -> bool: + idx = self.events.bisect_left((startTime, endTime)) + if idx > 0 and self.events[idx - 1][1] > startTime: + return False + if idx < len(self.events) and self.events[idx][0] < endTime: + return False + self.events.add((startTime, endTime)) + return True +``` + +```java +public class MyCalendar { + private TreeSet events; + + public MyCalendar() { + events = new TreeSet<>((a, b) -> a[0] - b[0]); + } + + public boolean book(int startTime, int endTime) { + int[] event = new int[]{startTime, endTime}; + int[] prev = events.floor(event); + int[] next = events.ceiling(event); + + if ((prev != null && prev[1] > startTime) || (next != null && next[0] < endTime)) { + return false; + } + events.add(event); + return true; + } +} +``` + +```cpp +class MyCalendar { +private: + set> events; + +public: + MyCalendar() {} + + bool book(int startTime, int endTime) { + if (startTime >= endTime) { + return false; + } + + auto next = events.lower_bound({startTime, startTime}); + if (next != events.end() && next->first < endTime) { + return false; + } + if (next != events.begin()) { + auto prev = std::prev(next); + if (prev->second > startTime) { + return false; + } + } + + events.insert({startTime, endTime}); + return true; + } +}; +``` + +```javascript +class MyCalendar { + constructor() { + this.events = []; + } + + /** + * @param {number} startTime + * @param {number} endTime + * @return {boolean} + */ + book(startTime, endTIme) { + if (startTime >= endTime) { + return false; + } + + const binarySearch = (target) => { + let left = 0, right = this.events.length; + + while (left < right) { + let mid = Math.floor((left + right) / 2); + if (this.events[mid][0] < target) { + left = mid + 1; + } else { + right = mid; + } + } + return left; + }; + + const idx = binarySearch(startTime); + if (idx > 0 && this.events[idx - 1][1] > startTime) { + return false; + } + if (idx < this.events.length && this.events[idx][0] < endTime) { + return false; + } + this.events.splice(idx, 0, [startTime, endTime]); + return true; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(\log n)$ for each $book()$ function call. +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/prefix-and-suffix-search.md b/articles/prefix-and-suffix-search.md new file mode 100644 index 000000000..a2674805b --- /dev/null +++ b/articles/prefix-and-suffix-search.md @@ -0,0 +1,596 @@ +## 1. Brute Force + +::tabs-start + +```python +class WordFilter: + + def __init__(self, words: List[str]): + self.words = words + + def f(self, pref: str, suff: str) -> int: + for i in range(len(self.words) - 1, -1, -1): + w = self.words[i] + if len(w) < len(pref) or len(w) < len(suff): + continue + + j, flag = 0, True + for c in pref: + if w[j] != c: + flag = False + break + j += 1 + + if not flag: + continue + + j = len(w) - len(suff) + for c in suff: + if w[j] != c: + flag = False + break + j += 1 + + if flag: + return i + + return -1 +``` + +```java +public class WordFilter { + private String[] words; + + public WordFilter(String[] words) { + this.words = words; + } + + public int f(String pref, String suff) { + for (int i = words.length - 1; i >= 0; i--) { + String w = words[i]; + if (w.length() < pref.length() || w.length() < suff.length()) { + continue; + } + + boolean flag = true; + for (int j = 0; j < pref.length(); j++) { + if (w.charAt(j) != pref.charAt(j)) { + flag = false; + break; + } + } + + if (!flag) { + continue; + } + + int j = w.length() - suff.length(); + for (int k = 0; k < suff.length(); k++) { + if (w.charAt(j + k) != suff.charAt(k)) { + flag = false; + break; + } + } + + if (flag) { + return i; + } + } + + return -1; + } +} +``` + +```cpp +class WordFilter { +private: + vector words; + +public: + WordFilter(vector& words) { + this->words = words; + } + + int f(string pref, string suff) { + for (int i = words.size() - 1; i >= 0; i--) { + const string& w = words[i]; + if (w.size() < pref.size() || w.size() < suff.size()) { + continue; + } + + bool flag = true; + for (int j = 0; j < pref.size(); j++) { + if (w[j] != pref[j]) { + flag = false; + break; + } + } + + if (!flag) { + continue; + } + + int j = w.size() - suff.size(); + for (int k = 0; k < suff.size(); k++) { + if (w[j + k] != suff[k]) { + flag = false; + break; + } + } + + if (flag) { + return i; + } + } + + return -1; + } +}; +``` + +```javascript +class WordFilter { + /** + * @constructor + * @param {string[]} words + */ + constructor(words) { + this.words = words; + } + + /** + * @param {string} pref + * @param {string} suff + * @return {number} + */ + f(pref, suff) { + for (let i = this.words.length - 1; i >= 0; i--) { + const w = this.words[i]; + if (w.length < pref.length || w.length < suff.length) { + continue; + } + + let flag = true; + for (let j = 0; j < pref.length; j++) { + if (w[j] !== pref[j]) { + flag = false; + break; + } + } + + if (!flag) { + continue; + } + + let j = w.length - suff.length; + for (let k = 0; k < suff.length; k++) { + if (w[j + k] !== suff[k]) { + flag = false; + break; + } + } + + if (flag) { + return i; + } + } + + return -1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(N * m * n)$ +* Space complexity: $O(1)$ extra space. + +> Where $N$ is the number $f()$ function calls, $n$ is the number of words, and $m$ is the average length of each word. + +--- + +## 2. Hash Map + +::tabs-start + +```python +class WordFilter: + + def __init__(self, words: List[str]): + self.mp = {} + for i, w in enumerate(words): + for j in range(len(w)): + pref = w[:j + 1] + for k in range(len(w)): + cur = pref + "$" + w[k:] + self.mp[cur] = i + + def f(self, pref: str, suff: str) -> int: + s = pref + "$" + suff + if s not in self.mp: + return -1 + + return self.mp[s] +``` + +```java +public class WordFilter { + private Map mp; + + public WordFilter(String[] words) { + mp = new HashMap<>(); + for (int i = 0; i < words.length; i++) { + String w = words[i]; + for (int j = 0; j < w.length(); j++) { + String pref = w.substring(0, j + 1); + for (int k = 0; k < w.length(); k++) { + String cur = pref + "$" + w.substring(k); + mp.put(cur, i); + } + } + } + } + + public int f(String pref, String suff) { + String s = pref + "$" + suff; + return mp.getOrDefault(s, -1); + } +} +``` + +```cpp +class WordFilter { +private: + unordered_map mp; + +public: + WordFilter(vector& words) { + for (int i = 0; i < words.size(); i++) { + string w = words[i]; + for (int j = 0; j < w.size(); j++) { + string pref = w.substr(0, j + 1); + for (int k = 0; k < w.size(); k++) { + string cur = pref + "$" + w.substr(k); + mp[cur] = i; + } + } + } + } + + int f(string pref, string suff) { + string s = pref + "$" + suff; + if (mp.find(s) == mp.end()) { + return -1; + } + return mp[s]; + } +}; +``` + +```javascript +class WordFilter { + /** + * @constructor + * @param {string[]} words + */ + constructor(words) { + this.mp = new Map(); + for (let i = 0; i < words.length; i++) { + const w = words[i]; + for (let j = 0; j < w.length; j++) { + const pref = w.slice(0, j + 1); + for (let k = 0; k < w.length; k++) { + const cur = pref + "$" + w.slice(k); + this.mp.set(cur, i); + } + } + } + } + + /** + * @param {string} pref + * @param {string} suff + * @return {number} + */ + f(pref, suff) { + const s = pref + "$" + suff; + return this.mp.has(s) ? this.mp.get(s) : -1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: + * $O(n * m ^ 3)$ time for initialization. + * $O(m)$ for each $f()$ function call. +* Space complexity: $O(n * m ^ 3)$ + +> Where $n$ is the number of words and $m$ is the average length of each word. + +--- + +## 3. Trie + +::tabs-start + +```python +class TrieNode: + def __init__(self): + self.children = [None] * 27 + self.index = -1 + +class Trie: + def __init__(self): + self.root = TrieNode() + + def addWord(self, w, i): + cur = self.root + for ch in w: + c = ord(ch) - ord('a') + if not cur.children[c]: + cur.children[c] = TrieNode() + cur = cur.children[c] + cur.index = i + + def search(self, w): + cur = self.root + for ch in w: + c = ord(ch) - ord('a') + if not cur.children[c]: + return -1 + cur = cur.children[c] + return cur.index + +class WordFilter: + def __init__(self, words: List[str]): + self.trie = Trie() + self.CHAR = '{' + for i, w in enumerate(words): + w_len = len(w) + for j in range(w_len): + suffix = w[j:] + for k in range(w_len + 1): + prefix = w[:k] + self.trie.addWord(suffix + self.CHAR + prefix, i) + + def f(self, pref: str, suff: str) -> int: + return self.trie.search(suff + self.CHAR + pref) +``` + +```java +class TrieNode { + TrieNode[] children; + int index; + + TrieNode() { + children = new TrieNode[27]; + index = -1; + } +} + +class Trie { + private TrieNode root; + + Trie() { + root = new TrieNode(); + } + + void addWord(String word, int i) { + TrieNode cur = root; + for (char ch : word.toCharArray()) { + int c = ch == '{' ? 26 : ch - 'a'; + if (cur.children[c] == null) { + cur.children[c] = new TrieNode(); + } + cur = cur.children[c]; + } + cur.index = i; + } + + int search(String word) { + TrieNode cur = root; + for (char ch : word.toCharArray()) { + int c = ch == '{' ? 26 : ch - 'a'; + if (cur.children[c] == null) { + return -1; + } + cur = cur.children[c]; + } + return cur.index; + } +} + +public class WordFilter { + private Trie trie; + private static final char CHAR = '{'; + + public WordFilter(String[] words) { + trie = new Trie(); + for (int i = 0; i < words.length; i++) { + String word = words[i]; + int wLen = word.length(); + for (int j = 0; j < wLen; j++) { + String suffix = word.substring(j); + for (int k = 0; k <= wLen; k++) { + String prefix = word.substring(0, k); + trie.addWord(suffix + CHAR + prefix, i); + } + } + } + } + + public int f(String pref, String suff) { + return trie.search(suff + CHAR + pref); + } +} +``` + +```cpp +class TrieNode { +public: + TrieNode* children[27]; + int index; + + TrieNode() { + for (int i = 0; i < 27; i++) { + children[i] = nullptr; + } + index = -1; + } +}; + +class Trie { +private: + TrieNode* root; + +public: + Trie() { + root = new TrieNode(); + } + + void addWord(const string& word, int i) { + TrieNode* cur = root; + for (char ch : word) { + int c = (ch == '{') ? 26 : (ch - 'a'); + if (cur->children[c] == nullptr) { + cur->children[c] = new TrieNode(); + } + cur = cur->children[c]; + } + cur->index = i; + } + + int search(const string& word) { + TrieNode* cur = root; + for (char ch : word) { + int c = (ch == '{') ? 26 : (ch - 'a'); + if (cur->children[c] == nullptr) { + return -1; + } + cur = cur->children[c]; + } + return cur->index; + } +}; + +class WordFilter { +private: + Trie trie; + const char CHAR = '{'; + +public: + WordFilter(vector& words) { + for (int i = 0; i < words.size(); i++) { + string word = words[i]; + int wLen = word.length(); + for (int j = 0; j < wLen; j++) { + string suffix = word.substr(j); + for (int k = 0; k <= wLen; k++) { + string prefix = word.substr(0, k); + trie.addWord(suffix + CHAR + prefix, i); + } + } + } + } + + int f(string pref, string suff) { + return trie.search(suff + CHAR + pref); + } +}; +``` + +```javascript +class TrieNode { + constructor() { + this.children = Array(27).fill(null); + this.index = -1; + } +} + +class Trie { + constructor() { + this.root = new TrieNode(); + } + + /** + * @param {string} word + * @param {number} i + * @return {void} + */ + addWord(word, i) { + let cur = this.root; + for (const ch of word) { + const c = ch === '{' ? 26 : ch.charCodeAt(0) - 'a'.charCodeAt(0); + if (!cur.children[c]) { + cur.children[c] = new TrieNode(); + } + cur = cur.children[c]; + } + cur.index = i; + } + + /** + * @param {string} word + * @return {number} + */ + search(word) { + let cur = this.root; + for (const ch of word) { + const c = ch === '{' ? 26 : ch.charCodeAt(0) - 'a'.charCodeAt(0); + if (!cur.children[c]) { + return -1; + } + cur = cur.children[c]; + } + return cur.index; + } +} + +class WordFilter { + /** + * @constructor + * @param {string[]} words + */ + constructor(words) { + this.trie = new Trie(); + this.CHAR = '{'; + for (let i = 0; i < words.length; i++) { + const word = words[i]; + const wLen = word.length; + for (let j = 0; j < wLen; j++) { + const suffix = word.substring(j); + for (let k = 0; k <= wLen; k++) { + const prefix = word.substring(0, k); + this.trie.addWord(suffix + this.CHAR + prefix, i); + } + } + } + } + + /** + * @param {string} pref + * @param {string} suff + * @return {number} + */ + f(pref, suff) { + return this.trie.search(suff + this.CHAR + pref); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: + * $O(n * m ^ 3)$ time for initialization. + * $O(m)$ for each $f()$ function call. +* Space complexity: $O(n * m ^ 3)$ + +> Where $n$ is the number of words and $m$ is the average length of each word. \ No newline at end of file diff --git a/articles/search-in-a-binary-search-tree.md b/articles/search-in-a-binary-search-tree.md new file mode 100644 index 000000000..548c56821 --- /dev/null +++ b/articles/search-in-a-binary-search-tree.md @@ -0,0 +1,205 @@ +## 1. Recursion + +::tabs-start + +```python +# 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 +class Solution: + def searchBST(self, root: Optional[TreeNode], val: int) -> Optional[TreeNode]: + if not root or root.val == val: + return root + return self.searchBST(root.left, val) if val < root.val else self.searchBST(root.right, val) +``` + +```java +/** + * 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; + * } + * } + */ +public class Solution { + public TreeNode searchBST(TreeNode root, int val) { + if (root == null || root.val == val) { + return root; + } + return val < root.val ? searchBST(root.left, val) : searchBST(root.right, val); + } +} +``` + +```cpp +/** + * 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* searchBST(TreeNode* root, int val) { + if (!root || root->val == val) { + return root; + } + return val < root->val ? searchBST(root->left, val) : searchBST(root->right, val); + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @param {number} val + * @return {TreeNode} + */ + searchBST(root, val) { + if (!root || root.val === val) { + return root; + } + return val < root.val ? this.searchBST(root.left, val) : this.searchBST(root.right, val); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(H)$ +* Space complexity: $O(H)$ for recursion stack. + +> Where $H$ is the height of the given tree. + +--- + +## 2. Iteration + +::tabs-start + +```python +# 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 +class Solution: + def searchBST(self, root: Optional[TreeNode], val: int) -> Optional[TreeNode]: + while root and root.val != val: + root = root.left if val < root.val else root.right + return root +``` + +```java +/** + * 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; + * } + * } + */ +public class Solution { + public TreeNode searchBST(TreeNode root, int val) { + while (root != null && root.val != val) { + root = val < root.val ? root.left : root.right; + } + return root; + } +} +``` + +```cpp +/** + * 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* searchBST(TreeNode* root, int val) { + while (root && root->val != val) { + root = val < root->val ? root->left : root->right; + } + return root; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @param {number} val + * @return {TreeNode} + */ + searchBST(root, val) { + while (root != null && root.val != val) { + root = val < root.val ? root.left : root.right; + } + return root; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(H)$ +* Space complexity: $O(1)$ extra space. + +> Where $H$ is the height of the given tree. \ No newline at end of file diff --git a/articles/shortest-common-supersequence.md b/articles/shortest-common-supersequence.md new file mode 100644 index 000000000..6390ec39d --- /dev/null +++ b/articles/shortest-common-supersequence.md @@ -0,0 +1,803 @@ +## 1. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def shortestCommonSupersequence(self, str1: str, str2: str) -> str: + cache = [[None] * (len(str2) + 1) for _ in range(len(str1) + 1)] + str1 = list(str1) + str2 = list(str2) + + def dfs(i: int, j: int) -> list: + if cache[i][j] is not None: + return cache[i][j] + if i == len(str1): + cache[i][j] = str2[j:][::-1] + return cache[i][j] + if j == len(str2): + cache[i][j] = str1[i:][::-1] + return cache[i][j] + + if str1[i] == str2[j]: + res = dfs(i + 1, j + 1) + [str1[i]] + else: + s1 = dfs(i + 1, j) + s2 = dfs(i, j + 1) + if len(s1) < len(s2): + res = s1 + [str1[i]] + else: + res = s2 + [str2[j]] + + cache[i][j] = res + return res + + return ''.join(reversed(dfs(0, 0))) +``` + +```java +public class Solution { + private List[][] cache; + private int n, m; + + public String shortestCommonSupersequence(String str1, String str2) { + n = str1.length(); + m = str2.length(); + cache = new ArrayList[n + 1][m + 1]; + + List res = dfs(0, 0, str1, str2); + StringBuilder sb = new StringBuilder(); + for (int k = res.size() - 1; k >= 0; k--) sb.append(res.get(k)); + return sb.toString(); + } + + private List dfs(int i, int j, String str1, String str2) { + if (cache[i][j] != null) return cache[i][j]; + if (i == n) { + List res = new ArrayList<>(); + for (int k = m - 1; k >= j; k--) { + res.add(str2.charAt(k)); + } + cache[i][j] = res; + return res; + } + if (j == m) { + List res = new ArrayList<>(); + for (int k = n - 1; k >= i; k--) { + res.add(str1.charAt(k)); + } + cache[i][j] = res; + return res; + } + + List res; + if (str1.charAt(i) == str2.charAt(j)) { + res = new ArrayList<>(dfs(i + 1, j + 1, str1, str2)); + res.add(str1.charAt(i)); + } else { + List s1 = dfs(i + 1, j, str1, str2); + List s2 = dfs(i, j + 1, str1, str2); + + if (s1.size() < s2.size()) { + res = new ArrayList<>(s1); + res.add(str1.charAt(i)); + } else { + res = new ArrayList<>(s2); + res.add(str2.charAt(j)); + } + } + + cache[i][j] = res; + return res; + } +} +``` + +```cpp +class Solution { +public: + string shortestCommonSupersequence(const string &str1, const string &str2) { + n = str1.size(); + m = str2.size(); + cache.resize(n + 1, vector(m + 1, "")); + cacheUsed.resize(n + 1, vector(m + 1, false)); + + string res = dfs(0, 0, str1, str2); + reverse(res.begin(), res.end()); + return res; + } + +private: + int n, m; + vector> cache; + vector> cacheUsed; + + string dfs(int i, int j, const string &str1, const string &str2) { + if (cacheUsed[i][j]) { + return cache[i][j]; + } + cacheUsed[i][j] = true; + + if (i == n) { + string tail = str2.substr(j); + reverse(tail.begin(), tail.end()); + cache[i][j] = tail; + return tail; + } + if (j == m) { + string tail = str1.substr(i); + reverse(tail.begin(), tail.end()); + cache[i][j] = tail; + return tail; + } + + if (str1[i] == str2[j]) { + string temp = dfs(i + 1, j + 1, str1, str2); + temp.push_back(str1[i]); + cache[i][j] = temp; + } else { + string s1 = dfs(i + 1, j, str1, str2); + string s2 = dfs(i, j + 1, str1, str2); + if (s1.size() < s2.size()) { + s1.push_back(str1[i]); + cache[i][j] = s1; + } else { + s2.push_back(str2[j]); + cache[i][j] = s2; + } + } + return cache[i][j]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} str1 + * @param {string} str2 + * @return {string} + */ + shortestCommonSupersequence(str1, str2) { + const n = str1.length, m = str2.length; + const cache = Array.from({ length: n + 1 }, () => + Array(m + 1).fill(null) + ); + + const dfs = (i, j) => { + if (cache[i][j] !== null) return cache[i][j]; + if (i === n) { + let arr = str2.slice(j).split(''); + arr.reverse(); + cache[i][j] = arr; + return arr; + } + if (j === m) { + let arr = str1.slice(i).split(''); + arr.reverse(); + cache[i][j] = arr; + return arr; + } + let res; + if (str1[i] === str2[j]) { + res = [...dfs(i + 1, j + 1)]; + res.push(str1[i]); + } else { + const s1 = dfs(i + 1, j); + const s2 = dfs(i, j + 1); + if (s1.length < s2.length) { + res = [...s1]; + res.push(str1[i]); + } else { + res = [...s2]; + res.push(str2[j]); + } + } + cache[i][j] = res; + return res; + }; + + return dfs(0, 0).reverse().join(''); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * m * min(n, m))$ +* Space complexity: $O(n * m * min(n, m))$ + +> Where $n$ and $m$ are the lengths of the strings $str1$ and $str2$ respectively. + +--- + +## 2. Dynamic Programming (Top-Down) + Tracing + +::tabs-start + +```python +class Solution: + def shortestCommonSupersequence(self, str1: str, str2: str) -> str: + n, m = len(str1), len(str2) + dp = [[-1] * (m + 1) for _ in range(n + 1)] + + def dfs(i, j): + if dp[i][j] != -1: + return dp[i][j] + if i == n: + dp[i][j] = m - j + return dp[i][j] + if j == m: + dp[i][j] = n - i + return dp[i][j] + if str1[i] == str2[j]: + dp[i][j] = 1 + dfs(i + 1, j + 1) + else: + dp[i][j] = 1 + min(dfs(i + 1, j), dfs(i, j + 1)) + return dp[i][j] + + dfs(0, 0) + + def build_scs(i, j): + res = [] + while i < n or j < m: + if i == n: + res.extend(str2[j:]) + break + if j == m: + res.extend(str1[i:]) + break + if str1[i] == str2[j]: + res.append(str1[i]) + i += 1 + j += 1 + elif dp[i + 1][j] < dp[i][j + 1]: + res.append(str1[i]) + i += 1 + else: + res.append(str2[j]) + j += 1 + return res + + return ''.join(build_scs(0, 0)) +``` + +```java +public class Solution { + private int[][] dp; + private int n, m; + + public String shortestCommonSupersequence(String str1, String str2) { + n = str1.length(); + m = str2.length(); + dp = new int[n + 1][m + 1]; + + for (int[] row : dp) { + Arrays.fill(row, -1); + } + + dfs(0, 0, str1, str2); + + return buildSCS(str1, str2); + } + + private int dfs(int i, int j, String str1, String str2) { + if (dp[i][j] != -1) return dp[i][j]; + if (i == n) return dp[i][j] = m - j; + if (j == m) return dp[i][j] = n - i; + + if (str1.charAt(i) == str2.charAt(j)) { + dp[i][j] = 1 + dfs(i + 1, j + 1, str1, str2); + } else { + dp[i][j] = 1 + Math.min(dfs(i + 1, j, str1, str2), dfs(i, j + 1, str1, str2)); + } + return dp[i][j]; + } + + private String buildSCS(String str1, String str2) { + StringBuilder res = new StringBuilder(); + int i = 0, j = 0; + + while (i < n || j < m) { + if (i == n) { + res.append(str2.substring(j)); + break; + } + if (j == m) { + res.append(str1.substring(i)); + break; + } + if (str1.charAt(i) == str2.charAt(j)) { + res.append(str1.charAt(i)); + i++; + j++; + } else if (dp[i + 1][j] < dp[i][j + 1]) { + res.append(str1.charAt(i)); + i++; + } else { + res.append(str2.charAt(j)); + j++; + } + } + + return res.toString(); + } +} +``` + +```cpp +class Solution { +private: + vector> dp; + int n, m; + + int dfs(int i, int j, const string& str1, const string& str2) { + if (dp[i][j] != -1) return dp[i][j]; + if (i == n) return dp[i][j] = m - j; + if (j == m) return dp[i][j] = n - i; + + if (str1[i] == str2[j]) { + dp[i][j] = 1 + dfs(i + 1, j + 1, str1, str2); + } else { + dp[i][j] = 1 + min(dfs(i + 1, j, str1, str2), dfs(i, j + 1, str1, str2)); + } + return dp[i][j]; + } + + string buildSCS(const string& str1, const string& str2) { + string res; + int i = 0, j = 0; + + while (i < n || j < m) { + if (i == n) { + res += str2.substr(j); + break; + } + if (j == m) { + res += str1.substr(i); + break; + } + if (str1[i] == str2[j]) { + res += str1[i]; + i++; + j++; + } else if (dp[i + 1][j] < dp[i][j + 1]) { + res += str1[i]; + i++; + } else { + res += str2[j]; + j++; + } + } + + return res; + } + +public: + string shortestCommonSupersequence(string str1, string str2) { + n = str1.size(); + m = str2.size(); + dp = vector>(n + 1, vector(m + 1, -1)); + + dfs(0, 0, str1, str2); + return buildSCS(str1, str2); + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} str1 + * @param {string} str2 + * @return {string} + */ + shortestCommonSupersequence(str1, str2) { + const n = str1.length, m = str2.length; + const dp = Array.from({ length: n + 1 }, () => + Array(m + 1).fill(-1) + ); + + const dfs = (i, j) => { + if (dp[i][j] !== -1) return dp[i][j]; + if (i === n) return (dp[i][j] = m - j); + if (j === m) return (dp[i][j] = n - i); + + if (str1[i] === str2[j]) { + dp[i][j] = 1 + dfs(i + 1, j + 1); + } else { + dp[i][j] = 1 + Math.min(dfs(i + 1, j), dfs(i, j + 1)); + } + return dp[i][j]; + }; + + dfs(0, 0); + + const buildSCS = () => { + const res = []; + let i = 0, j = 0; + + while (i < n || j < m) { + if (i === n) { + res.push(...str2.slice(j)); + break; + } + if (j === m) { + res.push(...str1.slice(i)); + break; + } + if (str1[i] === str2[j]) { + res.push(str1[i]); + i++; + j++; + } else if (dp[i + 1][j] < dp[i][j + 1]) { + res.push(str1[i]); + i++; + } else { + res.push(str2[j]); + j++; + } + } + + return res.join(''); + }; + + return buildSCS(); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * m)$ +* Space complexity: $O(n * m)$ + +> Where $n$ and $m$ are the lengths of the strings $str1$ and $str2$ respectively. + +--- + +## 3. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def shortestCommonSupersequence(self, str1: str, str2: str) -> str: + n, m = len(str1), len(str2) + dp = [[""] * (m + 1) for _ in range(n + 1)] + + for i in range(n + 1): + for j in range(m + 1): + if i == 0: + dp[i][j] = str2[:j] + elif j == 0: + dp[i][j] = str1[:i] + elif str1[i - 1] == str2[j - 1]: + dp[i][j] = dp[i - 1][j - 1] + str1[i - 1] + else: + if len(dp[i - 1][j]) < len(dp[i][j - 1]): + dp[i][j] = dp[i - 1][j] + str1[i - 1] + else: + dp[i][j] = dp[i][j - 1] + str2[j - 1] + + return dp[n][m] +``` + +```java +public class Solution { + public String shortestCommonSupersequence(String str1, String str2) { + int n = str1.length(), m = str2.length(); + String[][] dp = new String[n + 1][m + 1]; + + for (int i = 0; i <= n; i++) { + for (int j = 0; j <= m; j++) { + if (i == 0) { + dp[i][j] = str2.substring(0, j); + } else if (j == 0) { + dp[i][j] = str1.substring(0, i); + } else if (str1.charAt(i - 1) == str2.charAt(j - 1)) { + dp[i][j] = dp[i - 1][j - 1] + str1.charAt(i - 1); + } else { + dp[i][j] = dp[i - 1][j].length() < dp[i][j - 1].length() ? + dp[i - 1][j] + str1.charAt(i - 1) : + dp[i][j - 1] + str2.charAt(j - 1); + } + } + } + + return dp[n][m]; + } +} +``` + +```cpp +class Solution { +public: + string shortestCommonSupersequence(string str1, string str2) { + int n = str1.size(), m = str2.size(); + vector> dp(n + 1, vector(m + 1)); + + for (int i = 0; i <= n; i++) { + for (int j = 0; j <= m; j++) { + if (i == 0) { + dp[i][j] = str2.substr(0, j); + } else if (j == 0) { + dp[i][j] = str1.substr(0, i); + } else if (str1[i - 1] == str2[j - 1]) { + dp[i][j] = dp[i - 1][j - 1] + str1[i - 1]; + } else { + dp[i][j] = dp[i - 1][j].size() < dp[i][j - 1].size() ? + dp[i - 1][j] + str1[i - 1] : + dp[i][j - 1] + str2[j - 1]; + } + } + } + + return dp[n][m]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} str1 + * @param {string} str2 + * @return {string} + */ + shortestCommonSupersequence(str1, str2) { + const n = str1.length, m = str2.length; + const dp = Array.from({ length: n + 1 }, () => + Array(m + 1).fill("") + ); + + for (let i = 0; i <= n; i++) { + for (let j = 0; j <= m; j++) { + if (i === 0) { + dp[i][j] = str2.slice(0, j); + } else if (j === 0) { + dp[i][j] = str1.slice(0, i); + } else if (str1[i - 1] === str2[j - 1]) { + dp[i][j] = dp[i - 1][j - 1] + str1[i - 1]; + } else { + dp[i][j] = dp[i - 1][j].length < dp[i][j - 1].length ? + dp[i - 1][j] + str1[i - 1] : + dp[i][j - 1] + str2[j - 1]; + } + } + } + + return dp[n][m]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * m * min(n, m))$ +* Space complexity: $O(n * m * min(n, m))$ + +> Where $n$ and $m$ are the lengths of the strings $str1$ and $str2$ respectively. + +--- + +## 4. Dynamic Programming (Bottom-Up) + Tracing + +::tabs-start + +```python +class Solution: + def shortestCommonSupersequence(self, str1: str, str2: str) -> str: + n, m = len(str1), len(str2) + dp = [[0] * (m + 1) for _ in range(n + 1)] + + for i in range(n + 1): + for j in range(m + 1): + if i == 0: + dp[i][j] = j + elif j == 0: + dp[i][j] = i + elif str1[i - 1] == str2[j - 1]: + dp[i][j] = 1 + dp[i - 1][j - 1] + else: + dp[i][j] = 1 + min(dp[i - 1][j], dp[i][j - 1]) + + res = [] + i, j = n, m + while i > 0 and j > 0: + if str1[i - 1] == str2[j - 1]: + res.append(str1[i - 1]) + i -= 1 + j -= 1 + elif dp[i - 1][j] < dp[i][j - 1]: + res.append(str1[i - 1]) + i -= 1 + else: + res.append(str2[j - 1]) + j -= 1 + + while i > 0: + res.append(str1[i - 1]) + i -= 1 + + while j > 0: + res.append(str2[j - 1]) + j -= 1 + + return ''.join(reversed(res)) +``` + +```java +public class Solution { + public String shortestCommonSupersequence(String str1, String str2) { + int n = str1.length(), m = str2.length(); + int[][] dp = new int[n + 1][m + 1]; + + for (int i = 0; i <= n; i++) { + for (int j = 0; j <= m; j++) { + if (i == 0) { + dp[i][j] = j; + } else if (j == 0) { + dp[i][j] = i; + } else if (str1.charAt(i - 1) == str2.charAt(j - 1)) { + dp[i][j] = 1 + dp[i - 1][j - 1]; + } else { + dp[i][j] = 1 + Math.min(dp[i - 1][j], dp[i][j - 1]); + } + } + } + + StringBuilder res = new StringBuilder(); + int i = n, j = m; + while (i > 0 && j > 0) { + if (str1.charAt(i - 1) == str2.charAt(j - 1)) { + res.append(str1.charAt(i - 1)); + i--; + j--; + } else if (dp[i - 1][j] < dp[i][j - 1]) { + res.append(str1.charAt(i - 1)); + i--; + } else { + res.append(str2.charAt(j - 1)); + j--; + } + } + while (i > 0) { + res.append(str1.charAt(i - 1)); + i--; + } + while (j > 0) { + res.append(str2.charAt(j - 1)); + j--; + } + + return res.reverse().toString(); + } +} +``` + +```cpp +class Solution { +public: + string shortestCommonSupersequence(string str1, string str2) { + int n = str1.size(), m = str2.size(); + vector> dp(n + 1, vector(m + 1, 0)); + + for (int i = 0; i <= n; i++) { + for (int j = 0; j <= m; j++) { + if (i == 0) { + dp[i][j] = j; + } else if (j == 0) { + dp[i][j] = i; + } else if (str1[i - 1] == str2[j - 1]) { + dp[i][j] = 1 + dp[i - 1][j - 1]; + } else { + dp[i][j] = 1 + min(dp[i - 1][j], dp[i][j - 1]); + } + } + } + + string res; + int i = n, j = m; + while (i > 0 && j > 0) { + if (str1[i - 1] == str2[j - 1]) { + res.push_back(str1[i - 1]); + i--; + j--; + } else if (dp[i - 1][j] < dp[i][j - 1]) { + res.push_back(str1[i - 1]); + i--; + } else { + res.push_back(str2[j - 1]); + j--; + } + } + + while (i > 0) { + res.push_back(str1[i - 1]); + i--; + } + + while (j > 0) { + res.push_back(str2[j - 1]); + j--; + } + + reverse(res.begin(), res.end()); + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} str1 + * @param {string} str2 + * @return {string} + */ + shortestCommonSupersequence(str1, str2) { + const n = str1.length, m = str2.length; + const dp = Array.from({ length: n + 1 }, () => + Array(m + 1).fill(0) + ); + + for (let i = 0; i <= n; i++) { + for (let j = 0; j <= m; j++) { + if (i === 0) { + dp[i][j] = j; + } else if (j === 0) { + dp[i][j] = i; + } else if (str1[i - 1] === str2[j - 1]) { + dp[i][j] = 1 + dp[i - 1][j - 1]; + } else { + dp[i][j] = 1 + Math.min(dp[i - 1][j], dp[i][j - 1]); + } + } + } + + const res = []; + let i = n, j = m; + while (i > 0 && j > 0) { + if (str1[i - 1] === str2[j - 1]) { + res.push(str1[i - 1]); + i--; + j--; + } else if (dp[i - 1][j] < dp[i][j - 1]) { + res.push(str1[i - 1]); + i--; + } else { + res.push(str2[j - 1]); + j--; + } + } + + while (i > 0) { + res.push(str1[i - 1]); + i--; + } + + while (j > 0) { + res.push(str2[j - 1]); + j--; + } + + return res.reverse().join(''); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * m)$ +* Space complexity: $O(n * m)$ + +> Where $n$ and $m$ are the lengths of the strings $str1$ and $str2$ respectively. \ No newline at end of file diff --git a/articles/sliding-window-median.md b/articles/sliding-window-median.md new file mode 100644 index 000000000..e328fe000 --- /dev/null +++ b/articles/sliding-window-median.md @@ -0,0 +1,465 @@ +## 1. Brute Force (Sorting) + +::tabs-start + +```python +class Solution: + def medianSlidingWindow(self, nums: List[int], k: int) -> List[float]: + res = [] + for i in range(len(nums) - k + 1): + tmp = nums[i:i + k][:] + tmp.sort() + if k & 1: + res.append(tmp[k // 2]) + else: + res.append((tmp[k // 2] + tmp[(k - 1) // 2]) / 2) + return res +``` + +```java +public class Solution { + public double[] medianSlidingWindow(int[] nums, int k) { + int n = nums.length - k + 1; + double[] res = new double[n]; + for (int i = 0; i < n; i++) { + int[] tmp = Arrays.copyOfRange(nums, i, i + k); + Arrays.sort(tmp); + if (k % 2 == 1) { + res[i] = tmp[k / 2]; + } else { + res[i] = (tmp[k / 2] + 0L + tmp[(k - 1) / 2]) / 2.0; + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + vector medianSlidingWindow(vector& nums, int k) { + vector res; + for (int i = 0; i <= nums.size() - k; ++i) { + vector tmp(nums.begin() + i, nums.begin() + i + k); + sort(tmp.begin(), tmp.end()); + if (k % 2 == 1) { + res.push_back(tmp[k / 2]); + } else { + res.push_back((tmp[k / 2] + 0LL + tmp[(k - 1) / 2]) / 2.0); + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {number[]} + */ + medianSlidingWindow(nums, k) { + const res = []; + for (let i = 0; i <= nums.length - k; i++) { + const tmp = nums.slice(i, i + k).sort((a, b) => a - b); + if (k % 2 === 1) { + res.push(tmp[Math.floor(k / 2)]); + } else { + res.push((tmp[Math.floor(k / 2)] + tmp[Math.floor((k - 1) / 2)]) / 2); + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * k\log k)$ +* Space complexity: + * $O(k)$ extra space. + * $O(n - k + 1)$ space for output array. + +> Where $n$ is the size of the array $nums$ and $k$ is the size of the sliding window. + +--- + +## 2. Two Heaps + +::tabs-start + +```python +class Solution: + def medianSlidingWindow(self, nums: List[int], k: int) -> List[float]: + small, large = [], [] + d = defaultdict(int) + + for i in range(k): + heapq.heappush(small, -nums[i]) + for i in range(k // 2): + heapq.heappush(large, -heapq.heappop(small)) + + res = [-small[0] if k & 1 else (large[0] - small[0]) / 2] + for i in range(k, len(nums)): + d[nums[i - k]] += 1 + balance = -1 if small and nums[i - k] <= -small[0] else 1 + + if small and nums[i] <= -small[0]: + heapq.heappush(small, -nums[i]) + balance += 1 + else: + heapq.heappush(large, nums[i]) + balance -= 1 + + if balance > 0: + heapq.heappush(large, -heapq.heappop(small)) + if balance < 0: + heapq.heappush(small, -heapq.heappop(large)) + + while small and d[-small[0]] > 0: + d[-heapq.heappop(small)] -= 1 + + while large and d[large[0]] > 0: + d[heapq.heappop(large)] -= 1 + + res.append(-small[0] if k & 1 else (large[0] - small[0]) / 2) + + return res +``` + +```java +public class Solution { + public double[] medianSlidingWindow(int[] nums, int k) { + PriorityQueue small = new PriorityQueue<>(Collections.reverseOrder()); + PriorityQueue large = new PriorityQueue<>(); + Map d = new HashMap<>(); + + for (int i = 0; i < k; i++) { + small.add(nums[i]); + } + for (int i = 0; i < k / 2; i++) { + large.add(small.poll()); + } + + double[] res = new double[nums.length - k + 1]; + res[0] = k % 2 == 1 ? small.peek() : (large.peek() + 0L + small.peek()) / 2.0; + for (int i = k; i < nums.length; i++) { + d.put(nums[i - k], d.getOrDefault(nums[i - k], 0) + 1); + int balance = (small.size() > 0 && nums[i - k] <= small.peek()) ? -1 : 1; + + if (nums[i] <= small.peek()) { + small.add(nums[i]); + balance++; + } else { + large.add(nums[i]); + balance--; + } + + if (balance > 0) { + large.add(small.poll()); + } + if (balance < 0) { + small.add(large.poll()); + } + + while (!small.isEmpty() && d.getOrDefault(small.peek(), 0) > 0) { + d.put(small.peek(), d.get(small.peek()) - 1); + small.poll(); + } + while (!large.isEmpty() && d.getOrDefault(large.peek(), 0) > 0) { + d.put(large.peek(), d.get(large.peek()) - 1); + large.poll(); + } + + res[i - k + 1] = k % 2 == 1 ? small.peek() : (large.peek() + 0L + small.peek()) / 2.0; + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector medianSlidingWindow(vector& nums, int k) { + priority_queue small; + priority_queue, greater> large; + unordered_map d; + + for (int i = 0; i < k; ++i) { + small.push(nums[i]); + } + for (int i = 0; i < k / 2; ++i) { + large.push(small.top()); + small.pop(); + } + + vector res; + res.push_back(k & 1 ? small.top() : (large.top() + 0LL + small.top()) / 2.0); + for (int i = k; i < nums.size(); ++i) { + d[nums[i - k]]++; + int balance = small.size() > 0 && nums[i - k] <= small.top() ? -1 : 1; + + if (nums[i] <= small.top()) { + small.push(nums[i]); + balance++; + } else { + large.push(nums[i]); + balance--; + } + + if (balance > 0) { + large.push(small.top()); + small.pop(); + } + if (balance < 0) { + small.push(large.top()); + large.pop(); + } + + while (!small.empty() && d[small.top()] > 0) { + d[small.top()]--; + small.pop(); + } + + while (!large.empty() && d[large.top()] > 0) { + d[large.top()]--; + large.pop(); + } + + res.push_back(k & 1 ? small.top() : (large.top() + 0LL + small.top()) / 2.0); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {number[]} + */ + medianSlidingWindow(nums, k) { + const small = new MaxPriorityQueue({ compare: (a, b) => b - a }); + const large = new MinPriorityQueue({ compare: (a, b) => a - b }); + const d = new Map(); + + for (let i = 0; i < k; i++) { + small.enqueue(nums[i]); + } + for (let i = 0; i < Math.floor(k / 2); i++) { + large.enqueue(small.dequeue()); + } + + const res = [k % 2 === 1 ? small.front() : (large.front() + small.front()) / 2]; + for (let i = k; i < nums.length; i++) { + const toRemove = nums[i - k]; + d.set(toRemove, (d.get(toRemove) || 0) + 1); + let balance = small.size() > 0 && toRemove <= small.front() ? -1 : 1; + + if (nums[i] <= small.front()) { + small.enqueue(nums[i]); + balance++; + } else { + large.enqueue(nums[i]); + balance--; + } + + if (balance > 0) { + large.enqueue(small.dequeue()); + } + if (balance < 0) { + small.enqueue(large.dequeue()); + } + + while (small.size() > 0 && d.get(small.front()) > 0) { + d.set(small.front(), d.get(small.front()) - 1); + small.dequeue(); + } + while (large.size() > 0 && d.get(large.front()) > 0) { + d.set(large.front(), d.get(large.front()) - 1); + large.dequeue(); + } + + res.push(k % 2 === 1 ? small.front() : (large.front() + small.front()) / 2); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: + * $O(n)$ extra space. + * $O(n - k + 1)$ space for output array. + +> Where $n$ is the size of the array $nums$ and $k$ is the size of the sliding window. + +--- + +## 3. Two Multisets + +::tabs-start + +```python +class Solution: + def medianSlidingWindow(self, nums: List[int], k: int) -> List[float]: + small, large = SortedList(), SortedList() + res = [] + for i in range(len(nums)): + if len(small) == 0 or nums[i] <= small[-1]: + small.add(nums[i]) + else: + large.add(nums[i]) + if i >= k: + if nums[i - k] in small: + small.remove(nums[i - k]) + else: + large.remove(nums[i - k]) + if len(small) > len(large) + 1: + large.add(small.pop()) + if len(large) > len(small): + small.add(large.pop(0)) + if i >= k - 1: + res.append(small[-1] if k & 1 else (small[-1] + large[0]) / 2) + return res +``` + +```java +public class Solution { + public double[] medianSlidingWindow(int[] nums, int k) { + TreeSet small = new TreeSet<>((a, b) -> nums[a] != nums[b] ? Integer.compare(nums[a], nums[b]) : Integer.compare(a, b)); + TreeSet large = new TreeSet<>((a, b) -> nums[a] != nums[b] ? Integer.compare(nums[a], nums[b]) : Integer.compare(a, b)); + double[] res = new double[nums.length - k + 1]; + for (int i = 0; i < nums.length; i++) { + if (small.isEmpty() || nums[i] <= nums[small.last()]) small.add(i); + else large.add(i); + if (i >= k) { + if (small.contains(i - k)) small.remove(i - k); + else large.remove(i - k); + } + while (small.size() > large.size() + 1) large.add(small.pollLast()); + while (large.size() > small.size()) small.add(large.pollFirst()); + if (i >= k - 1) { + res[i - k + 1] = k % 2 == 1 ? nums[small.last()] : (nums[small.last()] + 0L + nums[large.first()]) / 2.0; + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + vector medianSlidingWindow(vector& nums, int k) { + multiset small, large; + vector res; + for (int i = 0; i < nums.size(); ++i) { + if (small.empty() || nums[i] <= *small.rbegin()) small.insert(nums[i]); + else large.insert(nums[i]); + if (i >= k) { + if (small.count(nums[i - k])) small.erase(small.find(nums[i - k])); + else large.erase(large.find(nums[i - k])); + } + if (small.size() > large.size() + 1) { + large.insert(*small.rbegin()); + small.erase(prev(small.end())); + } + if (large.size() > small.size()) { + small.insert(*large.begin()); + large.erase(large.begin()); + } + if (i >= k - 1) { + res.push_back(k % 2 == 1 ? *small.rbegin() : (*small.rbegin() + 0LL + *large.begin()) / 2.0); + } + } + return res; + } +}; +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log k)$ +* Space complexity: + * $O(k)$ extra space. + * $O(n - k + 1)$ space for output array. + +> Where $n$ is the size of the array $nums$ and $k$ is the size of the sliding window. + +--- + +## 4. Multiset + +::tabs-start + +```python +class Solution: + def medianSlidingWindow(self, nums: List[int], k: int) -> List[float]: + window = SortedList() + res = [] + for i in range(len(nums)): + window.add(nums[i]) + if i >= k: + window.remove(nums[i - k]) + if i >= k - 1: + if k % 2 == 1: + res.append(float(window[k // 2])) + else: + res.append((window[k // 2 - 1] + window[k // 2]) / 2) + return res +``` + +```cpp +class Solution { +public: + vector medianSlidingWindow(vector& nums, int k) { + multiset window(nums.begin(), nums.begin() + k); + auto mid = next(window.begin(), k / 2); + vector res; + + for (int i = k;; i++) { + if (k & 1) { + res.push_back(*mid); + } else { + res.push_back((*mid + 0LL + *prev(mid)) / 2.0); + } + + if (i == nums.size()) return res; + + window.insert(nums[i]); + if (nums[i] < *mid) mid--; + if (nums[i - k] <= *mid) mid++; + window.erase(window.lower_bound(nums[i - k])); + } + } +}; +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log k)$ +* Space complexity: + * $O(k)$ extra space. + * $O(n - k + 1)$ space for output array. + +> Where $n$ is the size of the array $nums$ and $k$ is the size of the sliding window. \ No newline at end of file diff --git a/articles/sort-items-by-groups-respecting-dependencies.md b/articles/sort-items-by-groups-respecting-dependencies.md new file mode 100644 index 000000000..69ce62c62 --- /dev/null +++ b/articles/sort-items-by-groups-respecting-dependencies.md @@ -0,0 +1,562 @@ +## 1. Topological Sort (DFS) + +::tabs-start + +```python +class Solution: + def sortItems(self, n, m, group, beforeItems): + for i in range(n): + if group[i] == -1: + group[i] = m + m += 1 + + item_adj = defaultdict(list) + group_adj = defaultdict(list) + for i in range(n): + for par in beforeItems[i]: + item_adj[par].append(i) + if group[i] != group[par]: + group_adj[group[par]].append(group[i]) + + itm = self.topo_sort(item_adj, n) + if not itm: return [] + grp = self.topo_sort(group_adj, m) + if not grp: return [] + + grouping = defaultdict(list) + for i in itm: + grouping[group[i]].append(i) + + res = [] + for g in grp: + res.extend(grouping[g]) + + return res + + def topo_sort(self, adj, N): + visited = [0] * N + topo = [] + + def dfs(node): + if visited[node] == 1: + return True + if visited[node] == 2: + return False + visited[node] = 1 + for neighbor in adj[node]: + if dfs(neighbor): + return True + topo.append(node) + visited[node] = 2 + return False + + for i in range(N): + if visited[i] == 0: + if dfs(i): + return [] + + return topo[::-1] +``` + +```java +public class Solution { + public int[] sortItems(int n, int m, int[] group, List> beforeItems) { + for (int i = 0; i < n; i++) { + if (group[i] == -1) { + group[i] = m++; + } + } + + List[] itemAdj = new ArrayList[n]; + List[] groupAdj = new ArrayList[m]; + for (int i = 0; i < n; i++) { + itemAdj[i] = new ArrayList<>(); + } + for (int i = 0; i < m; i++) { + groupAdj[i] = new ArrayList<>(); + } + + for (int i = 0; i < n; i++) { + for (int parent : beforeItems.get(i)) { + itemAdj[parent].add(i); + if (group[i] != group[parent]) { + groupAdj[group[parent]].add(group[i]); + } + } + } + + List itm = topoSort(itemAdj, n); + if (itm.isEmpty()) return new int[]{}; + List grp = topoSort(groupAdj, m); + if (grp.isEmpty()) return new int[]{}; + + List[] grouping = new ArrayList[m]; + for (int i = 0; i < m; i++) { + grouping[i] = new ArrayList<>(); + } + for (int i : itm) { + grouping[group[i]].add(i); + } + + List res = new ArrayList<>(); + for (int g : grp) { + res.addAll(grouping[g]); + } + + return res.stream().mapToInt(Integer::intValue).toArray(); + } + + private List topoSort(List[] adj, int N) { + int[] visited = new int[N]; + List topo = new ArrayList<>(); + + for (int i = 0; i < N; i++) { + if (visited[i] == 0) { + if (dfs(i, adj, visited, topo)) { + return new ArrayList<>(); + } + } + } + + Collections.reverse(topo); + return topo; + } + + private boolean dfs(int node, List[] adj, int[] visited, List topo) { + if (visited[node] == 1) return true; + if (visited[node] == 2) return false; + visited[node] = 1; + for (int neighbor : adj[node]) { + if (dfs(neighbor, adj, visited, topo)) { + return true; + } + } + topo.add(node); + visited[node] = 2; + return false; + } +} +``` + +```cpp +class Solution { +public: + vector sortItems(int n, int m, vector& group, vector>& beforeItems) { + for (int i = 0; i < n; ++i) { + if (group[i] == -1) { + group[i] = m++; + } + } + + vector> itemAdj(n), groupAdj(m); + for (int i = 0; i < n; ++i) { + for (int parent : beforeItems[i]) { + itemAdj[parent].push_back(i); + if (group[i] != group[parent]) { + groupAdj[group[parent]].push_back(group[i]); + } + } + } + + vector itm = topoSort(itemAdj, n); + if (itm.empty()) return {}; + vector grp = topoSort(groupAdj, m); + if (grp.empty()) return {}; + + unordered_map> grouping; + for (int i : itm) { + grouping[group[i]].push_back(i); + } + + vector res; + for (int& g : grp) { + res.insert(res.end(), grouping[g].begin(), grouping[g].end()); + } + + return res; + } + +private: + vector topoSort(const vector>& adj, int N) { + vector visited(N, 0), topo; + function dfs = [&](int node) { + if (visited[node] == 1) return true; + if (visited[node] == 2) return false; + visited[node] = 1; + for (int neighbor : adj[node]) { + if (dfs(neighbor)) return true; + } + topo.push_back(node); + visited[node] = 2; + return false; + }; + + for (int i = 0; i < N; ++i) { + if (visited[i] == 0 && dfs(i)) { + return {}; + } + } + + reverse(topo.begin(), topo.end()); + return topo; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number} m + * @param {number[]} group + * @param {number[][]} beforeItems + * @return {number[]} + */ + sortItems(n, m, group, beforeItems) { + for (let i = 0; i < n; i++) { + if (group[i] === -1) { + group[i] = m++; + } + } + + const itemAdj = Array.from({ length: n }, () => []); + const groupAdj = Array.from({ length: m }, () => []); + for (let i = 0; i < n; i++) { + for (const parent of beforeItems[i]) { + itemAdj[parent].push(i); + if (group[i] !== group[parent]) { + groupAdj[group[parent]].push(group[i]); + } + } + } + + const itm = this.topoSort(itemAdj, n); + if (!itm.length) return []; + const grp = this.topoSort(groupAdj, m); + if (!grp.length) return []; + + const grouping = {}; + for (const i of itm) { + if (!grouping[group[i]]) grouping[group[i]] = []; + grouping[group[i]].push(i); + } + + const res = []; + for (const g of grp) { + if (grouping[g]) res.push(...grouping[g]); + } + + return res; + } + + /** + * @param {number[][]} adj + * @param {number} N + * @return {number[]} + */ + topoSort(adj, N) { + const visited = new Array(N).fill(0); + const topo = []; + + const dfs = (node) => { + if (visited[node] === 1) return true; + if (visited[node] === 2) return false; + visited[node] = 1; + for (const neighbor of adj[node]) { + if (dfs(neighbor)) return true; + } + topo.push(node); + visited[node] = 2; + return false; + }; + + for (let i = 0; i < N; i++) { + if (visited[i] === 0 && dfs(i)) { + return []; + } + } + + return topo.reverse(); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(V + E)$ +* Space complexity: $O(V + E)$ + +> Where $V$ is the number of items and $E$ is the total number of $beforeItems$ dependencies. + +--- + +## 2. Topological Sort (Kahn's Algorithm) + +::tabs-start + +```python +class Solution: + def sortItems(self, n, m, group, beforeItems): + for i in range(n): + if group[i] == -1: + group[i] = m + m += 1 + + item_adj = defaultdict(list) + group_adj = defaultdict(list) + item_indegree = [0] * n + group_indegree = [0] * m + + for i in range(n): + for par in beforeItems[i]: + item_adj[par].append(i) + item_indegree[i] += 1 + if group[i] != group[par]: + group_adj[group[par]].append(group[i]) + group_indegree[group[i]] += 1 + + itm = self.topo_sort(item_adj, item_indegree, n) + if not itm: return [] + grp = self.topo_sort(group_adj, group_indegree, m) + if not grp: return [] + + grouping = defaultdict(list) + for i in itm: + grouping[group[i]].append(i) + + res = [] + for g in grp: + res.extend(grouping[g]) + + return res + + def topo_sort(self, adj, indegree, N): + topo = [] + q = deque([i for i in range(N) if indegree[i] == 0]) + + while q: + node = q.popleft() + topo.append(node) + for neighbor in adj[node]: + indegree[neighbor] -= 1 + if indegree[neighbor] == 0: + q.append(neighbor) + + return topo if len(topo) == N else [] +``` + +```java +public class Solution { + public int[] sortItems(int n, int m, int[] group, List> beforeItems) { + for (int i = 0; i < n; i++) { + if (group[i] == -1) { + group[i] = m++; + } + } + + List> itemAdj = new ArrayList<>(); + List> groupAdj = new ArrayList<>(); + for (int i = 0; i < n; i++) itemAdj.add(new ArrayList<>()); + for (int i = 0; i < m; i++) groupAdj.add(new ArrayList<>()); + + int[] itemIndegree = new int[n]; + int[] groupIndegree = new int[m]; + + for (int i = 0; i < n; i++) { + for (int par : beforeItems.get(i)) { + itemAdj.get(par).add(i); + itemIndegree[i]++; + if (group[i] != group[par]) { + groupAdj.get(group[par]).add(group[i]); + groupIndegree[group[i]]++; + } + } + } + + List itm = topoSort(itemAdj, itemIndegree, n); + if (itm.isEmpty()) return new int[0]; + List grp = topoSort(groupAdj, groupIndegree, m); + if (grp.isEmpty()) return new int[0]; + + Map> grouping = new HashMap<>(); + for (int i : itm) { + grouping.computeIfAbsent(group[i], x -> new ArrayList<>()).add(i); + } + + List res = new ArrayList<>(); + for (int g : grp) { + res.addAll(grouping.getOrDefault(g, new ArrayList<>())); + } + + return res.stream().mapToInt(Integer::intValue).toArray(); + } + + private List topoSort(List> adj, int[] indegree, int N) { + Queue q = new LinkedList<>(); + List topo = new ArrayList<>(); + for (int i = 0; i < N; i++) { + if (indegree[i] == 0) q.add(i); + } + + while (!q.isEmpty()) { + int node = q.poll(); + topo.add(node); + for (int neighbor : adj.get(node)) { + indegree[neighbor]--; + if (indegree[neighbor] == 0) q.add(neighbor); + } + } + + return topo.size() == N ? topo : new ArrayList<>(); + } +} +``` + +```cpp +class Solution { +public: + vector sortItems(int n, int m, vector& group, vector>& beforeItems) { + for (int i = 0; i < n; i++) { + if (group[i] == -1) group[i] = m++; + } + + vector> itemAdj(n), groupAdj(m); + vector itemIndegree(n, 0), groupIndegree(m, 0); + + for (int i = 0; i < n; i++) { + for (int& par : beforeItems[i]) { + itemAdj[par].push_back(i); + itemIndegree[i]++; + if (group[i] != group[par]) { + groupAdj[group[par]].push_back(group[i]); + groupIndegree[group[i]]++; + } + } + } + + vector itm = topoSort(itemAdj, itemIndegree, n); + if (itm.empty()) return {}; + vector grp = topoSort(groupAdj, groupIndegree, m); + if (grp.empty()) return {}; + + unordered_map> grouping; + for (int& i : itm) { + grouping[group[i]].push_back(i); + } + + vector res; + for (int& g : grp) { + res.insert(res.end(), grouping[g].begin(), grouping[g].end()); + } + + return res; + } + +private: + vector topoSort(vector>& adj, vector& indegree, int N) { + queue q; + vector topo; + for (int i = 0; i < N; i++) { + if (indegree[i] == 0) q.push(i); + } + + while (!q.empty()) { + int node = q.front(); q.pop(); + topo.push_back(node); + for (int& neighbor : adj[node]) { + indegree[neighbor]--; + if (indegree[neighbor] == 0) q.push(neighbor); + } + } + + return topo.size() == N ? topo : vector(); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number} m + * @param {number[]} group + * @param {number[][]} beforeItems + * @return {number[]} + */ + sortItems(n, m, group, beforeItems) { + for (let i = 0; i < n; i++) { + if (group[i] === -1) group[i] = m++; + } + + const itemAdj = Array.from({ length: n }, () => []); + const groupAdj = Array.from({ length: m }, () => []); + const itemIndegree = Array(n).fill(0); + const groupIndegree = Array(m).fill(0); + + for (let i = 0; i < n; i++) { + for (const par of beforeItems[i]) { + itemAdj[par].push(i); + itemIndegree[i]++; + if (group[i] !== group[par]) { + groupAdj[group[par]].push(group[i]); + groupIndegree[group[i]]++; + } + } + } + + const itm = this.topoSort(itemAdj, itemIndegree, n); + if (itm.length === 0) return []; + const grp = this.topoSort(groupAdj, groupIndegree, m); + if (grp.length === 0) return []; + + const grouping = new Map(); + for (const i of itm) { + if (!grouping.has(group[i])) grouping.set(group[i], []); + grouping.get(group[i]).push(i); + } + + const res = []; + for (const g of grp) { + if (grouping.has(g)) res.push(...grouping.get(g)); + } + + return res; + } + + /** + * @param {number[][]} adj + * @param {number[]} indegree + * @param {number} N + * @return {number[]} + */ + topoSort(adj, indegree, N) { + const q = new Queue(); + const topo = []; + for (let i = 0; i < N; i++) { + if (indegree[i] === 0) q.push(i); + } + + while (!q.isEmpty()) { + const node = q.pop(); + topo.push(node); + for (const neighbor of adj[node]) { + indegree[neighbor]--; + if (indegree[neighbor] === 0) q.push(neighbor); + } + } + + return topo.length === N ? topo : []; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(V + E)$ +* Space complexity: $O(V + E)$ + +> Where $V$ is the number of items and $E$ is the total number of $beforeItems$ dependencies. \ No newline at end of file From 332199e7ceb1fc9f735baec908e205893e5ca0b6 Mon Sep 17 00:00:00 2001 From: Sri Hari <94112314+Srihari2222@users.noreply.github.com> Date: Mon, 20 Jan 2025 01:26:24 +0530 Subject: [PATCH 28/45] Sri Hari: Batch-5/Neetcode-ALL/Added-articles (#3815) * Batch-5/Neetcode-ALL/Added-articles * Batch-5/Neetcode-ALL/Added-articles --- articles/calculate-money-in-leetcode-bank.md | 248 +++++++ .../count-odd-numbers-in-an-interval-range.md | 187 ++++++ articles/count-of-matches-in-tournament.md | 120 ++++ articles/find-missing-observations.md | 210 ++++++ articles/find-the-difference.md | 466 ++++++++++++++ articles/image-smoother.md | 530 +++++++++++++++ articles/largest-local-values-in-a-matrix.md | 439 +++++++++++++ articles/largest-odd-number-in-string.md | 166 +++++ articles/matrix-diagonal-sum.md | 184 ++++++ articles/max-points-on-a-line.md | 384 +++++++++++ articles/palindrome-number.md | 420 ++++++++++++ articles/power-of-four.md | 367 +++++++++++ articles/power-of-two.md | 351 ++++++++++ articles/robot-bounded-in-circle.md | 107 +++ articles/shift-2d-grid.md | 502 +++++++++++++++ articles/shuffle-the-array.md | 257 ++++++++ articles/single-number-iii.md | 608 ++++++++++++++++++ articles/spiral-matrix-ii.md | 454 +++++++++++++ articles/ugly-number.md | 76 +++ articles/zigzag-conversion.md | 226 +++++++ 20 files changed, 6302 insertions(+) create mode 100644 articles/calculate-money-in-leetcode-bank.md create mode 100644 articles/count-odd-numbers-in-an-interval-range.md create mode 100644 articles/count-of-matches-in-tournament.md create mode 100644 articles/find-missing-observations.md create mode 100644 articles/find-the-difference.md create mode 100644 articles/image-smoother.md create mode 100644 articles/largest-local-values-in-a-matrix.md create mode 100644 articles/largest-odd-number-in-string.md create mode 100644 articles/matrix-diagonal-sum.md create mode 100644 articles/max-points-on-a-line.md create mode 100644 articles/palindrome-number.md create mode 100644 articles/power-of-four.md create mode 100644 articles/power-of-two.md create mode 100644 articles/robot-bounded-in-circle.md create mode 100644 articles/shift-2d-grid.md create mode 100644 articles/shuffle-the-array.md create mode 100644 articles/single-number-iii.md create mode 100644 articles/spiral-matrix-ii.md create mode 100644 articles/ugly-number.md create mode 100644 articles/zigzag-conversion.md diff --git a/articles/calculate-money-in-leetcode-bank.md b/articles/calculate-money-in-leetcode-bank.md new file mode 100644 index 000000000..1735a4724 --- /dev/null +++ b/articles/calculate-money-in-leetcode-bank.md @@ -0,0 +1,248 @@ +## 1. Simulation + +::tabs-start + +```python +class Solution: + def totalMoney(self, n: int) -> int: + day, deposit = 0, 1 + res = 0 + + while day < n: + res += deposit + deposit += 1 + day += 1 + + if day % 7 == 0: + deposit = 1 + day // 7 + + return res +``` + +```java +public class Solution { + public int totalMoney(int n) { + int day = 0, deposit = 1, res = 0; + + while (day < n) { + res += deposit; + deposit++; + day++; + + if (day % 7 == 0) { + deposit = 1 + day / 7; + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int totalMoney(int n) { + int day = 0, deposit = 1, res = 0; + + while (day < n) { + res += deposit; + deposit++; + day++; + + if (day % 7 == 0) { + deposit = 1 + day / 7; + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {number} + */ + totalMoney(n) { + let day = 0, deposit = 1, res = 0; + + while (day < n) { + res += deposit; + deposit++; + day++; + + if (day % 7 === 0) { + deposit = 1 + Math.floor(day / 7); + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ + +--- + +## 2. Math + +::tabs-start + +```python +class Solution: + def totalMoney(self, n: int) -> int: + weeks = n // 7 + low = 28 + high = 28 + 7 * (weeks - 1) + res = weeks * (low + high) // 2 + + monday = weeks + 1 + for i in range(n % 7): + res += i + monday + + return res +``` + +```java +public class Solution { + public int totalMoney(int n) { + int weeks = n / 7; + int low = 28; + int high = 28 + 7 * (weeks - 1); + int res = weeks * (low + high) / 2; + + int monday = weeks + 1; + for (int i = 0; i < n % 7; i++) { + res += i + monday; + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int totalMoney(int n) { + int weeks = n / 7; + int low = 28; + int high = 28 + 7 * (weeks - 1); + int res = weeks * (low + high) / 2; + + int monday = weeks + 1; + for (int i = 0; i < n % 7; i++) { + res += i + monday; + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {number} + */ + totalMoney(n) { + const weeks = Math.floor(n / 7); + const low = 28; + const high = 28 + 7 * (weeks - 1); + let res = weeks * (low + high) / 2; + + const monday = weeks + 1; + for (let i = 0; i < n % 7; i++) { + res += i + monday; + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(1)$ +* Space complexity: $O(1)$ + +--- + +## 3. Math (Optimal) + +::tabs-start + +```python +class Solution: + def totalMoney(self, n: int) -> int: + SUM = lambda x: (x * (x + 1)) >> 1 + weeks = n // 7 + res = SUM(weeks - 1) * 7 + weeks * SUM(7) + res += SUM(n % 7) + weeks * (n % 7) + return res +``` + +```java +public class Solution { + public int totalMoney(int n) { + int weeks = n / 7; + int res = SUM(weeks - 1) * 7 + weeks * SUM(7); + res += SUM(n % 7) + weeks * (n % 7); + return res; + } + + private int SUM(int n) { + return (n * (n + 1)) / 2; + } +} +``` + +```cpp +class Solution { +public: + int totalMoney(int n) { + auto SUM = [](int x) { return (x * (x + 1)) / 2; }; + + int weeks = n / 7; + int res = SUM(weeks - 1) * 7 + weeks * SUM(7); + res += SUM(n % 7) + weeks * (n % 7); + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {number} + */ + totalMoney(n) { + const SUM = x => (x * (x + 1)) / 2; + + const weeks = Math.floor(n / 7); + let res = SUM(weeks - 1) * 7 + weeks * SUM(7); + res += SUM(n % 7) + weeks * (n % 7); + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(1)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/count-odd-numbers-in-an-interval-range.md b/articles/count-odd-numbers-in-an-interval-range.md new file mode 100644 index 000000000..1cbaf37b8 --- /dev/null +++ b/articles/count-odd-numbers-in-an-interval-range.md @@ -0,0 +1,187 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def countOdds(self, low: int, high: int) -> int: + odd = 0 + for num in range(low, high + 1): + if num & 1: + odd += 1 + return odd +``` + +```java +public class Solution { + public int countOdds(int low, int high) { + int odd = 0; + for (int num = low; num <= high; num++) { + if ((num & 1) == 1) { + odd++; + } + } + return odd; + } +} +``` + +```cpp +class Solution { +public: + int countOdds(int low, int high) { + int odd = 0; + for (int num = low; num <= high; num++) { + if (num & 1) { + odd++; + } + } + return odd; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} low + * @param {number} high + * @return {number} + */ + countOdds(low, high) { + let odd = 0; + for (let num = low; num <= high; num++) { + if (num & 1) { + odd++; + } + } + return odd; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ + +> Where $n$ is the number of integers in the given range. + +--- + +## 2. Math + +::tabs-start + +```python +class Solution: + def countOdds(self, low: int, high: int) -> int: + length = high - low + 1 + count = length // 2 + if length % 2 and low % 2: + count += 1 + return count +``` + +```java +public class Solution { + public int countOdds(int low, int high) { + int length = high - low + 1; + int count = length / 2; + if (length % 2 == 1 && low % 2 == 1) { + count++; + } + return count; + } +} +``` + +```cpp +class Solution { +public: + int countOdds(int low, int high) { + int length = high - low + 1; + int count = length / 2; + if (length % 2 == 1 && low % 2 == 1) { + count++; + } + return count; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} low + * @param {number} high + * @return {number} + */ + countOdds(low, high) { + let length = high - low + 1; + let count = Math.floor(length / 2); + if (length % 2 === 1 && low % 2 === 1) { + count++; + } + return count; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(1)$ +* Space complexity: $O(1)$ + +--- + +## 3. Math (One Liner) + +::tabs-start + +```python +class Solution: + def countOdds(self, low: int, high: int) -> int: + return ((high + 1) >> 1) - (low >> 1) +``` + +```java +public class Solution { + public int countOdds(int low, int high) { + return ((high + 1) >> 1) - (low >> 1); + } +} +``` + +```cpp +class Solution { +public: + int countOdds(int low, int high) { + return ((high + 1) >> 1) - (low >> 1); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} low + * @param {number} high + * @return {number} + */ + countOdds(low, high) { + return ((high + 1) >> 1) - (low >> 1); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(1)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/count-of-matches-in-tournament.md b/articles/count-of-matches-in-tournament.md new file mode 100644 index 000000000..2ceeeae80 --- /dev/null +++ b/articles/count-of-matches-in-tournament.md @@ -0,0 +1,120 @@ +## 1. Simulation + +::tabs-start + +```python +class Solution: + def numberOfMatches(self, n: int) -> int: + res = 0 + + while n > 1: + res += n // 2 + n = (n + 1) // 2 + + return res +``` + +```java +public class Solution { + public int numberOfMatches(int n) { + int res = 0; + + while (n > 1) { + res += n / 2; + n = (n + 1) / 2; + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int numberOfMatches(int n) { + int res = 0; + + while (n > 1) { + res += n / 2; + n = (n + 1) / 2; + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {number} + */ + numberOfMatches(n) { + let res = 0; + + while (n > 1) { + res += Math.floor(n / 2); + n = Math.ceil(n / 2); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(\log n)$ +* Space complexity: $O(1)$ + +--- + +## 2. Math + +::tabs-start + +```python +class Solution: + def numberOfMatches(self, n: int) -> int: + return n - 1 +``` + +```java +public class Solution { + public int numberOfMatches(int n) { + return n - 1; + } +} +``` + +```cpp +class Solution { +public: + int numberOfMatches(int n) { + return n - 1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {number} + */ + numberOfMatches(n) { + return n - 1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(1)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/find-missing-observations.md b/articles/find-missing-observations.md new file mode 100644 index 000000000..218ffed24 --- /dev/null +++ b/articles/find-missing-observations.md @@ -0,0 +1,210 @@ +## 1. Math - I + +::tabs-start + +```python +class Solution: + def missingRolls(self, rolls: List[int], mean: int, n: int) -> List[int]: + m = len(rolls) + nTotal = (mean * (n + m)) - sum(rolls) + + if nTotal < n or nTotal > n * 6: + return [] + + res = [] + while nTotal: + dice = min(nTotal - n + 1, 6) + res.append(dice) + nTotal -= dice + n -= 1 + return res +``` + +```java +public class Solution { + public int[] missingRolls(int[] rolls, int mean, int n) { + int m = rolls.length; + int nTotal = (mean * (n + m)) - Arrays.stream(rolls).sum(); + + if (nTotal < n || nTotal > n * 6) { + return new int[0]; + } + + int[] res = new int[n]; + for (int i = 0; i < n; i++) { + int dice = Math.min(nTotal - (n - i - 1), 6); + res[i] = dice; + nTotal -= dice; + } + return res; + } +} +``` + +```cpp +class Solution { +public: + vector missingRolls(vector& rolls, int mean, int n) { + int m = rolls.size(); + int nTotal = (mean * (n + m)) - accumulate(rolls.begin(), rolls.end(), 0); + + if (nTotal < n || nTotal > n * 6) { + return {}; + } + + vector res; + for (int i = 0; i < n; ++i) { + int dice = min(nTotal - (n - i - 1), 6); + res.push_back(dice); + nTotal -= dice; + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} rolls + * @param {number} mean + * @param {number} n + * @return {number[]} + */ + missingRolls(rolls, mean, n) { + const m = rolls.length; + let nTotal = (mean * (n + m)) - rolls.reduce((sum, roll) => sum + roll, 0); + + if (nTotal < n || nTotal > n * 6) { + return []; + } + + const res = []; + while (nTotal > 0) { + const dice = Math.min(nTotal - n + 1, 6); + res.push(dice); + nTotal -= dice; + n--; + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m + n)$ +* Space complexity: + * $O(1)$ extra space. + * $O(n)$ space for the output array. + +> Where $m$ is the size of the array $rolls$ and $n$ is the number of missing observations. + +--- + +## 2. Math - II + +::tabs-start + +```python +class Solution: + def missingRolls(self, rolls: List[int], mean: int, n: int) -> List[int]: + m = len(rolls) + nTotal = (mean * (n + m)) - sum(rolls) + + if nTotal < n or nTotal > n * 6: + return [] + + avg = nTotal // n + rem = nTotal - (avg * n) + return [avg] * (n - rem) + [avg + 1] * rem +``` + +```java +public class Solution { + public int[] missingRolls(int[] rolls, int mean, int n) { + int m = rolls.length; + int nTotal = (mean * (n + m)) - Arrays.stream(rolls).sum(); + + if (nTotal < n || nTotal > n * 6) { + return new int[0]; + } + + int avg = nTotal / n; + int rem = nTotal - (avg * n); + int[] res = new int[n]; + + for (int i = 0; i < n - rem; i++) { + res[i] = avg; + } + for (int i = n - rem; i < n; i++) { + res[i] = avg + 1; + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector missingRolls(vector& rolls, int mean, int n) { + int m = rolls.size(); + int nTotal = (mean * (n + m)) - accumulate(rolls.begin(), rolls.end(), 0); + + if (nTotal < n || nTotal > n * 6) { + return {}; + } + + int avg = nTotal / n; + int rem = nTotal - (avg * n); + vector res; + + for (int i = 0; i < n - rem; ++i) { + res.push_back(avg); + } + for (int i = 0; i < rem; ++i) { + res.push_back(avg + 1); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} rolls + * @param {number} mean + * @param {number} n + * @return {number[]} + */ + missingRolls(rolls, mean, n) { + const m = rolls.length; + const nTotal = (mean * (n + m)) - rolls.reduce((sum, roll) => sum + roll, 0); + + if (nTotal < n || nTotal > n * 6) { + return []; + } + + const avg = Math.floor(nTotal / n); + const rem = nTotal - (avg * n); + return Array(n - rem).fill(avg).concat(Array(rem).fill(avg + 1)); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m + n)$ +* Space complexity: + * $O(1)$ extra space. + * $O(n)$ space for the output array. + +> Where $m$ is the size of the array $rolls$ and $n$ is the number of missing observations. \ No newline at end of file diff --git a/articles/find-the-difference.md b/articles/find-the-difference.md new file mode 100644 index 000000000..b06b2803f --- /dev/null +++ b/articles/find-the-difference.md @@ -0,0 +1,466 @@ +## 1. Two Hash Maps + +::tabs-start + +```python +class Solution: + def findTheDifference(self, s: str, t: str) -> str: + count_s, count_t = Counter(s), Counter(t) + for c in count_t: + if c not in count_s or count_s[c] < count_t[c]: + return c +``` + +```java +public class Solution { + public char findTheDifference(String s, String t) { + int[] countS = new int[26]; + int[] countT = new int[26]; + + for (char c : s.toCharArray()) countS[c - 'a']++; + for (char c : t.toCharArray()) countT[c - 'a']++; + + for (int i = 0; i < 26; i++) { + if (countT[i] > countS[i]) { + return (char) (i + 'a'); + } + } + return ' '; + } +} +``` + +```cpp +class Solution { +public: + char findTheDifference(string s, string t) { + vector countS(26, 0), countT(26, 0); + + for (char c : s) countS[c - 'a']++; + for (char c : t) countT[c - 'a']++; + + for (int i = 0; i < 26; i++) { + if (countT[i] > countS[i]) { + return i + 'a'; + } + } + return ' '; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @param {string} t + * @return {character} + */ + findTheDifference(s, t) { + let countS = Array(26).fill(0); + let countT = Array(26).fill(0); + + for (let char of s) countS[char.charCodeAt(0) - 'a'.charCodeAt(0)]++; + for (let char of t) countT[char.charCodeAt(0) - 'a'.charCodeAt(0)]++; + + for (let i = 0; i < 26; i++) { + if (countT[i] > countS[i]) { + return String.fromCharCode(i + 'a'.charCodeAt(0)); + } + } + return ''; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ since we have at most $26$ different characters. + +--- + +## 2. One Hash Map + +::tabs-start + +```python +class Solution: + def findTheDifference(self, s: str, t: str) -> str: + count = Counter(t) + for c in s: + count[c] -= 1 + for c in count: + if count[c] == 1: + return c +``` + +```java +public class Solution { + public char findTheDifference(String s, String t) { + int[] count = new int[26]; + + for (char c : t.toCharArray()) count[c - 'a']++; + for (char c : s.toCharArray()) count[c - 'a']--; + + for (int i = 0; i < 26; i++) { + if (count[i] == 1) { + return (char) (i + 'a'); + } + } + return ' '; + } +} +``` + +```cpp +class Solution { +public: + char findTheDifference(string s, string t) { + vector count(26); + + for (char c : t) count[c - 'a']++; + for (char c : s) count[c - 'a']--; + + for (int i = 0; i < 26; i++) { + if (count[i] == 1) { + return i + 'a'; + } + } + return ' '; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @param {string} t + * @return {character} + */ + findTheDifference(s, t) { + let count = Array(26).fill(0); + + for (let char of t) count[char.charCodeAt(0) - 'a'.charCodeAt(0)]++; + for (let char of s) count[char.charCodeAt(0) - 'a'.charCodeAt(0)]--; + + for (let i = 0; i < 26; i++) { + if (count[i] === 1) { + return String.fromCharCode(i + 'a'.charCodeAt(0)); + } + } + return ''; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ since we have at most $26$ different characters. + +--- + +## 3. Sorting + +::tabs-start + +```python +class Solution: + def findTheDifference(self, s: str, t: str) -> str: + s, t = sorted(s), sorted(t) + for c1, c2 in zip(s, t): + if c1 != c2: + return c2 + return t[-1] +``` + +```java +public class Solution { + public char findTheDifference(String s, String t) { + char[] sArr = s.toCharArray(); + char[] tArr = t.toCharArray(); + Arrays.sort(sArr); + Arrays.sort(tArr); + for (int i = 0; i < sArr.length; i++) { + if (sArr[i] != tArr[i]) { + return tArr[i]; + } + } + return tArr[tArr.length - 1]; + } +} +``` + +```cpp +class Solution { +public: + char findTheDifference(string s, string t) { + sort(s.begin(), s.end()); + sort(t.begin(), t.end()); + for (int i = 0; i < s.size(); i++) { + if (s[i] != t[i]) { + return t[i]; + } + } + return t[t.size() - 1]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @param {string} t + * @return {character} + */ + findTheDifference(s, t) { + s = s.split('').sort(); + t = t.split('').sort(); + for (let i = 0; i < s.length; i++) { + if (s[i] !== t[i]) { + return t[i]; + } + } + return t[t.length - 1]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(1)$ or $O(n)$ depending on the sorting algorithm. + +--- + +## 4. Difference Between ASCII Values + +::tabs-start + +```python +class Solution: + def findTheDifference(self, s: str, t: str) -> str: + sum_s, sum_t = 0, 0 + for c in s: + sum_s += ord(c) + for c in t: + sum_t += ord(c) + return chr(sum_t - sum_s) +``` + +```java +public class Solution { + public char findTheDifference(String s, String t) { + int sumS = 0, sumT = 0; + for (int i = 0; i < s.length(); i++) { + sumS += s.charAt(i); + } + for (int i = 0; i < t.length(); i++) { + sumT += t.charAt(i); + } + return (char) (sumT - sumS); + } +} +``` + +```cpp +class Solution { +public: + char findTheDifference(string s, string t) { + int sumS = 0, sumT = 0; + for (char c : s) { + sumS += c; + } + for (char c : t) { + sumT += c; + } + return (char) (sumT - sumS); + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @param {string} t + * @return {character} + */ + findTheDifference(s, t) { + let sumS = 0, sumT = 0; + for (let char of s) { + sumS += char.charCodeAt(0); + } + for (let char of t) { + sumT += char.charCodeAt(0); + } + return String.fromCharCode(sumT - sumS); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. + +--- + +## 5. Difference Between ASCII Values (Optimal) + +::tabs-start + +```python +class Solution: + def findTheDifference(self, s: str, t: str) -> str: + res = 0 + for c in s: + res -= ord(c) + for c in t: + res += ord(c) + return chr(res) +``` + +```java +public class Solution { + public char findTheDifference(String s, String t) { + int res = 0; + for (int i = 0; i < s.length(); i++) { + res -= s.charAt(i); + } + for (int i = 0; i < t.length(); i++) { + res += t.charAt(i); + } + return (char) (res); + } +} +``` + +```cpp +class Solution { +public: + char findTheDifference(string s, string t) { + int res = 0; + for (char c : s) { + res -= c; + } + for (char c : t) { + res += c; + } + return (char) (res); + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @param {string} t + * @return {character} + */ + findTheDifference(s, t) { + let res = 0; + for (let char of s) { + res -= char.charCodeAt(0); + } + for (let char of t) { + res += char.charCodeAt(0); + } + return String.fromCharCode(res); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. + +--- + +## 6. Bitwise XOR + +::tabs-start + +```python +class Solution: + def findTheDifference(self, s: str, t: str) -> str: + res = 0 + for c in s: + res ^= ord(c) + for c in t: + res ^= ord(c) + return chr(res) +``` + +```java +public class Solution { + public char findTheDifference(String s, String t) { + int res = 0; + for (int i = 0; i < s.length(); i++) { + res ^= s.charAt(i); + } + for (int i = 0; i < t.length(); i++) { + res ^= t.charAt(i); + } + return (char) (res); + } +} +``` + +```cpp +class Solution { +public: + char findTheDifference(string s, string t) { + int res = 0; + for (char c : s) { + res ^= c; + } + for (char c : t) { + res ^= c; + } + return (char) (res); + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @param {string} t + * @return {character} + */ + findTheDifference(s, t) { + let res = 0; + for (let char of s) { + res ^= char.charCodeAt(0); + } + for (let char of t) { + res ^= char.charCodeAt(0); + } + return String.fromCharCode(res); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. \ No newline at end of file diff --git a/articles/image-smoother.md b/articles/image-smoother.md new file mode 100644 index 000000000..767d82e16 --- /dev/null +++ b/articles/image-smoother.md @@ -0,0 +1,530 @@ +## 1. Iteration (Using Extra Matrix) + +::tabs-start + +```python +class Solution: + def imageSmoother(self, img: List[List[int]]) -> List[List[int]]: + ROWS, COLS = len(img), len(img[0]) + res = [[0] * COLS for _ in range(ROWS)] + + for r in range(ROWS): + for c in range(COLS): + total, cnt = 0, 0 + for i in range(r - 1, r + 2): + for j in range(c - 1, c + 2): + if 0 <= i < ROWS and 0 <= j < COLS: + total += img[i][j] + cnt += 1 + res[r][c] = total // cnt + + return res +``` + +```java +public class Solution { + public int[][] imageSmoother(int[][] img) { + int ROWS = img.length, COLS = img[0].length; + int[][] res = new int[ROWS][COLS]; + + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + int total = 0, count = 0; + for (int i = r - 1; i <= r + 1; i++) { + for (int j = c - 1; j <= c + 1; j++) { + if (i >= 0 && i < ROWS && j >= 0 && j < COLS) { + total += img[i][j]; + count++; + } + } + } + res[r][c] = total / count; + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector> imageSmoother(vector>& img) { + int ROWS = img.size(), COLS = img[0].size(); + vector> res(ROWS, vector(COLS, 0)); + + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + int total = 0, count = 0; + for (int i = r - 1; i <= r + 1; i++) { + for (int j = c - 1; j <= c + 1; j++) { + if (i >= 0 && i < ROWS && j >= 0 && j < COLS) { + total += img[i][j]; + count++; + } + } + } + res[r][c] = total / count; + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} img + * @return {number[][]} + */ + imageSmoother(img) { + const ROWS = img.length, COLS = img[0].length; + const res = Array.from({ length: ROWS }, () => Array(COLS).fill(0)); + + for (let r = 0; r < ROWS; r++) { + for (let c = 0; c < COLS; c++) { + let total = 0, count = 0; + for (let i = r - 1; i <= r + 1; i++) { + for (let j = c - 1; j <= c + 1; j++) { + if (i >= 0 && i < ROWS && j >= 0 && j < COLS) { + total += img[i][j]; + count++; + } + } + } + res[r][c] = Math.floor(total / count); + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * m)$ +* Space complexity: $O(n * m)$ + +> Where $n$ is the number of rows and $m$ is the number of columns of the matrix. + +--- + +## 2. Iteration (Using Extra Row) + +::tabs-start + +```python +class Solution: + def imageSmoother(self, img: List[List[int]]) -> List[List[int]]: + ROWS, COLS = len(img), len(img[0]) + prev_row = img[0][:] + + for r in range(ROWS): + curr_row = img[r][:] + + for c in range(COLS): + total, cnt = 0, 0 + for i in range(max(0, r - 1), min(ROWS, r + 2)): + for j in range(max(0, c - 1), min(COLS, c + 2)): + if i == r: + total += curr_row[j] + elif i == r - 1: + total += prev_row[j] + else: + total += img[i][j] + cnt += 1 + img[r][c] = total // cnt + + prev_row = curr_row + + return img +``` + +```java +public class Solution { + public int[][] imageSmoother(int[][] img) { + int ROWS = img.length, COLS = img[0].length; + int[] prevRow = img[0].clone(); + + for (int r = 0; r < ROWS; r++) { + int[] currRow = img[r].clone(); + + for (int c = 0; c < COLS; c++) { + int total = 0, count = 0; + for (int i = Math.max(0, r - 1); i < Math.min(ROWS, r + 2); i++) { + for (int j = Math.max(0, c - 1); j < Math.min(COLS, c + 2); j++) { + if (i == r) { + total += currRow[j]; + } else if (i == r - 1) { + total += prevRow[j]; + } else { + total += img[i][j]; + } + count++; + } + } + img[r][c] = total / count; + } + + prevRow = currRow; + } + + return img; + } +} +``` + +```cpp +class Solution { +public: + vector> imageSmoother(vector>& img) { + int ROWS = img.size(), COLS = img[0].size(); + vector prevRow = img[0]; + + for (int r = 0; r < ROWS; r++) { + vector currRow = img[r]; + + for (int c = 0; c < COLS; c++) { + int total = 0, count = 0; + for (int i = max(0, r - 1); i < min(ROWS, r + 2); i++) { + for (int j = max(0, c - 1); j < min(COLS, c + 2); j++) { + if (i == r) { + total += currRow[j]; + } else if (i == r - 1) { + total += prevRow[j]; + } else { + total += img[i][j]; + } + count++; + } + } + img[r][c] = total / count; + } + + prevRow = currRow; + } + + return img; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} img + * @return {number[][]} + */ + imageSmoother(img) { + const ROWS = img.length, COLS = img[0].length; + let prevRow = [...img[0]]; + + for (let r = 0; r < ROWS; r++) { + let currRow = [...img[r]]; + + for (let c = 0; c < COLS; c++) { + let total = 0, count = 0; + for (let i = Math.max(0, r - 1); i < Math.min(ROWS, r + 2); i++) { + for (let j = Math.max(0, c - 1); j < Math.min(COLS, c + 2); j++) { + if (i === r) { + total += currRow[j]; + } else if (i === r - 1) { + total += prevRow[j]; + } else { + total += img[i][j]; + } + count++; + } + } + img[r][c] = Math.floor(total / count); + } + + prevRow = currRow; + } + + return img; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * m)$ +* Space complexity: $O(n)$ extra space. + +> Where $n$ is the number of rows and $m$ is the number of columns of the matrix. + +--- + +## 3. Iteration (Without Extra Space) + +::tabs-start + +```python +class Solution: + def imageSmoother(self, img: List[List[int]]) -> List[List[int]]: + ROWS, COLS = len(img), len(img[0]) + LIMIT = 256 + + for r in range(ROWS): + for c in range(COLS): + total, cnt = 0, 0 + for i in range(max(0, r - 1), min(ROWS, r + 2)): + for j in range(max(0, c - 1), min(COLS, c + 2)): + total += img[i][j] % LIMIT + cnt += 1 + img[r][c] += (total // cnt) * LIMIT + + for r in range(ROWS): + for c in range(COLS): + img[r][c] //= LIMIT + + return img +``` + +```java +public class Solution { + public int[][] imageSmoother(int[][] img) { + int ROWS = img.length, COLS = img[0].length; + int LIMIT = 256; + + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + int total = 0, count = 0; + for (int i = Math.max(0, r - 1); i < Math.min(ROWS, r + 2); i++) { + for (int j = Math.max(0, c - 1); j < Math.min(COLS, c + 2); j++) { + total += img[i][j] % LIMIT; + count++; + } + } + img[r][c] += (total / count) * LIMIT; + } + } + + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + img[r][c] /= LIMIT; + } + } + + return img; + } +} +``` + +```cpp +class Solution { +public: + vector> imageSmoother(vector>& img) { + int ROWS = img.size(), COLS = img[0].size(); + int LIMIT = 256; + + for (int r = 0; r < ROWS; ++r) { + for (int c = 0; c < COLS; ++c) { + int total = 0, count = 0; + for (int i = max(0, r - 1); i < min(ROWS, r + 2); ++i) { + for (int j = max(0, c - 1); j < min(COLS, c + 2); ++j) { + total += img[i][j] % LIMIT; + count++; + } + } + img[r][c] += (total / count) * LIMIT; + } + } + + for (int r = 0; r < ROWS; ++r) { + for (int c = 0; c < COLS; ++c) { + img[r][c] /= LIMIT; + } + } + + return img; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} img + * @return {number[][]} + */ + imageSmoother(img) { + const ROWS = img.length, COLS = img[0].length; + const LIMIT = 256; + + for (let r = 0; r < ROWS; r++) { + for (let c = 0; c < COLS; c++) { + let total = 0, count = 0; + for (let i = Math.max(0, r - 1); i < Math.min(ROWS, r + 2); i++) { + for (let j = Math.max(0, c - 1); j < Math.min(COLS, c + 2); j++) { + total += img[i][j] % LIMIT; + count++; + } + } + img[r][c] += Math.floor(total / count) * LIMIT; + } + } + + for (let r = 0; r < ROWS; r++) { + for (let c = 0; c < COLS; c++) { + img[r][c] = Math.floor(img[r][c] / LIMIT); + } + } + + return img; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * m)$ +* Space complexity: $O(1)$ extra space. + +> Where $n$ is the number of rows and $m$ is the number of columns of the matrix. + +--- + +## 4. Bit Mask + +::tabs-start + +```python +class Solution: + def imageSmoother(self, img: List[List[int]]) -> List[List[int]]: + ROWS, COLS = len(img), len(img[0]) + + for r in range(ROWS): + for c in range(COLS): + total, cnt = 0, 0 + for i in range(r - 1, r + 2): + for j in range(c - 1, c + 2): + if i < 0 or i == ROWS or j < 0 or j == COLS: + continue + total += img[i][j] % 256 + cnt += 1 + img[r][c] ^= ((total // cnt) << 8) + + for r in range(ROWS): + for c in range(COLS): + img[r][c] >>= 8 + return img +``` + +```java +public class Solution { + public int[][] imageSmoother(int[][] img) { + int ROWS = img.length, COLS = img[0].length; + + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + int total = 0, cnt = 0; + for (int i = r - 1; i <= r + 1; i++) { + for (int j = c - 1; j <= c + 1; j++) { + if (i < 0 || i >= ROWS || j < 0 || j >= COLS) { + continue; + } + total += img[i][j] % 256; + cnt++; + } + } + img[r][c] ^= ((total / cnt) << 8); + } + } + + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + img[r][c] >>= 8; + } + } + return img; + } +} +``` + +```cpp +class Solution { +public: + vector> imageSmoother(vector>& img) { + int ROWS = img.size(), COLS = img[0].size(); + + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + int total = 0, cnt = 0; + for (int i = r - 1; i <= r + 1; i++) { + for (int j = c - 1; j <= c + 1; j++) { + if (i < 0 || i >= ROWS || j < 0 || j >= COLS) { + continue; + } + total += img[i][j] % 256; + cnt++; + } + } + img[r][c] ^= ((total / cnt) << 8); + } + } + + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + img[r][c] >>= 8; + } + } + return img; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} img + * @return {number[][]} + */ + imageSmoother(img) { + const ROWS = img.length, COLS = img[0].length; + + for (let r = 0; r < ROWS; r++) { + for (let c = 0; c < COLS; c++) { + let total = 0, cnt = 0; + for (let i = r - 1; i <= r + 1; i++) { + for (let j = c - 1; j <= c + 1; j++) { + if (i < 0 || i >= ROWS || j < 0 || j >= COLS) { + continue; + } + total += img[i][j] % 256; + cnt++; + } + } + img[r][c] ^= ((total / cnt) << 8); + } + } + + for (let r = 0; r < ROWS; r++) { + for (let c = 0; c < COLS; c++) { + img[r][c] >>= 8; + } + } + return img; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * m)$ +* Space complexity: $O(1)$ extra space. + +> Where $n$ is the number of rows and $m$ is the number of columns of the matrix. \ No newline at end of file diff --git a/articles/largest-local-values-in-a-matrix.md b/articles/largest-local-values-in-a-matrix.md new file mode 100644 index 000000000..169c4d992 --- /dev/null +++ b/articles/largest-local-values-in-a-matrix.md @@ -0,0 +1,439 @@ +## 1. Iteration + +::tabs-start + +```python +class Solution: + def largestLocal(self, grid: List[List[int]]) -> List[List[int]]: + N = len(grid) + res = [[0] * (N - 2) for _ in range(N - 2)] + + for i in range(N - 2): + for j in range(N - 2): + for r in range(i, i + 3): + for c in range(j, j + 3): + res[i][j] = max(res[i][j], grid[r][c]) + + return res +``` + +```java +public class Solution { + public int[][] largestLocal(int[][] grid) { + int N = grid.length; + int[][] res = new int[N - 2][N - 2]; + + for (int i = 0; i < N - 2; i++) { + for (int j = 0; j < N - 2; j++) { + for (int r = i; r < i + 3; r++) { + for (int c = j; c < j + 3; c++) { + res[i][j] = Math.max(res[i][j], grid[r][c]); + } + } + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector> largestLocal(vector>& grid) { + int N = grid.size(); + vector> res(N - 2, vector(N - 2, 0)); + + for (int i = 0; i < N - 2; i++) { + for (int j = 0; j < N - 2; j++) { + for (int r = i; r < i + 3; r++) { + for (int c = j; c < j + 3; c++) { + res[i][j] = max(res[i][j], grid[r][c]); + } + } + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} grid + * @return {number[][]} + */ + largestLocal(grid) { + const N = grid.length; + const res = Array.from({ length: N - 2 }, () => Array(N - 2).fill(0)); + + for (let i = 0; i < N - 2; i++) { + for (let j = 0; j < N - 2; j++) { + for (let r = i; r < i + 3; r++) { + for (let c = j; c < j + 3; c++) { + res[i][j] = Math.max(res[i][j], grid[r][c]); + } + } + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: + * $O(1)$ extra space. + * $O(n ^ 2)$ for the output array. + +--- + +## 2. Generalized Approach (Sparse Table) + +::tabs-start + +```python +class SparseTable: + def __init__(self, grid: List[List[int]]): + self.n = len(grid) + self.LOG = [0] * (self.n + 1) + for i in range(2, self.n + 1): + self.LOG[i] = self.LOG[i // 2] + 1 + + self.sparse_table = [[[[0] * (self.LOG[self.n] + 1) for _ in range(self.LOG[self.n] + 1)] for _ in range(self.n)] for _ in range(self.n)] + + for r in range(self.n): + for c in range(self.n): + self.sparse_table[r][c][0][0] = grid[r][c] + + for i in range(self.LOG[self.n] + 1): + for j in range(self.LOG[self.n] + 1): + for r in range(self.n - (1 << i) + 1): + for c in range(self.n - (1 << j) + 1): + if i == 0 and j == 0: + self.sparse_table[r][c][i][j] = grid[r][c] + elif i == 0: + self.sparse_table[r][c][i][j] = max( + self.sparse_table[r][c][i][j - 1], + self.sparse_table[r][c + (1 << (j - 1))][i][j - 1], + ) + elif j == 0: + self.sparse_table[r][c][i][j] = max( + self.sparse_table[r][c][i - 1][j], + self.sparse_table[r + (1 << (i - 1))][c][i - 1][j], + ) + else: + self.sparse_table[r][c][i][j] = max( + self.sparse_table[r][c][i - 1][j - 1], + self.sparse_table[r + (1 << (i - 1))][c][i - 1][j - 1], + self.sparse_table[r][c + (1 << (j - 1))][i - 1][j - 1], + self.sparse_table[r + (1 << (i - 1))][c + (1 << (j - 1))][i - 1][j - 1], + ) + + def query(self, x1: int, y1: int, x2: int, y2: int) -> int: + lx, ly = self.LOG[x2 - x1 + 1], self.LOG[y2 - y1 + 1] + return max( + self.sparse_table[x1][y1][lx][ly], + self.sparse_table[x2 - (1 << lx) + 1][y1][lx][ly], + self.sparse_table[x1][y2 - (1 << ly) + 1][lx][ly], + self.sparse_table[x2 - (1 << lx) + 1][y2 - (1 << ly) + 1][lx][ly], + ) + + +class Solution: + def largestLocal(self, grid: List[List[int]]) -> List[List[int]]: + N, k = len(grid), 3 + sparse_table = SparseTable(grid) + res = [[0] * (N - k + 1) for _ in range(N - k + 1)] + + for i in range(N - k + 1): + for j in range(N - k + 1): + res[i][j] = sparse_table.query(i, j, i + k - 1, j + k - 1) + + return res +``` + +```java +class SparseTable { + private int[][][][] sparseTable; + private int[] log; + private int n; + + public SparseTable(int[][] grid) { + n = grid.length; + log = new int[n + 1]; + for (int i = 2; i <= n; i++) { + log[i] = log[i >> 1] + 1; + } + + int maxLog = log[n]; + sparseTable = new int[n][n][maxLog + 1][maxLog + 1]; + + for (int r = 0; r < n; r++) { + for (int c = 0; c < n; c++) { + sparseTable[r][c][0][0] = grid[r][c]; + } + } + + for (int i = 0; i <= maxLog; i++) { + for (int j = 0; j <= maxLog; j++) { + for (int r = 0; r + (1 << i) <= n; r++) { + for (int c = 0; c + (1 << j) <= n; c++) { + if (i == 0 && j == 0) continue; + if (i == 0) { + sparseTable[r][c][i][j] = Math.max( + sparseTable[r][c][i][j - 1], + sparseTable[r][c + (1 << (j - 1))][i][j - 1] + ); + } else if (j == 0) { + sparseTable[r][c][i][j] = Math.max( + sparseTable[r][c][i - 1][j], + sparseTable[r + (1 << (i - 1))][c][i - 1][j] + ); + } else { + sparseTable[r][c][i][j] = Math.max( + Math.max(sparseTable[r][c][i - 1][j - 1], sparseTable[r + (1 << (i - 1))][c][i - 1][j - 1]), + Math.max(sparseTable[r][c + (1 << (j - 1))][i - 1][j - 1], + sparseTable[r + (1 << (i - 1))][c + (1 << (j - 1))][i - 1][j - 1]) + ); + } + } + } + } + } + } + + public int query(int x1, int y1, int x2, int y2) { + int lx = log[x2 - x1 + 1]; + int ly = log[y2 - y1 + 1]; + return Math.max( + Math.max(sparseTable[x1][y1][lx][ly], sparseTable[x2 - (1 << lx) + 1][y1][lx][ly]), + Math.max(sparseTable[x1][y2 - (1 << ly) + 1][lx][ly], + sparseTable[x2 - (1 << lx) + 1][y2 - (1 << ly) + 1][lx][ly]) + ); + } +} + +public class Solution { + public int[][] largestLocal(int[][] grid) { + int n = grid.length; + int k = 3; + SparseTable st = new SparseTable(grid); + int[][] res = new int[n - k + 1][n - k + 1]; + + for (int i = 0; i <= n - k; i++) { + for (int j = 0; j <= n - k; j++) { + res[i][j] = st.query(i, j, i + k - 1, j + k - 1); + } + } + + return res; + } +} +``` + +```cpp +class SparseTable { +public: + vector>>> sparseTable; + vector log; + int n; + + SparseTable(vector>& grid) { + n = grid.size(); + log.resize(n + 1, 0); + for (int i = 2; i <= n; i++) { + log[i] = log[i / 2] + 1; + } + + int maxLog = log[n]; + sparseTable.resize(n, vector>>(n, vector>(maxLog + 1, vector(maxLog + 1)))); + + for (int r = 0; r < n; r++) { + for (int c = 0; c < n; c++) { + sparseTable[r][c][0][0] = grid[r][c]; + } + } + + for (int i = 0; i <= maxLog; i++) { + for (int j = 0; j <= maxLog; j++) { + for (int r = 0; r + (1 << i) <= n; r++) { + for (int c = 0; c + (1 << j) <= n; c++) { + if (i == 0 && j == 0) continue; + if (i == 0) { + sparseTable[r][c][i][j] = max( + sparseTable[r][c][i][j - 1], + sparseTable[r][c + (1 << (j - 1))][i][j - 1] + ); + } else if (j == 0) { + sparseTable[r][c][i][j] = max( + sparseTable[r][c][i - 1][j], + sparseTable[r + (1 << (i - 1))][c][i - 1][j] + ); + } else { + sparseTable[r][c][i][j] = max( + max(sparseTable[r][c][i - 1][j - 1], sparseTable[r + (1 << (i - 1))][c][i - 1][j - 1]), + max(sparseTable[r][c + (1 << (j - 1))][i - 1][j - 1], + sparseTable[r + (1 << (i - 1))][c + (1 << (j - 1))][i - 1][j - 1]) + ); + } + } + } + } + } + } + + int query(int x1, int y1, int x2, int y2) { + int lx = log[x2 - x1 + 1]; + int ly = log[y2 - y1 + 1]; + return max( + max(sparseTable[x1][y1][lx][ly], sparseTable[x2 - (1 << lx) + 1][y1][lx][ly]), + max(sparseTable[x1][y2 - (1 << ly) + 1][lx][ly], + sparseTable[x2 - (1 << lx) + 1][y2 - (1 << ly) + 1][lx][ly]) + ); + } +}; + +class Solution { +public: + vector> largestLocal(vector>& grid) { + int n = grid.size(), k = 3; + SparseTable st(grid); + vector> res(n - k + 1, vector(n - k + 1)); + + for (int i = 0; i <= n - k; i++) { + for (int j = 0; j <= n - k; j++) { + res[i][j] = st.query(i, j, i + k - 1, j + k - 1); + } + } + + return res; + } +}; +``` + +```javascript +class SparseTable { + /** + * @constructor + * @param {number[][]} grid + */ + constructor(grid) { + this.n = grid.length; + this.log = Array(this.n + 1).fill(0); + for (let i = 2; i <= this.n; i++) { + this.log[i] = this.log[Math.floor(i / 2)] + 1; + } + + const maxLog = this.log[this.n]; + this.sparseTable = Array.from({ length: this.n }, () => + Array.from({ length: this.n }, () => + Array.from({ length: maxLog + 1 }, () => + Array(maxLog + 1).fill(0) + ) + ) + ); + + for (let r = 0; r < this.n; r++) { + for (let c = 0; c < this.n; c++) { + this.sparseTable[r][c][0][0] = grid[r][c]; + } + } + + for (let i = 0; i <= maxLog; i++) { + for (let j = 0; j <= maxLog; j++) { + for (let r = 0; r + (1 << i) <= this.n; r++) { + for (let c = 0; c + (1 << j) <= this.n; c++) { + if (i === 0 && j === 0) continue; + if (i === 0) { + this.sparseTable[r][c][i][j] = Math.max( + this.sparseTable[r][c][i][j - 1], + this.sparseTable[r][c + (1 << (j - 1))][i][j - 1] + ); + } else if (j === 0) { + this.sparseTable[r][c][i][j] = Math.max( + this.sparseTable[r][c][i - 1][j], + this.sparseTable[r + (1 << (i - 1))][c][i - 1][j] + ); + } else { + this.sparseTable[r][c][i][j] = Math.max( + Math.max( + this.sparseTable[r][c][i - 1][j - 1], + this.sparseTable[r + (1 << (i - 1))][c][i - 1][j - 1] + ), + Math.max( + this.sparseTable[r][c + (1 << (j - 1))][i - 1][j - 1], + this.sparseTable[r + (1 << (i - 1))][c + (1 << (j - 1))][i - 1][j - 1] + ) + ); + } + } + } + } + } + } + + /** + * @param {number} x1 + * @param {number} y1 + * @param {number} x2 + * @param {number} y2 + * @return {number} + */ + query(x1, y1, x2, y2) { + const lx = this.log[x2 - x1 + 1]; + const ly = this.log[y2 - y1 + 1]; + return Math.max( + Math.max( + this.sparseTable[x1][y1][lx][ly], + this.sparseTable[x2 - (1 << lx) + 1][y1][lx][ly] + ), + Math.max( + this.sparseTable[x1][y2 - (1 << ly) + 1][lx][ly], + this.sparseTable[x2 - (1 << lx) + 1][y2 - (1 << ly) + 1][lx][ly] + ) + ); + } +} + +class Solution { + /** + * @param {number[][]} grid + * @return {number[][]} + */ + largestLocal(grid) { + const n = grid.length, k = 3; + const st = new SparseTable(grid); + const res = Array.from({ length: n - k + 1 }, () => + Array(n - k + 1).fill(0) + ); + + for (let i = 0; i <= n - k; i++) { + for (let j = 0; j <= n - k; j++) { + res[i][j] = st.query(i, j, i + k - 1, j + k - 1); + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2 \log ^ 2 n)$ +* Space complexity: + * $O(n ^ 2 \log ^ 2 n)$ extra space. + * $O((n - k) ^ 2)$ for the output matrix. + +> Where $n$ is the size of the given square grid and $k$ is the fixed size of the submatrix window. \ No newline at end of file diff --git a/articles/largest-odd-number-in-string.md b/articles/largest-odd-number-in-string.md new file mode 100644 index 000000000..566fb7c11 --- /dev/null +++ b/articles/largest-odd-number-in-string.md @@ -0,0 +1,166 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def largestOddNumber(self, num: str) -> str: + res = "" + for i in range(len(num)): + for j in range(i, len(num)): + ones_digit = ord(num[j]) - ord('0') + if ones_digit & 1: + cur = num[i:j + 1] + if len(res) < len(cur) or (len(cur) == len(res) and res < cur): + res = cur + return res +``` + +```java +public class Solution { + public String largestOddNumber(String num) { + String res = ""; + int n = num.length(); + + for (int i = 0; i < n; i++) { + for (int j = i; j < n; j++) { + int onesDigit = num.charAt(j) - '0'; + if ((onesDigit & 1) == 1) { + String cur = num.substring(i, j + 1); + if (res.length() < cur.length() || + (res.length() == cur.length() && res.compareTo(cur) < 0)) { + res = cur; + } + } + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + string largestOddNumber(string num) { + string res = ""; + int n = num.size(); + + for (int i = 0; i < n; i++) { + for (int j = i; j < n; j++) { + int onesDigit = num[j] - '0'; + if (onesDigit & 1) { + string cur = num.substr(i, j - i + 1); + if (res.size() < cur.size() || + (res.size() == cur.size() && res < cur)) { + res = cur; + } + } + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} num + * @return {string} + */ + largestOddNumber(num) { + let res = ""; + const n = num.length; + + for (let i = 0; i < n; i++) { + for (let j = i; j < n; j++) { + const onesDigit = num[j].charCodeAt(0) - '0'.charCodeAt(0); + if (onesDigit & 1) { + const cur = num.slice(i, j + 1); + if (res.length < cur.length || + (res.length === cur.length && res < cur)) { + res = cur; + } + } + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 3)$ +* Space complexity: $O(n)$ + +--- + +## 2. Find The Rightmost Odd Digit + +::tabs-start + +```python +class Solution: + def largestOddNumber(self, num: str) -> str: + for i in range(len(num) - 1, -1, -1): + if int(num[i]) % 2: + return num[:i + 1] + return "" +``` + +```java +public class Solution { + public String largestOddNumber(String num) { + for (int i = num.length() - 1; i >= 0; i--) { + if ((num.charAt(i) - '0') % 2 == 1) { + return num.substring(0, i + 1); + } + } + return ""; + } +} +``` + +```cpp +class Solution { +public: + string largestOddNumber(string num) { + for (int i = num.size() - 1; i >= 0; i--) { + if ((num[i] - '0') % 2 == 1) { + return num.substr(0, i + 1); + } + } + return ""; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} num + * @return {string} + */ + largestOddNumber(num) { + for (let i = num.length - 1; i >= 0; i--) { + if (parseInt(num[i]) % 2 === 1) { + return num.slice(0, i + 1); + } + } + return ""; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: + * $O(1)$ extra space. + * $O(n)$ for the output string. \ No newline at end of file diff --git a/articles/matrix-diagonal-sum.md b/articles/matrix-diagonal-sum.md new file mode 100644 index 000000000..8524ff1a7 --- /dev/null +++ b/articles/matrix-diagonal-sum.md @@ -0,0 +1,184 @@ +## 1. Iteration + +::tabs-start + +```python +class Solution: + def diagonalSum(self, mat: List[List[int]]) -> int: + n = len(mat) + + def helper(matrix): + res = 0 + for i in range(n): + for j in range(n): + if i == j: + res += matrix[i][j] + matrix[i].reverse() + return res + + return helper(mat) + helper(mat) - (mat[n // 2][n // 2] if n & 1 else 0) +``` + +```java +public class Solution { + public int diagonalSum(int[][] mat) { + int n = mat.length; + return helper(mat) + helper(mat) - (n % 2 == 1 ? mat[n / 2][n / 2] : 0); + } + + int helper(int[][] matrix) { + int res = 0, n = matrix.length; + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { + if (i == j) { + res += matrix[i][j]; + } + } + reverse(matrix[i]); + } + return res; + } + + void reverse(int[] row) { + int left = 0, right = row.length - 1; + while (left < right) { + int temp = row[left]; + row[left++] = row[right]; + row[right--] = temp; + } + } +} +``` + +```cpp +class Solution { +public: + int diagonalSum(vector>& mat) { + int n = mat.size(); + return helper(mat) + helper(mat) - (n % 2 == 1 ? mat[n / 2][n / 2] : 0); + } + +private: + int helper(vector>& matrix) { + int res = 0, n = matrix.size(); + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { + if (i == j) { + res += matrix[i][j]; + } + } + reverse(matrix[i].begin(), matrix[i].end()); + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} mat + * @return {number} + */ + diagonalSum(mat) { + const n = mat.length; + + const helper = (matrix) => { + let res = 0; + for (let i = 0; i < n; i++) { + for (let j = 0; j < n; j++) { + if (i === j) { + res += matrix[i][j]; + } + } + matrix[i].reverse(); + } + return res; + }; + + return helper(mat) + helper(mat) - (n % 2 === 1 ? mat[Math.floor(n / 2)][Math.floor(n / 2)] : 0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(1)$ extra space. + +--- + +## 2. Iteration (Optimal) + +::tabs-start + +```python +class Solution: + def diagonalSum(self, mat: List[List[int]]) -> int: + res, n = 0, len(mat) + + for r in range(n): + res += mat[r][r] + res += mat[r][n - r - 1] + + return res - (mat[n // 2][n // 2] if n & 1 else 0) +``` + +```java +public class Solution { + public int diagonalSum(int[][] mat) { + int res = 0, n = mat.length; + + for (int r = 0; r < n; r++) { + res += mat[r][r]; + res += mat[r][n - r - 1]; + } + + return res - (n % 2 == 1 ? mat[n / 2][n / 2] : 0); + } +} +``` + +```cpp +class Solution { +public: + int diagonalSum(vector>& mat) { + int res = 0, n = mat.size(); + + for (int r = 0; r < n; r++) { + res += mat[r][r]; + res += mat[r][n - r - 1]; + } + + return res - (n % 2 == 1 ? mat[n / 2][n / 2] : 0); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} mat + * @return {number} + */ + diagonalSum(mat) { + let res = 0, n = mat.length; + + for (let r = 0; r < n; r++) { + res += mat[r][r]; + res += mat[r][n - r - 1]; + } + + return res - (n % 2 == 1 ? mat[Math.floor(n / 2)][Math.floor(n / 2)] : 0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. \ No newline at end of file diff --git a/articles/max-points-on-a-line.md b/articles/max-points-on-a-line.md new file mode 100644 index 000000000..8c87a3369 --- /dev/null +++ b/articles/max-points-on-a-line.md @@ -0,0 +1,384 @@ +## 1. Math + +::tabs-start + +```python +class Solution: + def maxPoints(self, points: List[List[int]]) -> int: + n = len(points) + if n <= 2: + return n + + def get_slope(p1, p2): + if p1[0] == p2[0]: + return float("inf") + return (p2[1] - p1[1]) / (p2[0] - p1[0]) + + res = 1 + for i in range(n): + for j in range(i + 1, n): + slope = get_slope(points[i], points[j]) + cnt = 2 + for k in range(j + 1, n): + if slope == get_slope(points[i], points[k]): + cnt += 1 + res = max(res, cnt) + + return res +``` + +```java +public class Solution { + public int maxPoints(int[][] points) { + int n = points.length; + if (n <= 2) { + return n; + } + + int res = 1; + for (int i = 0; i < n; i++) { + for (int j = i + 1; j < n; j++) { + double slope = getSlope(points[i], points[j]); + int cnt = 2; + for (int k = j + 1; k < n; k++) { + if (slope == getSlope(points[i], points[k])) { + cnt++; + } + } + res = Math.max(res, cnt); + } + } + + return res; + } + + private double getSlope(int[] p1, int[] p2) { + if (p1[0] == p2[0]) { + return Double.POSITIVE_INFINITY; + } + return (double) (p2[1] - p1[1]) / (p2[0] - p1[0]); + } +} +``` + +```cpp +class Solution { +public: + int maxPoints(vector>& points) { + int n = points.size(); + if (n <= 2) { + return n; + } + + int res = 1; + for (int i = 0; i < n; i++) { + for (int j = i + 1; j < n; j++) { + double slope = getSlope(points[i], points[j]); + int cnt = 2; + for (int k = j + 1; k < n; k++) { + if (slope == getSlope(points[i], points[k])) { + cnt++; + } + } + res = max(res, cnt); + } + } + + return res; + } + +private: + double getSlope(vector& p1, vector& p2) { + if (p1[0] == p2[0]) { + return INFINITY; + } + return (double)(p2[1] - p1[1]) / (p2[0] - p1[0]); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} points + * @return {number} + */ + maxPoints(points) { + const n = points.length; + if (n <= 2) { + return n; + } + + let res = 1; + const getSlope = (p1, p2) => { + if (p1[0] === p2[0]) { + return Infinity; + } + return (p2[1] - p1[1]) / (p2[0] - p1[0]); + }; + + for (let i = 0; i < n; i++) { + for (let j = i + 1; j < n; j++) { + const slope = getSlope(points[i], points[j]); + let cnt = 2; + for (let k = j + 1; k < n; k++) { + if (slope === getSlope(points[i], points[k])) { + cnt++; + } + } + res = Math.max(res, cnt); + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 3)$ +* Space complexity: $O(1)$ extra space. + +--- + +## 2. Math + Hash Map + +::tabs-start + +```python +class Solution: + def maxPoints(self, points: List[List[int]]) -> int: + res = 1 + for i in range(len(points)): + p1 = points[i] + count = defaultdict(int) + for j in range(i + 1, len(points)): + p2 = points[j] + if p2[0] == p1[0]: + slope = float("inf") + else: + slope = (p2[1] - p1[1]) / (p2[0] - p1[0]) + count[slope] += 1 + res = max(res, count[slope] + 1) + return res +``` + +```java +public class Solution { + public int maxPoints(int[][] points) { + int res = 1; + for (int i = 0; i < points.length; i++) { + Map count = new HashMap<>(); + for (int j = i + 1; j < points.length; j++) { + double slope = getSlope(points[i], points[j]); + if (slope == -0.0) slope = 0.0; + + count.put(slope, count.getOrDefault(slope, 0) + 1); + res = Math.max(res, count.get(slope) + 1); + } + } + return res; + } + + private double getSlope(int[] p1, int[] p2) { + if (p1[0] == p2[0]) { + return Double.POSITIVE_INFINITY; + } + return (double) (p2[1] - p1[1]) / (p2[0] - p1[0]); + } +} +``` + +```cpp +class Solution { +public: + int maxPoints(vector>& points) { + int res = 1; + for (int i = 0; i < points.size(); i++) { + vector& p1 = points[i]; + unordered_map count; + for (int j = i + 1; j < points.size(); j++) { + vector& p2 = points[j]; + double slope = (p2[0] == p1[0]) ? INFINITY : + (double)(p2[1] - p1[1]) / (p2[0] - p1[0]); + count[slope]++; + res = max(res, count[slope] + 1); + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} points + * @return {number} + */ + maxPoints(points) { + let res = 1; + for (let i = 0; i < points.length; i++) { + const p1 = points[i]; + const count = new Map(); + for (let j = i + 1; j < points.length; j++) { + const p2 = points[j]; + const slope = (p2[0] === p1[0]) ? Infinity : + (p2[1] - p1[1]) / (p2[0] - p1[0]); + count.set(slope, (count.get(slope) || 0) + 1); + res = Math.max(res, count.get(slope) + 1); + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n)$ + +--- + +## 3. Math + Hash Map (Optimal) + +::tabs-start + +```python +class Solution: + def maxPoints(self, points: List[List[int]]) -> int: + if len(points) <= 2: + return len(points) + + def gcd(a, b): + return gcd(b, a % b) if b else a + + res = 1 + for i in range(len(points) - 1): + count = defaultdict(int) + for j in range(i + 1, len(points)): + dx = points[j][0] - points[i][0] + dy = points[j][1] - points[i][1] + g = gcd(dx, dy) + dx //= g + dy //= g + slope = (dx, dy) + count[slope] += 1 + res = max(res, max(count.values()) + 1) + return res +``` + +```java +public class Solution { + public int maxPoints(int[][] points) { + if (points.length <= 2) { + return points.length; + } + + int res = 1; + for (int i = 0; i < points.length - 1; i++) { + Map count = new HashMap<>(); + for (int j = i + 1; j < points.length; j++) { + int dx = points[j][0] - points[i][0]; + int dy = points[j][1] - points[i][1]; + int g = gcd(dx, dy); + dx /= g; + dy /= g; + String slope = dx + ":" + dy; + count.put(slope, count.getOrDefault(slope, 0) + 1); + } + for (int val : count.values()) { + res = Math.max(res, val + 1); + } + } + return res; + } + + private int gcd(int a, int b) { + return b == 0 ? a : gcd(b, a % b); + } +} +``` + +```cpp +class Solution { +public: + int maxPoints(vector>& points) { + if (points.size() <= 2) { + return points.size(); + } + + int res = 1; + for (int i = 0; i < points.size() - 1; i++) { + unordered_map count; + for (int j = i + 1; j < points.size(); j++) { + int dx = points[j][0] - points[i][0]; + int dy = points[j][1] - points[i][1]; + int g = gcd(dx, dy); + dx /= g; + dy /= g; + string slope = to_string(dx) + ":" + to_string(dy); + count[slope]++; + } + for (const auto& [slope, freq] : count) { + res = max(res, freq + 1); + } + } + return res; + } + +private: + int gcd(int a, int b) { + return b == 0 ? a : gcd(b, a % b); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} points + * @return {number} + */ + maxPoints(points) { + if (points.length <= 2) { + return points.length; + } + + const gcd = (a, b) => (b === 0 ? a : gcd(b, a % b)); + + let res = 1; + for (let i = 0; i < points.length - 1; i++) { + const count = new Map(); + for (let j = i + 1; j < points.length; j++) { + let dx = points[j][0] - points[i][0]; + let dy = points[j][1] - points[i][1]; + const g = gcd(dx, dy); + dx /= g; + dy /= g; + const slope = `${dx}:${dy}`; + count.set(slope, (count.get(slope) || 0) + 1); + } + for (const freq of count.values()) { + res = Math.max(res, freq + 1); + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2 \log m)$ +* Space complexity: $O(n)$ + +> Where $n$ is the number of points and $m$ is the maximum value in the points. \ No newline at end of file diff --git a/articles/palindrome-number.md b/articles/palindrome-number.md new file mode 100644 index 000000000..77f44aad6 --- /dev/null +++ b/articles/palindrome-number.md @@ -0,0 +1,420 @@ +## 1. Convert to String + +::tabs-start + +```python +class Solution: + def isPalindrome(self, x: int) -> bool: + s = str(x) + return s == s[::-1] +``` + +```java +public class Solution { + public boolean isPalindrome(int x) { + String s = String.valueOf(x); + return s.equals(new StringBuilder(s).reverse().toString()); + } +} +``` + +```cpp +class Solution { +public: + bool isPalindrome(int x) { + string s = to_string(x); + string rev = s; + reverse(rev.begin(), rev.end()); + return s == rev; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} x + * @return {boolean} + */ + isPalindrome(x) { + const s = String(x); + return s === s.split('').reverse().join(''); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +> Where $n$ is the number of digits in the given integer. + +--- + +## 2. Convert to String (Optimal) + +::tabs-start + +```python +class Solution: + def isPalindrome(self, x: int) -> bool: + s = str(x) + n = len(s) + for i in range(n // 2): + if s[i] != s[n - i - 1]: + return False + return True +``` + +```java +public class Solution { + public boolean isPalindrome(int x) { + String s = String.valueOf(x); + int n = s.length(); + for (int i = 0; i < n / 2; i++) { + if (s.charAt(i) != s.charAt(n - i - 1)) { + return false; + } + } + return true; + } +} +``` + +```cpp +class Solution { +public: + bool isPalindrome(int x) { + string s = to_string(x); + int n = s.length(); + for (int i = 0; i < n / 2; i++) { + if (s[i] != s[n - i - 1]) { + return false; + } + } + return true; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} x + * @return {boolean} + */ + isPalindrome(x) { + const s = String(x); + let n = s.length; + for (let i = 0; i < (n >> 1); i++) { + if (s.charAt(i) != s.charAt(n - i - 1)) { + return false; + } + } + return true; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +> Where $n$ is the number of digits in the given integer. + +--- + +## 3. Reverse the Integer + +::tabs-start + +```python +class Solution: + def isPalindrome(self, x: int) -> bool: + if x < 0: + return False + + rev = 0 + num = x + while num: + rev = (rev * 10) + (num % 10) + num //= 10 + + return rev == x +``` + +```java +public class Solution { + public boolean isPalindrome(int x) { + if (x < 0) { + return false; + } + + long rev = 0, num = x; + while (num != 0) { + rev = (rev * 10) + (num % 10); + num /= 10; + } + + return rev == x; + } +} +``` + +```cpp +class Solution { +public: + bool isPalindrome(int x) { + if (x < 0) { + return false; + } + + long long rev = 0, num = x; + while (num != 0) { + rev = (rev * 10) + (num % 10); + num /= 10; + } + + return rev == x; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} x + * @return {boolean} + */ + isPalindrome(x) { + if (x < 0) { + return false; + } + + let rev = 0, num = x; + while (num !== 0) { + rev = (rev * 10) + (num % 10); + num = Math.floor(num / 10); + } + + return rev === x; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ + +> Where $n$ is the number of digits in the given integer. + +--- + +## 4. Two Pointers + +::tabs-start + +```python +class Solution: + def isPalindrome(self, x: int) -> bool: + if x < 0: + return False + + div = 1 + while x >= 10 * div: + div *= 10 + + while x: + if x // div != x % 10: + return False + x = (x % div) // 10 + div //= 100 + + return True +``` + +```java +public class Solution { + public boolean isPalindrome(int x) { + if (x < 0) { + return false; + } + + long div = 1; + while (x >= 10 * div) { + div *= 10; + } + + while (x != 0) { + if (x / div != x % 10) { + return false; + } + x = (int) (x % div) / 10; + div /= 100; + } + + return true; + } +} +``` + +```cpp +class Solution { +public: + bool isPalindrome(int x) { + if (x < 0) { + return false; + } + + long long div = 1; + while (x >= 10 * div) { + div *= 10; + } + + while (x != 0) { + if (x / div != x % 10) { + return false; + } + x = (x % div) / 10; + div /= 100; + } + + return true; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} x + * @return {boolean} + */ + isPalindrome(x) { + if (x < 0) { + return false; + } + + let div = 1; + while (x >= 10 * div) { + div *= 10; + } + + while (x !== 0) { + if (Math.floor(x / div) !== x % 10) { + return false; + } + x = Math.floor((x % div) / 10); + div = Math.floor(div / 100); + } + + return true; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ + +> Where $n$ is the number of digits in the given integer. + +--- + +## 5. Reverse Half of the Number + +::tabs-start + +```python +class Solution: + def isPalindrome(self, x: int) -> bool: + if x < 0 or (x != 0 and x % 10 == 0): + return False + + rev = 0 + while x > rev: + rev = (rev * 10) + (x % 10) + x //= 10 + + return x == rev or x == rev // 10 +``` + +```java +public class Solution { + public boolean isPalindrome(int x) { + if (x < 0 || (x != 0 && x % 10 == 0)) { + return false; + } + + int rev = 0; + while (x > rev) { + rev = rev * 10 + x % 10; + x /= 10; + } + + return x == rev || x == rev / 10; + } +} +``` + +```cpp +class Solution { +public: + bool isPalindrome(int x) { + if (x < 0 || (x != 0 && x % 10 == 0)) { + return false; + } + + int rev = 0; + while (x > rev) { + rev = rev * 10 + x % 10; + x /= 10; + } + + return x == rev || x == rev / 10; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} x + * @return {boolean} + */ + isPalindrome(x) { + if (x < 0 || (x !== 0 && x % 10 === 0)) { + return false; + } + + let rev = 0; + while (x > rev) { + rev = rev * 10 + (x % 10); + x = Math.floor(x / 10); + } + + return x === rev || x === Math.floor(rev / 10); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ + +> Where $n$ is the number of digits in the given integer. \ No newline at end of file diff --git a/articles/power-of-four.md b/articles/power-of-four.md new file mode 100644 index 000000000..dc54669cc --- /dev/null +++ b/articles/power-of-four.md @@ -0,0 +1,367 @@ +## 1. Recursion + +::tabs-start + +```python +class Solution: + def isPowerOfFour(self, n: int) -> bool: + if n == 1: + return True + if n <= 0 or n % 4: + return False + return self.isPowerOfFour(n // 4) +``` + +```java +public class Solution { + public boolean isPowerOfFour(int n) { + if (n == 1) { + return true; + } + if (n <= 0 || n % 4 != 0) { + return false; + } + return isPowerOfFour(n / 4); + } +} +``` + +```cpp +class Solution { +public: + bool isPowerOfFour(int n) { + if (n == 1) { + return true; + } + if (n <= 0 || n % 4 != 0) { + return false; + } + return isPowerOfFour(n / 4); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {boolean} + */ + isPowerOfFour(n) { + if (n === 1) { + return true; + } + if (n <= 0 || n % 4 !== 0) { + return false; + } + return this.isPowerOfFour(Math.floor(n / 4)); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(1)$ +* Space complexity: $O(1)$ + +--- + +## 2. Iteration + +::tabs-start + +```python +class Solution: + def isPowerOfFour(self, n: int) -> bool: + if n < 0: + return False + + while n > 1: + if n % 4: + return False + n //= 4 + + return n == 1 +``` + +```java +public class Solution { + public boolean isPowerOfFour(int n) { + if (n < 0) return false; + + while (n > 1) { + if (n % 4 != 0) return false; + n /= 4; + } + + return n == 1; + } +} +``` + +```cpp +class Solution { +public: + bool isPowerOfFour(int n) { + if (n < 0) return false; + + while (n > 1) { + if (n % 4 != 0) return false; + n /= 4; + } + + return n == 1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {boolean} + */ + isPowerOfFour(n) { + if (n < 0) return false; + + while (n > 1) { + if (n % 4 !== 0) return false; + n = Math.floor(n / 4); + } + + return n === 1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(1)$ +* Space complexity: $O(1)$ + +--- + +## 3. Math + +::tabs-start + +```python +class Solution: + def isPowerOfFour(self, n: int) -> bool: + return n > 0 and log(n, 4) % 1 == 0 +``` + +```java +public class Solution { + public boolean isPowerOfFour(int n) { + return n > 0 && Math.log(n) / Math.log(4) % 1 == 0; + } +} +``` + +```cpp +class Solution { +public: + bool isPowerOfFour(int n) { + return n > 0 && fmod(log(n) / log(4), 1) == 0; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {boolean} + */ + isPowerOfFour(n) { + return n > 0 && (Math.log(n) / Math.log(4)) % 1 === 0; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(1)$ +* Space complexity: $O(1)$ + +--- + +## 4. Bit Manipulation + +::tabs-start + +```python +class Solution: + def isPowerOfFour(self, n: int) -> bool: + if n < 0: + return False + + for i in range(0, 32, 2): + if n == (1 << i): + return True + + return False +``` + +```java +public class Solution { + public boolean isPowerOfFour(int n) { + if (n < 0) return false; + + for (int i = 0; i < 32; i += 2) { + if (n == (1 << i)) { + return true; + } + } + + return false; + } +} +``` + +```cpp +class Solution { +public: + bool isPowerOfFour(int n) { + if (n < 0) return false; + + for (int i = 0; i < 32; i += 2) { + if (n == (1 << i)) { + return true; + } + } + + return false; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {boolean} + */ + isPowerOfFour(n) { + if (n < 0) return false; + + for (let i = 0; i < 32; i += 2) { + if (n === (1 << i)) { + return true; + } + } + + return false; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(1)$ +* Space complexity: $O(1)$ + +--- + +## 5. Bit Mask - I + +::tabs-start + +```python +class Solution: + def isPowerOfFour(self, n: int) -> bool: + return n > 0 and (n & (n - 1)) == 0 and (n & 0x55555555) == n +``` + +```java +public class Solution { + public boolean isPowerOfFour(int n) { + return n > 0 && (n & (n - 1)) == 0 && (n & 0x55555555) == n; + } +} +``` + +```cpp +class Solution { +public: + bool isPowerOfFour(int n) { + return n > 0 && (n & (n - 1)) == 0 && (n & 0x55555555) == n; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {boolean} + */ + isPowerOfFour(n) { + return n > 0 && (n & (n - 1)) === 0 && (n & 0x55555555) === n; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(1)$ +* Space complexity: $O(1)$ + +--- + +## 6. Bit Mask - II + +::tabs-start + +```python +class Solution: + def isPowerOfFour(self, n: int) -> bool: + return n > 0 and (n & (n - 1)) == 0 and (n % 3 == 1) +``` + +```java +public class Solution { + public boolean isPowerOfFour(int n) { + return n > 0 && (n & (n - 1)) == 0 && (n % 3 == 1); + } +} +``` + +```cpp +class Solution { +public: + bool isPowerOfFour(int n) { + return n > 0 && (n & (n - 1)) == 0 && (n % 3 == 1); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {boolean} + */ + isPowerOfFour(n) { + return n > 0 && (n & (n - 1)) === 0 && (n % 3 == 1); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(1)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/power-of-two.md b/articles/power-of-two.md new file mode 100644 index 000000000..5024ea27a --- /dev/null +++ b/articles/power-of-two.md @@ -0,0 +1,351 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def isPowerOfTwo(self, n: int) -> bool: + if n <= 0: + return False + + x = 1 + while x < n: + x *= 2 + return x == n +``` + +```java +public class Solution { + public boolean isPowerOfTwo(int n) { + if (n <= 0) return false; + + long x = 1; + while (x < n) { + x *= 2; + } + return x == n; + } +} +``` + +```cpp +class Solution { +public: + bool isPowerOfTwo(int n) { + if (n <= 0) return false; + + long long x = 1; + while (x < n) { + x *= 2; + } + return x == n; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {boolean} + */ + isPowerOfTwo(n) { + if (n <= 0) return false; + + let x = 1; + while (x < n) { + x *= 2; + } + return x === n; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(\log n)$ +* Space complexity: $O(1)$ + +--- + +## 2. Recursion + +::tabs-start + +```python +class Solution: + def isPowerOfTwo(self, n: int) -> bool: + if n == 1: + return True + if n <= 0 or n % 2 == 1: + return False + return self.isPowerOfTwo(n // 2) +``` + +```java +public class Solution { + public boolean isPowerOfTwo(int n) { + if (n == 1) { + return true; + } + if (n <= 0 || n % 2 == 1) { + return false; + } + return isPowerOfTwo(n / 2); + } +} +``` + +```cpp +class Solution { +public: + bool isPowerOfTwo(int n) { + if (n == 1) { + return true; + } + if (n <= 0 || n % 2 == 1) { + return false; + } + return isPowerOfTwo(n / 2); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {boolean} + */ + isPowerOfTwo(n) { + if (n === 1) { + return true; + } + if (n <= 0 || n % 2 === 1) { + return false; + } + return this.isPowerOfTwo(Math.floor(n / 2)); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(\log n)$ +* Space complexity: $O(\log n)$ for recursion stack. + +--- + +## 3. Iteration + +::tabs-start + +```python +class Solution: + def isPowerOfTwo(self, n: int) -> bool: + if n <= 0: + return False + + while n % 2 == 0: + n >>= 1 + return n == 1 +``` + +```java +public class Solution { + public boolean isPowerOfTwo(int n) { + if (n <= 0) return false; + + while (n % 2 == 0) { + n >>= 1; + } + return n == 1; + } +} +``` + +```cpp +class Solution { +public: + bool isPowerOfTwo(int n) { + if (n <= 0) return false; + + while (n % 2 == 0) { + n >>= 1; + } + return n == 1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {boolean} + */ + isPowerOfTwo(n) { + if (n <= 0) return 0; + + while (n % 2 === 0) { + n >>= 1; + } + return n === 1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(\log n)$ +* Space complexity: $O(1)$ + +--- + +## 4. Bit Manipulation - I + +::tabs-start + +```python +class Solution: + def isPowerOfTwo(self, n: int) -> bool: + return n > 0 and (n & (-n)) == n +``` + +```java +public class Solution { + public boolean isPowerOfTwo(int n) { + return n > 0 && (n & (-n)) == n; + } +} +``` + +```cpp +class Solution { +public: + bool isPowerOfTwo(int n) { + return n > 0 && (n & (-n)) == n; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {boolean} + */ + isPowerOfTwo(n) { + return n > 0 && (n & (-n)) === n; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(1)$ +* Space complexity: $O(1)$ + +--- + +## 5. Bit Manipulation - II + +::tabs-start + +```python +class Solution: + def isPowerOfTwo(self, n: int) -> bool: + return n > 0 and (n & (n - 1)) == 0 +``` + +```java +public class Solution { + public boolean isPowerOfTwo(int n) { + return n > 0 && (n & (n - 1)) == 0; + } +} +``` + +```cpp +class Solution { +public: + bool isPowerOfTwo(int n) { + return n > 0 && (n & (n - 1)) == 0; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {boolean} + */ + isPowerOfTwo(n) { + return n > 0 && (n & (n - 1)) === 0; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(1)$ +* Space complexity: $O(1)$ + +--- + +## 6. Math + +::tabs-start + +```python +class Solution: + def isPowerOfTwo(self, n: int) -> bool: + return n > 0 and ((1 << 30) % n) == 0 +``` + +```java +public class Solution { + public boolean isPowerOfTwo(int n) { + return n > 0 && ((1 << 30) % n) == 0; + } +} +``` + +```cpp +class Solution { +public: + bool isPowerOfTwo(int n) { + return n > 0 && ((1 << 30) % n) == 0; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {boolean} + */ + isPowerOfTwo(n) { + return n > 0 && ((1 << 30) % n) === 0; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(1)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/robot-bounded-in-circle.md b/articles/robot-bounded-in-circle.md new file mode 100644 index 000000000..76ec62fc9 --- /dev/null +++ b/articles/robot-bounded-in-circle.md @@ -0,0 +1,107 @@ +## 1. Simulation + +::tabs-start + +```python +class Solution: + def isRobotBounded(self, instructions: str) -> bool: + dirX, dirY = 0, 1 + x, y = 0, 0 + + for d in instructions: + if d == "G": + x, y = x + dirX, y + dirY + elif d == "L": + dirX, dirY = -dirY, dirX + else: + dirX, dirY = dirY, -dirX + + return (x, y) == (0, 0) or (dirX, dirY) != (0, 1) +``` + +```java +public class Solution { + public boolean isRobotBounded(String instructions) { + int dirX = 0, dirY = 1; + int x = 0, y = 0; + + for (int i = 0; i < instructions.length(); i++) { + char d = instructions.charAt(i); + if (d == 'G') { + x += dirX; + y += dirY; + } else if (d == 'L') { + int temp = dirX; + dirX = -dirY; + dirY = temp; + } else { + int temp = dirX; + dirX = dirY; + dirY = -temp; + } + } + + return (x == 0 && y == 0) || (dirX != 0 || dirY != 1); + } +} +``` + +```cpp +class Solution { +public: + bool isRobotBounded(string instructions) { + int dirX = 0, dirY = 1; + int x = 0, y = 0; + + for (char d : instructions) { + if (d == 'G') { + x += dirX; + y += dirY; + } else if (d == 'L') { + int temp = dirX; + dirX = -dirY; + dirY = temp; + } else { + int temp = dirX; + dirX = dirY; + dirY = -temp; + } + } + + return (x == 0 && y == 0) || (dirX != 0 || dirY != 1); + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} instructions + * @return {boolean} + */ + isRobotBounded(instructions) { + let dirX = 0, dirY = 1; + let x = 0, y = 0; + + for (const d of instructions) { + if (d === 'G') { + x += dirX; + y += dirY; + } else if (d === 'L') { + [dirX, dirY] = [-dirY, dirX]; + } else { + [dirX, dirY] = [dirY, -dirX]; + } + } + + return (x === 0 && y === 0) || (dirX !== 0 || dirY !== 1); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. \ No newline at end of file diff --git a/articles/shift-2d-grid.md b/articles/shift-2d-grid.md new file mode 100644 index 000000000..78f570a31 --- /dev/null +++ b/articles/shift-2d-grid.md @@ -0,0 +1,502 @@ +## 1. Simulation (Extra Space) + +::tabs-start + +```python +class Solution: + def shiftGrid(self, grid: List[List[int]], k: int) -> List[List[int]]: + m, n = len(grid), len(grid[0]) + + while k: + cur = [[0] * n for _ in range(m)] + + for r in range(m): + for c in range(n - 1): + cur[r][c + 1] = grid[r][c] + + for r in range(m): + cur[(r + 1) % m][0] = grid[r][n - 1] + + grid = cur + k -= 1 + + return grid +``` + +```java +public class Solution { + public List> shiftGrid(int[][] grid, int k) { + int m = grid.length, n = grid[0].length; + + while (k > 0) { + int[][] cur = new int[m][n]; + + for (int r = 0; r < m; r++) { + for (int c = 0; c < n - 1; c++) { + cur[r][c + 1] = grid[r][c]; + } + } + + for (int r = 0; r < m; r++) { + cur[(r + 1) % m][0] = grid[r][n - 1]; + } + + grid = cur; + k--; + } + + List> res = new ArrayList<>(); + for (int r = 0; r < m; r++) { + List tmp = new ArrayList<>(); + for (int c = 0; c < n; c++) { + tmp.add(grid[r][c]); + } + res.add(tmp); + } + return res; + } +} +``` + +```cpp +class Solution { +public: + vector> shiftGrid(vector>& grid, int k) { + int m = grid.size(), n = grid[0].size(); + + while (k > 0) { + vector> cur(m, vector(n, 0)); + + for (int r = 0; r < m; r++) { + for (int c = 0; c < n - 1; c++) { + cur[r][c + 1] = grid[r][c]; + } + } + + for (int r = 0; r < m; r++) { + cur[(r + 1) % m][0] = grid[r][n - 1]; + } + + grid = cur; + k--; + } + + return grid; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} grid + * @param {number} k + * @return {number[][]} + */ + shiftGrid(grid, k) { + const m = grid.length, n = grid[0].length; + + while (k > 0) { + const cur = Array.from({ length: m }, () => Array(n).fill(0)); + + for (let r = 0; r < m; r++) { + for (let c = 0; c < n - 1; c++) { + cur[r][c + 1] = grid[r][c]; + } + } + + for (let r = 0; r < m; r++) { + cur[(r + 1) % m][0] = grid[r][n - 1]; + } + + grid = cur; + k--; + } + + return grid; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(k * m * n)$ +* Space complexity: $O(m * n)$ + +> Where $m$ is the number of rows in the grid, $n$ is the number of columns in the grid, and $k$ is the shift count. + +--- + +## 2. Simulation + +::tabs-start + +```python +class Solution: + def shiftGrid(self, grid: List[List[int]], k: int) -> List[List[int]]: + m, n = len(grid), len(grid[0]) + + while k: + prev = grid[m - 1][n - 1] + for r in range(m): + for c in range(n): + grid[r][c], prev = prev, grid[r][c] + k -= 1 + + return grid +``` + +```java +public class Solution { + public List> shiftGrid(int[][] grid, int k) { + int m = grid.length, n = grid[0].length; + + while (k > 0) { + int prev = grid[m - 1][n - 1]; + for (int r = 0; r < m; r++) { + for (int c = 0; c < n; c++) { + int temp = grid[r][c]; + grid[r][c] = prev; + prev = temp; + } + } + k--; + } + + List> res = new ArrayList<>(); + for (int r = 0; r < m; r++) { + List tmp = new ArrayList<>(); + for (int c = 0; c < n; c++) { + tmp.add(grid[r][c]); + } + res.add(tmp); + } + return res; + } +} +``` + +```cpp +class Solution { +public: + vector> shiftGrid(vector>& grid, int k) { + int m = grid.size(), n = grid[0].size(); + + while (k > 0) { + int prev = grid[m - 1][n - 1]; + for (int r = 0; r < m; r++) { + for (int c = 0; c < n; c++) { + swap(grid[r][c], prev); + } + } + k--; + } + + return grid; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} grid + * @param {number} k + * @return {number[][]} + */ + shiftGrid(grid, k) { + const m = grid.length, n = grid[0].length; + + while (k > 0) { + let prev = grid[m - 1][n - 1]; + for (let r = 0; r < m; r++) { + for (let c = 0; c < n; c++) { + [prev, grid[r][c]] = [grid[r][c], prev]; + } + } + k--; + } + + return grid; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(k * m * n)$ +* Space complexity: $O(m * n)$ for the output matrix. + +> Where $m$ is the number of rows in the grid, $n$ is the number of columns in the grid, and $k$ is the shift count. + +--- + +## 3. Convert to One Dimensional Array + +::tabs-start + +```python +class Solution: + def shiftGrid(self, grid: List[List[int]], k: int) -> List[List[int]]: + m, n = len(grid), len(grid[0]) + N = m * n + k %= N + + arr = [0] * N + for r in range(m): + for c in range(n): + arr[r * n + c] = grid[r][c] + + def reverse(l, r): + while l < r: + arr[l], arr[r] = arr[r], arr[l] + l += 1 + r -= 1 + + reverse(0, N - 1) + reverse(0, k - 1) + reverse(k, N - 1) + + for r in range(m): + for c in range(n): + grid[r][c] = arr[r * n + c] + + return grid +``` + +```java +public class Solution { + public List> shiftGrid(int[][] grid, int k) { + int m = grid.length, n = grid[0].length; + int N = m * n; + k %= N; + + int[] arr = new int[N]; + for (int r = 0; r < m; r++) { + for (int c = 0; c < n; c++) { + arr[r * n + c] = grid[r][c]; + } + } + + reverse(arr, 0, N - 1); + reverse(arr, 0, k - 1); + reverse(arr, k, N - 1); + + List> res = new ArrayList<>(); + for (int r = 0; r < m; r++) { + List tmp = new ArrayList<>(); + for (int c = 0; c < n; c++) { + tmp.add(arr[r * n + c]); + } + res.add(tmp); + } + return res; + } + + private void reverse(int[] arr, int l, int r) { + while (l < r) { + int temp = arr[l]; + arr[l] = arr[r]; + arr[r] = temp; + l++; + r--; + } + } +} +``` + +```cpp +class Solution { +public: + vector> shiftGrid(vector>& grid, int k) { + int m = grid.size(), n = grid[0].size(); + int N = m * n; + k %= N; + + vector arr(N); + for (int r = 0; r < m; r++) { + for (int c = 0; c < n; c++) { + arr[r * n + c] = grid[r][c]; + } + } + + reverse(arr.begin(), arr.end()); + reverse(arr.begin(), arr.begin() + k); + reverse(arr.begin() + k, arr.end()); + + for (int r = 0; r < m; r++) { + for (int c = 0; c < n; c++) { + grid[r][c] = arr[r * n + c]; + } + } + + return grid; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} grid + * @param {number} k + * @return {number[][]} + */ + shiftGrid(grid, k) { + const m = grid.length, n = grid[0].length; + const N = m * n; + k %= N; + + const arr = new Array(N); + for (let r = 0; r < m; r++) { + for (let c = 0; c < n; c++) { + arr[r * n + c] = grid[r][c]; + } + } + + const reverse = (l, r) => { + while (l < r) { + [arr[l], arr[r]] = [arr[r], arr[l]]; + l++; + r--; + } + }; + + reverse(0, N - 1); + reverse(0, k - 1); + reverse(k, N - 1); + + for (let r = 0; r < m; r++) { + for (let c = 0; c < n; c++) { + grid[r][c] = arr[r * n + c]; + } + } + + return grid; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n)$ +* Space complexity: $O(m * n)$ + +> Where $m$ is the number of rows in the grid and $n$ is the number of columns in the grid. + +--- + +## 4. Iteration + +::tabs-start + +```python +class Solution: + def shiftGrid(self, grid: List[List[int]], k: int) -> List[List[int]]: + M, N = len(grid), len(grid[0]) + + def posToVal(r, c): + return r * N + c + + def valToPos(v): + return [v // N, v % N] + + res = [[0] * N for _ in range(M)] + for r in range(M): + for c in range(N): + newVal = (posToVal(r, c) + k) % (M * N) + newR, newC = valToPos(newVal) + res[newR][newC] = grid[r][c] + + return res +``` + +```java +public class Solution { + public List> shiftGrid(int[][] grid, int k) { + int M = grid.length, N = grid[0].length; + int[][] arr = new int[M][N]; + + for (int r = 0; r < M; r++) { + for (int c = 0; c < N; c++) { + int newVal = (r * N + c + k) % (M * N); + int newR = newVal / N, newC = newVal % N; + arr[newR][newC] = grid[r][c]; + } + } + + List> res = new ArrayList<>(); + for (int r = 0; r < M; r++) { + List tmp = new ArrayList<>(); + for (int c = 0; c < N; c++) { + tmp.add(arr[r][c]); + } + res.add(tmp); + } + return res; + } + +} +``` + +```cpp +class Solution { +public: + vector> shiftGrid(vector>& grid, int k) { + int M = grid.size(), N = grid[0].size(); + vector> res(M, vector(N)); + + for (int r = 0; r < M; r++) { + for (int c = 0; c < N; c++) { + int newVal = (r * N + c + k) % (M * N); + int newR = newVal / N, newC = newVal % N; + res[newR][newC] = grid[r][c]; + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} grid + * @param {number} k + * @return {number[][]} + */ + shiftGrid(grid, k) { + const M = grid.length, N = grid[0].length; + + const posToVal = (r, c) => r * N + c; + const valToPos = (v) => [Math.floor(v / N), v % N]; + + const res = Array.from({ length: M }, () => Array(N).fill(0)); + for (let r = 0; r < M; r++) { + for (let c = 0; c < N; c++) { + const newVal = (posToVal(r, c) + k) % (M * N); + const [newR, newC] = valToPos(newVal); + res[newR][newC] = grid[r][c]; + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n)$ +* Space complexity: $O(m * n)$ + +> Where $m$ is the number of rows in the grid and $n$ is the number of columns in the grid. \ No newline at end of file diff --git a/articles/shuffle-the-array.md b/articles/shuffle-the-array.md new file mode 100644 index 000000000..eded033b7 --- /dev/null +++ b/articles/shuffle-the-array.md @@ -0,0 +1,257 @@ +## 1. Iteration (Extra Space) + +::tabs-start + +```python +class Solution: + def shuffle(self, nums: List[int], n: int) -> List[int]: + res = [] + for i in range(n): + res.append(nums[i]) + res.append(nums[i + n]) + return res +``` + +```java +public class Solution { + public int[] shuffle(int[] nums, int n) { + int[] res = new int[2 * n]; + int idx = 0; + for (int i = 0; i < n; i++) { + res[idx++] = nums[i]; + res[idx++] = nums[i + n]; + } + return res; + } +} +``` + +```cpp +class Solution { +public: + vector shuffle(vector& nums, int n) { + vector res; + for (int i = 0; i < n; i++) { + res.push_back(nums[i]); + res.push_back(nums[i + n]); + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} n + * @return {number[]} + */ + shuffle(nums) { + const res = []; + for (let i = 0; i < n; i++) { + res.push(nums[i]); + res.push(nums[i + n]); + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ extra space. + +--- + +## 2. Multiplication And Modulo + +::tabs-start + +```python +class Solution: + def shuffle(self, nums: List[int], n: int) -> List[int]: + M = max(nums) + 1 + for i in range(2 * n): + if i % 2 == 0: + nums[i] += (nums[i // 2] % M) * M + else: + nums[i] += (nums[n + i // 2] % M) * M + + for i in range(2 * n): + nums[i] //= M + + return nums +``` + +```java +public class Solution { + public int[] shuffle(int[] nums, int n) { + int M = 1001; + for (int i = 0; i < 2 * n; i++) { + if (i % 2 == 0) { + nums[i] += (nums[i / 2] % M) * M; + } else { + nums[i] += (nums[n + i / 2] % M) * M; + } + } + for (int i = 0; i < 2 * n; i++) { + nums[i] /= M; + } + return nums; + } +} +``` + +```cpp +class Solution { +public: + vector shuffle(vector& nums, int n) { + int M = *max_element(nums.begin(), nums.end()) + 1; + for (int i = 0; i < 2 * n; i++) { + if (i % 2 == 0) { + nums[i] += (nums[i / 2] % M) * M; + } else { + nums[i] += (nums[n + i / 2] % M) * M; + } + } + for (int i = 0; i < 2 * n; i++) { + nums[i] /= M; + } + return nums; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} n + * @return {number[]} + */ + shuffle(nums) { + const M = Math.max(...nums) + 1; + for (let i = 0; i < 2 * n; i++) { + if (i % 2 === 0) { + nums[i] += (nums[i >> 1] % M) * M; + } else { + nums[i] += (nums[n + (i >> 1)] % M) * M; + } + } + for (let i = 0; i < 2 * n; i++) { + nums[i] = Math.floor(nums[i] / M); + } + return nums; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. + +--- + +## 3. Bit Manipulation + +::tabs-start + +```python +class Solution: + def shuffle(self, nums: List[int], n: int) -> List[int]: + for i in range(n): + nums[i] = (nums[i] << 10) | nums[i + n] # Store x, y in nums[i] + + j = 2 * n - 1 + for i in range(n - 1, -1, -1): + y = nums[i] & ((1 << 10) - 1) + x = nums[i] >> 10 + nums[j] = y + nums[j - 1] = x + j -= 2 + + return nums +``` + +```java +public class Solution { + public int[] shuffle(int[] nums, int n) { + for (int i = 0; i < n; i++) { + nums[i] = (nums[i] << 10) | nums[i + n]; // Store x, y in nums[i] + } + + int j = 2 * n - 1; + for (int i = n - 1; i >= 0; i--) { + int y = nums[i] & ((1 << 10) - 1); + int x = nums[i] >> 10; + nums[j] = y; + nums[j - 1] = x; + j -= 2; + } + + return nums; + } +} +``` + +```cpp +class Solution { +public: + vector shuffle(vector& nums, int n) { + for (int i = 0; i < n; i++) { + nums[i] = (nums[i] << 10) | nums[i + n]; // Store x, y in nums[i] + } + + int j = 2 * n - 1; + for (int i = n - 1; i >= 0; i--) { + int y = nums[i] & ((1 << 10) - 1); + int x = nums[i] >> 10; + nums[j] = y; + nums[j - 1] = x; + j -= 2; + } + + return nums; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} n + * @return {number[]} + */ + shuffle(nums) { + for (let i = 0; i < n; i++) { + nums[i] = (nums[i] << 10) | nums[i + n]; // Store x, y in nums[i] + } + + let j = 2 * n - 1; + for (let i = n - 1; i >= 0; i--) { + let y = nums[i] & ((1 << 10) - 1); + let x = nums[i] >> 10; + nums[j] = y; + nums[j - 1] = x; + j -= 2; + } + + return nums; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. \ No newline at end of file diff --git a/articles/single-number-iii.md b/articles/single-number-iii.md new file mode 100644 index 000000000..5e714092d --- /dev/null +++ b/articles/single-number-iii.md @@ -0,0 +1,608 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def singleNumber(self, nums: List[int]) -> List[int]: + n, res = len(nums), [] + + for i in range(n): + flag = True + for j in range(n): + if i != j and nums[i] == nums[j]: + flag = False + break + + if flag: + res.append(nums[i]) + if len(res) == 2: + break + + return res +``` + +```java +public class Solution { + public int[] singleNumber(int[] nums) { + int n = nums.length; + List res = new ArrayList<>(); + + for (int i = 0; i < n; i++) { + boolean flag = true; + for (int j = 0; j < n; j++) { + if (i != j && nums[i] == nums[j]) { + flag = false; + break; + } + } + + if (flag) { + res.add(nums[i]); + if (res.size() == 2) { + break; + } + } + } + + return new int[] {res.get(0), res.get(1)}; + } +} +``` + +```cpp +class Solution { +public: + vector singleNumber(vector& nums) { + int n = nums.size(); + vector res; + + for (int i = 0; i < n; i++) { + bool flag = true; + for (int j = 0; j < n; j++) { + if (i != j && nums[i] == nums[j]) { + flag = false; + break; + } + } + + if (flag) { + res.push_back(nums[i]); + if (res.size() == 2) { + break; + } + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number[]} + */ + singleNumber(nums) { + const n = nums.length; + const res = []; + + for (let i = 0; i < n; i++) { + let flag = true; + for (let j = 0; j < n; j++) { + if (i !== j && nums[i] === nums[j]) { + flag = false; + break; + } + } + + if (flag) { + res.push(nums[i]); + if (res.length === 2) { + break; + } + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(1)$ extra space. + +--- + +## 2. Hash Map + +::tabs-start + +```python +class Solution: + def singleNumber(self, nums: List[int]) -> List[int]: + count = {} + for num in nums: + count[num] = 1 + count.get(num, 0) + + return [k for k in count if count[k] == 1] +``` + +```java +public class Solution { + public int[] singleNumber(int[] nums) { + Map count = new HashMap<>(); + for (int num : nums) { + count.put(num, count.getOrDefault(num, 0) + 1); + } + + ArrayList res = new ArrayList<>(); + for (int key : count.keySet()) { + if (count.get(key) == 1) { + res.add(key); + } + } + + return new int[] {res.get(0), res.get(1)}; + } +} +``` + +```cpp +class Solution { +public: + vector singleNumber(vector& nums) { + unordered_map count; + for (int num : nums) { + count[num]++; + } + + vector res; + for (const auto& pair : count) { + if (pair.second == 1) { + res.push_back(pair.first); + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number[]} + */ + singleNumber(nums) { + const count = new Map(); + for (const num of nums) { + count.set(num, (count.get(num) || 0) + 1); + } + + const res = []; + for (const [key, value] of count) { + if (value === 1) { + res.push(key); + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Hash Set + +::tabs-start + +```python +class Solution: + def singleNumber(self, nums: List[int]) -> List[int]: + seen = set() + for num in nums: + if num in seen: + seen.remove(num) + else: + seen.add(num) + return list(seen) +``` + +```java +public class Solution { + public int[] singleNumber(int[] nums) { + HashSet seen = new HashSet<>(); + for (int num : nums) { + if (seen.contains(num)) { + seen.remove(num); + } else { + seen.add(num); + } + } + + int[] res = new int[2]; + int index = 0; + for (int num : seen) { + res[index++] = num; + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector singleNumber(vector& nums) { + unordered_set seen; + for (int& num : nums) { + if (seen.count(num)) { + seen.erase(num); + } else { + seen.insert(num); + } + } + + return vector(seen.begin(), seen.end()); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number[]} + */ + singleNumber(nums) { + const seen = new Set(); + for (const num of nums) { + if (seen.has(num)) { + seen.delete(num); + } else { + seen.add(num); + } + } + + return Array.from(seen); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 4. Sorting + +::tabs-start + +```python +class Solution: + def singleNumber(self, nums: List[int]) -> List[int]: + res, n = [], len(nums) + nums.sort() + + for i in range(n): + if ((i > 0 and nums[i] == nums[i - 1]) or + (i + 1 < n and nums[i] == nums[i + 1])): + continue + res.append(nums[i]) + + return res +``` + +```java +public class Solution { + public int[] singleNumber(int[] nums) { + Arrays.sort(nums); + List res = new ArrayList<>(); + int n = nums.length; + + for (int i = 0; i < n; i++) { + if ((i > 0 && nums[i] == nums[i - 1]) || + (i + 1 < n && nums[i] == nums[i + 1])) { + continue; + } + res.add(nums[i]); + } + + return res.stream().mapToInt(i -> i).toArray(); + } +} +``` + +```cpp +class Solution { +public: + vector singleNumber(vector& nums) { + sort(nums.begin(), nums.end()); + vector res; + int n = nums.size(); + + for (int i = 0; i < n; i++) { + if ((i > 0 && nums[i] == nums[i - 1]) || + (i + 1 < n && nums[i] == nums[i + 1])) { + continue; + } + res.push_back(nums[i]); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number[]} + */ + singleNumber(nums) { + nums.sort((a, b) => a - b); + const res = []; + const n = nums.length; + + for (let i = 0; i < n; i++) { + if ((i > 0 && nums[i] === nums[i - 1]) || + (i + 1 < n && nums[i] === nums[i + 1])) { + continue; + } + res.push(nums[i]); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(1)$ or $O(n)$ depending on the sorting algorithm. + +--- + +## 5. Bitwise XOR (Least Significant Bit) + +::tabs-start + +```python +class Solution: + def singleNumber(self, nums: List[int]) -> List[int]: + xor = 0 + for num in nums: + xor ^= num + + diff_bit = 1 + while not (xor & diff_bit): + diff_bit <<= 1 + + a = b = 0 + for num in nums: + if diff_bit & num: + a ^= num + else: + b ^= num + return [a, b] +``` + +```java +public class Solution { + public int[] singleNumber(int[] nums) { + int xor = 0; + for (int num : nums) { + xor ^= num; + } + + int diff_bit = 1; + while ((xor & diff_bit) == 0) { + diff_bit <<= 1; + } + + int a = 0, b = 0; + for (int num : nums) { + if ((num & diff_bit) != 0) { + a ^= num; + } else { + b ^= num; + } + } + return new int[]{a, b}; + } +} +``` + +```cpp +class Solution { +public: + vector singleNumber(vector& nums) { + int xor_all = 0; + for (int& num : nums) { + xor_all ^= num; + } + + int diff_bit = 1; + while ((xor_all & diff_bit) == 0) { + diff_bit <<= 1; + } + + int a = 0, b = 0; + for (int& num : nums) { + if (num & diff_bit) { + a ^= num; + } else { + b ^= num; + } + } + return {a, b}; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number[]} + */ + singleNumber(nums) { + let xor = 0; + for (const num of nums) { + xor ^= num; + } + + let diff_bit = 1; + while ((xor & diff_bit) === 0) { + diff_bit <<= 1; + } + + let a = 0, b = 0; + for (const num of nums) { + if (num & diff_bit) { + a ^= num; + } else { + b ^= num; + } + } + return [a, b]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. + +--- + +## 6. Bitwise XOR (Most Significant Bit) + +::tabs-start + +```python +class Solution: + def singleNumber(self, nums: List[int]) -> List[int]: + xor = 0 + for num in nums: + xor ^= num + + diff_bit = xor & (-xor) + + a = b = 0 + for num in nums: + if diff_bit & num: + a ^= num + else: + b ^= num + return [a, b] +``` + +```java +public class Solution { + public int[] singleNumber(int[] nums) { + int xor = 0; + for (int num : nums) { + xor ^= num; + } + + int diff_bit = xor & (-xor); + + int a = 0, b = 0; + for (int num : nums) { + if ((num & diff_bit) != 0) { + a ^= num; + } else { + b ^= num; + } + } + return new int[]{a, b}; + } +} +``` + +```cpp +class Solution { +public: + vector singleNumber(vector& nums) { + uint xor_all = 0; + for (int& num : nums) { + xor_all ^= num; + } + + int diff_bit = xor_all & (-xor_all); + + int a = 0, b = 0; + for (int& num : nums) { + if (num & diff_bit) { + a ^= num; + } else { + b ^= num; + } + } + return {a, b}; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number[]} + */ + singleNumber(nums) { + let xor = 0; + for (const num of nums) { + xor ^= num; + } + + let diff_bit = xor & (-xor); + + let a = 0, b = 0; + for (const num of nums) { + if (num & diff_bit) { + a ^= num; + } else { + b ^= num; + } + } + return [a, b]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. \ No newline at end of file diff --git a/articles/spiral-matrix-ii.md b/articles/spiral-matrix-ii.md new file mode 100644 index 000000000..1df46f786 --- /dev/null +++ b/articles/spiral-matrix-ii.md @@ -0,0 +1,454 @@ +## 1. Iteration + +::tabs-start + +```python +class Solution: + def generateMatrix(self, n: int) -> List[List[int]]: + mat = [[0] * n for _ in range(n)] + left, right = 0, n - 1 + top, bottom = 0, n - 1 + val = 1 + + while left <= right: + # Fill every val in top row + for c in range(left, right + 1): + mat[top][c] = val + val += 1 + top += 1 + + # Fill every val in right col + for r in range(top, bottom + 1): + mat[r][right] = val + val += 1 + right -= 1 + + # Fill every val in bottom row (reverse order) + for c in range(right, left - 1, -1): + mat[bottom][c] = val + val += 1 + bottom -= 1 + + # Fill every val in the left col (reverse order) + for r in range(bottom, top - 1, -1): + mat[r][left] = val + val += 1 + left += 1 + + return mat +``` + +```java +public class Solution { + public int[][] generateMatrix(int n) { + int[][] mat = new int[n][n]; + int left = 0, right = n - 1, top = 0, bottom = n - 1, val = 1; + + while (left <= right) { + // Fill every val in top row + for (int c = left; c <= right; c++) { + mat[top][c] = val++; + } + top++; + + // Fill every val in right col + for (int r = top; r <= bottom; r++) { + mat[r][right] = val++; + } + right--; + + // Fill every val in bottom row (reverse order) + for (int c = right; c >= left; c--) { + mat[bottom][c] = val++; + } + bottom--; + + // Fill every val in the left col (reverse order) + for (int r = bottom; r >= top; r--) { + mat[r][left] = val++; + } + left++; + } + + return mat; + } +} +``` + +```cpp +class Solution { +public: + vector> generateMatrix(int n) { + vector> mat(n, vector(n, 0)); + int left = 0, right = n - 1, top = 0, bottom = n - 1, val = 1; + + while (left <= right) { + // Fill every val in top row + for (int c = left; c <= right; c++) { + mat[top][c] = val++; + } + top++; + + // Fill every val in right col + for (int r = top; r <= bottom; r++) { + mat[r][right] = val++; + } + right--; + + // Fill every val in bottom row (reverse order) + for (int c = right; c >= left; c--) { + mat[bottom][c] = val++; + } + bottom--; + + // Fill every val in the left col (reverse order) + for (int r = bottom; r >= top; r--) { + mat[r][left] = val++; + } + left++; + } + + return mat; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {number[][]} + */ + generateMatrix(n) { + const mat = Array.from({ length: n }, () => Array(n).fill(0)); + let left = 0, right = n - 1, top = 0, bottom = n - 1, val = 1; + + while (left <= right) { + // Fill every val in top row + for (let c = left; c <= right; c++) { + mat[top][c] = val++; + } + top++; + + // Fill every val in right col + for (let r = top; r <= bottom; r++) { + mat[r][right] = val++; + } + right--; + + // Fill every val in bottom row (reverse order) + for (let c = right; c >= left; c--) { + mat[bottom][c] = val++; + } + bottom--; + + // Fill every val in the left col (reverse order) + for (let r = bottom; r >= top; r--) { + mat[r][left] = val++; + } + left++; + } + + return mat; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n ^ 2)$ for the output matrix. + +--- + +## 2. Recursion + +::tabs-start + +```python +class Solution: + def generateMatrix(self, n: int) -> List[List[int]]: + mat = [[0] * n for _ in range(n)] + + def fill(left, right, top, bottom, val): + if left > right or top > bottom: + return + + # Fill every val in top row + for c in range(left, right + 1): + mat[top][c] = val + val += 1 + top += 1 + + # Fill every val in right col + for r in range(top, bottom + 1): + mat[r][right] = val + val += 1 + right -= 1 + + # Fill every val in bottom row (reverse order) + for c in range(right, left - 1, -1): + mat[bottom][c] = val + val += 1 + bottom -= 1 + + # Fill every val in the left col (reverse order) + for r in range(bottom, top - 1, -1): + mat[r][left] = val + val += 1 + left += 1 + + # Recur for the inner layer + fill(left, right, top, bottom, val) + + fill(0, n - 1, 0, n - 1, 1) + return mat +``` + +```java +public class Solution { + public int[][] generateMatrix(int n) { + int[][] mat = new int[n][n]; + fill(mat, 0, n - 1, 0, n - 1, 1); + return mat; + } + + private void fill(int[][] mat, int left, int right, int top, int bottom, int val) { + if (left > right || top > bottom) return; + + // Fill every val in top row + for (int c = left; c <= right; c++) { + mat[top][c] = val++; + } + top++; + + // Fill every val in right col + for (int r = top; r <= bottom; r++) { + mat[r][right] = val++; + } + right--; + + // Fill every val in bottom row (reverse order) + for (int c = right; c >= left; c--) { + mat[bottom][c] = val++; + } + bottom--; + + // Fill every val in the left col (reverse order) + for (int r = bottom; r >= top; r--) { + mat[r][left] = val++; + } + left++; + + // Recur for the inner layer + fill(mat, left, right, top, bottom, val); + } +} +``` + +```cpp +class Solution { +public: + vector> generateMatrix(int n) { + vector> mat(n, vector(n, 0)); + fill(mat, 0, n - 1, 0, n - 1, 1); + return mat; + } + +private: + void fill(vector> &mat, int left, int right, int top, int bottom, int val) { + if (left > right || top > bottom) return; + + // Fill every val in top row + for (int c = left; c <= right; c++) { + mat[top][c] = val++; + } + top++; + + // Fill every val in right col + for (int r = top; r <= bottom; r++) { + mat[r][right] = val++; + } + right--; + + // Fill every val in bottom row (reverse order) + for (int c = right; c >= left; c--) { + mat[bottom][c] = val++; + } + bottom--; + + // Fill every val in the left col (reverse order) + for (int r = bottom; r >= top; r--) { + mat[r][left] = val++; + } + left++; + + // Recur for the inner layer + fill(mat, left, right, top, bottom, val); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {number[][]} + */ + generateMatrix(n) { + const mat = Array.from({ length: n }, () => Array(n).fill(0)); + + const fill = (left, right, top, bottom, val) => { + if (left > right || top > bottom) return; + + // Fill every val in top row + for (let c = left; c <= right; c++) { + mat[top][c] = val++; + } + top++; + + // Fill every val in right col + for (let r = top; r <= bottom; r++) { + mat[r][right] = val++; + } + right--; + + // Fill every val in bottom row (reverse order) + for (let c = right; c >= left; c--) { + mat[bottom][c] = val++; + } + bottom--; + + // Fill every val in the left col (reverse order) + for (let r = bottom; r >= top; r--) { + mat[r][left] = val++; + } + left++; + + // Recur for the inner layer + fill(left, right, top, bottom, val); + }; + + fill(0, n - 1, 0, n - 1, 1); + return mat; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: + * $O(n)$ space for recursion stack. + * $O(n ^ 2)$ space for the output matrix. + +--- + +## 3. Iteration (Optimal) + +::tabs-start + +```python +class Solution: + def generateMatrix(self, n: int) -> List[List[int]]: + mat = [[0] * n for _ in range(n)] + r = c = 0 + dr, dc = 0, 1 + + for val in range(n * n): + mat[r][c] = val + 1 + if mat[(r + dr) % n][(c + dc) % n] != 0: + dr, dc = dc, -dr + r, c = r + dr, c + dc + + return mat +``` + +```java +public class Solution { + public int[][] generateMatrix(int n) { + int[][] mat = new int[n][n]; + int r = 0, c = 0, dr = 0, dc = 1; + + for (int val = 0; val < n * n; val++) { + mat[r][c] = val + 1; + int nextR = (r + dr) % n, nextC = (c + dc) % n; + if (nextR < 0) nextR += n; + if (nextC < 0) nextC += n; + if (mat[nextR][nextC] != 0) { + int temp = dr; + dr = dc; + dc = -temp; + } + r += dr; + c += dc; + } + + return mat; + } +} +``` + +```cpp +class Solution { +public: + vector> generateMatrix(int n) { + vector> mat(n, vector(n, 0)); + int r = 0, c = 0, dr = 0, dc = 1; + + for (int val = 0; val < n * n; val++) { + mat[r][c] = val + 1; + int nextR = (r + dr) % n, nextC = (c + dc) % n; + if (nextR < 0) nextR += n; + if (nextC < 0) nextC += n; + if (mat[nextR][nextC] != 0) { + int temp = dr; + dr = dc; + dc = -temp; + } + r += dr; + c += dc; + } + + return mat; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {number[][]} + */ + generateMatrix(n) { + const mat = Array.from({ length: n }, () => Array(n).fill(0)); + let r = 0, c = 0, dr = 0, dc = 1; + + for (let val = 0; val < n * n; val++) { + mat[r][c] = val + 1; + let nextR = (r + dr) % n, nextC = (c + dc) % n; + if (nextR < 0) nextR += n; + if (nextC < 0) nextC += n; + if (mat[nextR][nextC] !== 0) { + [dr, dc] = [dc, -dr]; + } + r += dr; + c += dc; + } + + return mat; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n ^ 2)$ for the output matrix. \ No newline at end of file diff --git a/articles/ugly-number.md b/articles/ugly-number.md new file mode 100644 index 000000000..f2a68a2e5 --- /dev/null +++ b/articles/ugly-number.md @@ -0,0 +1,76 @@ +## 1. Math + +::tabs-start + +```python +class Solution: + def isUgly(self, n: int) -> bool: + if n <= 0: + return False + + for p in [2, 3, 5]: + while n % p == 0: + n //= p + + return n == 1 +``` + +```java +public class Solution { + public boolean isUgly(int n) { + if (n <= 0) return false; + + for (int p = 2; p <= 5 && n > 0; p++) { + while (n % p == 0) { + n /= p; + } + } + + return n == 1; + } +} +``` + +```cpp +class Solution { +public: + bool isUgly(int n) { + if (n <= 0) return false; + + for (int p = 2; p <= 5 && n > 0; p++) { + while (n % p == 0) { + n /= p; + } + } + + return n == 1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {boolean} + */ + isUgly(n) { + if (n <= 0) return false; + + for (let p of [2, 3, 5]) { + while (n % p == 0) { + n /= p; + } + } + + return n === 1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(\log n)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/zigzag-conversion.md b/articles/zigzag-conversion.md new file mode 100644 index 000000000..5df4dafff --- /dev/null +++ b/articles/zigzag-conversion.md @@ -0,0 +1,226 @@ +## 1. Iteration - I + +::tabs-start + +```python +class Solution: + def convert(self, s: str, numRows: int) -> str: + if numRows == 1: + return s + + res = [] + for r in range(numRows): + increment = 2 * (numRows - 1) + for i in range(r, len(s), increment): + res.append(s[i]) + if r > 0 and r < numRows - 1 and i + increment - 2 * r < len(s): + res.append(s[i + increment - 2 * r]) + + return ''.join(res) +``` + +```java +public class Solution { + public String convert(String s, int numRows) { + if (numRows == 1) { + return s; + } + + StringBuilder res = new StringBuilder(); + int len = s.length(); + + for (int r = 0; r < numRows; r++) { + int increment = 2 * (numRows - 1); + for (int i = r; i < len; i += increment) { + res.append(s.charAt(i)); + if (r > 0 && r < numRows - 1 && i + increment - 2 * r < len) { + res.append(s.charAt(i + increment - 2 * r)); + } + } + } + + return res.toString(); + } +} +``` + +```cpp +class Solution { +public: + string convert(string s, int numRows) { + if (numRows == 1) { + return s; + } + + string res; + int len = s.size(); + + for (int r = 0; r < numRows; r++) { + int increment = 2 * (numRows - 1); + for (int i = r; i < len; i += increment) { + res += s[i]; + if (r > 0 && r < numRows - 1 && i + increment - 2 * r < len) { + res += s[i + increment - 2 * r]; + } + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @param {number} numRows + * @return {string} + */ + convert(s, numRows) { + if (numRows === 1) { + return s; + } + + let res = []; + const len = s.length; + + for (let r = 0; r < numRows; r++) { + const increment = 2 * (numRows - 1); + for (let i = r; i < len; i += increment) { + res.push(s[i]); + if (r > 0 && r < numRows - 1 && i + increment - 2 * r < len) { + res.push(s[i + increment - 2 * r]); + } + } + } + + return res.join(''); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ for the ouput string. + +--- + +## 2. Iteration - II + +::tabs-start + +```python +class Solution: + def convert(self, s: str, numRows: int) -> str: + if numRows == 1 or numRows >= len(s): + return s + + res = [[] for _ in range(numRows)] + row, dir = 0, 1 + for c in s: + res[row].append(c) + row += dir + if row == 0 or row == (numRows - 1): + dir *= -1 + + return ''.join([''.join(row) for row in res]) +``` + +```java +public class Solution { + public String convert(String s, int numRows) { + if (numRows == 1 || numRows >= s.length()) { + return s; + } + + List[] res = new ArrayList[numRows]; + for (int i = 0; i < numRows; i++) { + res[i] = new ArrayList<>(); + } + + int row = 0, dir = 1; + for (int i = 0; i < s.length(); i++) { + res[row].add(s.charAt(i)); + row += dir; + if (row == 0 || row == numRows - 1) { + dir *= -1; + } + } + + StringBuilder result = new StringBuilder(); + for (List rowList : res) { + for (char c : rowList) { + result.append(c); + } + } + return result.toString(); + } +} +``` + +```cpp +class Solution { +public: + string convert(string s, int numRows) { + if (numRows == 1 || numRows >= s.size()) { + return s; + } + + vector res(numRows); + int row = 0, dir = 1; + + for (char& c : s) { + res[row] += c; + row += dir; + if (row == 0 || row == numRows - 1) { + dir *= -1; + } + } + + string result; + for (string& rowString : res) { + result += rowString; + } + return result; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @param {number} numRows + * @return {string} + */ + convert(s, numRows) { + if (numRows === 1 || numRows >= s.length) { + return s; + } + + const res = Array.from({ length: numRows }, () => []); + let row = 0, dir = 1; + + for (const c of s) { + res[row].push(c); + row += dir; + if (row === 0 || row === numRows - 1) { + dir *= -1; + } + } + + return res.map(row => row.join("")).join(""); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ for the output string. \ No newline at end of file From 1063ce2cb9752df4cc6772ac07bb4a087bfcacd1 Mon Sep 17 00:00:00 2001 From: Sri Hari <94112314+Srihari2222@users.noreply.github.com> Date: Thu, 23 Jan 2025 01:52:11 +0530 Subject: [PATCH 29/45] Sri Hari: Batch-5/Neetcode-ALL/Added-articles (#3822) * Batch-5/Neetcode-ALL/Added-articles * Batch-5/Neetcode-ALL/Added-articles * Batch-5/Neetcode-ALL/Added-articles --- ...ree-from-preorder-and-inorder-traversal.md | 6 +- ...inary-tree-zigzag-level-order-traversal.md | 647 ++++++++++++++ .../check-completeness-of-a-binary-tree.md | 591 ++++++++++++ ...ee-from-inorder-and-postorder-traversal.md | 470 ++++++++++ articles/construct-string-from-binary-tree.md | 518 +++++++++++ ...vert-sorted-array-to-binary-search-tree.md | 437 +++++++++ ...s-that-can-form-two-arrays-of-equal-xor.md | 432 +++++++++ articles/design-twitter-feed.md | 733 ++++++++++++++- articles/evaluate-boolean-binary-tree.md | 309 +++++++ articles/find-duplicate-subtrees.md | 550 ++++++++++++ articles/invert-a-binary-tree.md | 2 +- articles/leaf-similar-trees.md | 504 +++++++++++ articles/maximum-width-of-binary-tree.md | 513 +++++++++++ articles/merge-two-binary-trees.md | 658 ++++++++++++++ .../minimum-distance-between-bst-nodes.md | 840 ++++++++++++++++++ ...um-time-to-collect-all-apples-in-a-tree.md | 312 +++++++ articles/path-sum.md | 581 ++++++++++++ articles/range-sum-of-bst.md | 408 +++++++++ articles/same-binary-tree.md | 433 +++++++-- articles/sliding-window-median.md | 16 +- articles/symmetric-tree.md | 447 ++++++++++ .../time-needed-to-inform-all-employees.md | 479 ++++++++++ articles/unique-binary-search-trees.md | 411 +++++++++ hints/design-twitter-feed.md | 2 +- 24 files changed, 10167 insertions(+), 132 deletions(-) create mode 100644 articles/binary-tree-zigzag-level-order-traversal.md create mode 100644 articles/check-completeness-of-a-binary-tree.md create mode 100644 articles/construct-binary-tree-from-inorder-and-postorder-traversal.md create mode 100644 articles/construct-string-from-binary-tree.md create mode 100644 articles/convert-sorted-array-to-binary-search-tree.md create mode 100644 articles/count-triplets-that-can-form-two-arrays-of-equal-xor.md create mode 100644 articles/evaluate-boolean-binary-tree.md create mode 100644 articles/find-duplicate-subtrees.md create mode 100644 articles/leaf-similar-trees.md create mode 100644 articles/maximum-width-of-binary-tree.md create mode 100644 articles/merge-two-binary-trees.md create mode 100644 articles/minimum-distance-between-bst-nodes.md create mode 100644 articles/minimum-time-to-collect-all-apples-in-a-tree.md create mode 100644 articles/path-sum.md create mode 100644 articles/range-sum-of-bst.md create mode 100644 articles/symmetric-tree.md create mode 100644 articles/time-needed-to-inform-all-employees.md create mode 100644 articles/unique-binary-search-trees.md diff --git a/articles/binary-tree-from-preorder-and-inorder-traversal.md b/articles/binary-tree-from-preorder-and-inorder-traversal.md index 0e2049976..621fc91cd 100644 --- a/articles/binary-tree-from-preorder-and-inorder-traversal.md +++ b/articles/binary-tree-from-preorder-and-inorder-traversal.md @@ -240,11 +240,11 @@ class Solution { ### Time & Space Complexity * Time complexity: $O(n ^ 2)$ -* Space complexity: $O(n ^ 2)$ +* Space complexity: $O(n)$ --- -## 2. Hash Map +## 2. Hash Map + Depth First Search ::tabs-start @@ -780,4 +780,4 @@ class Solution { ### Time & Space Complexity * Time complexity: $O(n)$ -* Space complexity: $O(n)$ \ No newline at end of file +* Space complexity: $O(n)$ for recursion stack. \ No newline at end of file diff --git a/articles/binary-tree-zigzag-level-order-traversal.md b/articles/binary-tree-zigzag-level-order-traversal.md new file mode 100644 index 000000000..d41aac9cd --- /dev/null +++ b/articles/binary-tree-zigzag-level-order-traversal.md @@ -0,0 +1,647 @@ +## 1. Breadth First Search + +::tabs-start + +```python +# 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 +class Solution: + def zigzagLevelOrder(self, root: Optional[TreeNode]) -> List[List[int]]: + res = [] + q = deque([root] if root else []) + while q: + level = [] + for i in range(len(q)): + node = q.popleft() + level.append(node.val) + if node.left: + q.append(node.left) + if node.right: + q.append(node.right) + if len(res) % 2: + level.reverse() + res.append(level) + return res +``` + +```java +/** + * 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; + * } + * } + */ +public class Solution { + public List> zigzagLevelOrder(TreeNode root) { + List> res = new ArrayList<>(); + if (root == null) return res; + + Queue q = new LinkedList<>(); + q.offer(root); + + while (!q.isEmpty()) { + List level = new ArrayList<>(); + for (int i = q.size(); i > 0; i--) { + TreeNode node = q.poll(); + level.add(node.val); + if (node.left != null) q.offer(node.left); + if (node.right != null) q.offer(node.right); + } + if (res.size() % 2 != 0) Collections.reverse(level); + res.add(level); + } + return res; + } +} +``` + +```cpp +/** + * 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: + vector> zigzagLevelOrder(TreeNode* root) { + vector> res; + if (!root) return res; + + queue q; + q.push(root); + + while (!q.empty()) { + vector level; + for (int i = q.size(); i > 0; i--) { + TreeNode* node = q.front(); + q.pop(); + level.push_back(node->val); + if (node->left) q.push(node->left); + if (node->right) q.push(node->right); + } + if (res.size() % 2) reverse(level.begin(), level.end()); + res.push_back(level); + } + return res; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {number[][]} + */ + zigzagLevelOrder(root) { + const res = []; + if (!root) return res; + + const queue = new Queue([root]); + + while (!queue.isEmpty()) { + const level = []; + for (let i = queue.size(); i > 0; i--) { + const node = queue.pop(); + level.push(node.val); + if (node.left) queue.push(node.left); + if (node.right) queue.push(node.right); + } + if (res.length % 2 !== 0) level.reverse(); + res.push(level); + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Breadth First Search (Optimal) + +::tabs-start + +```python +# 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 +class Solution: + def zigzagLevelOrder(self, root: Optional[TreeNode]) -> List[List[int]]: + res = [] + q = deque([root] if root else []) + while q: + size = len(q) + level = [0] * size + for i in range(size): + node = q.popleft() + idx = size - i - 1 if len(res) % 2 else i + level[idx] = node.val + if node.left: + q.append(node.left) + if node.right: + q.append(node.right) + res.append(level) + return res +``` + +```java +/** + * 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; + * } + * } + */ +public class Solution { + public List> zigzagLevelOrder(TreeNode root) { + List> res = new ArrayList<>(); + if (root == null) return res; + + Queue q = new LinkedList<>(); + q.offer(root); + + while (!q.isEmpty()) { + int size = q.size(); + Integer[] level = new Integer[size]; + for (int i = 0; i < size; i++) { + TreeNode node = q.poll(); + int idx = (res.size() % 2 == 0) ? i : size - i - 1; + level[idx] = node.val; + if (node.left != null) q.offer(node.left); + if (node.right != null) q.offer(node.right); + } + res.add(Arrays.asList(level)); + } + return res; + } +} +``` + +```cpp +/** + * 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: + vector> zigzagLevelOrder(TreeNode* root) { + vector> res; + if (!root) return res; + + queue q; + q.push(root); + + while (!q.empty()) { + int size = q.size(); + vector level(size); + for (int i = 0; i < size; ++i) { + TreeNode* node = q.front(); + q.pop(); + int idx = (res.size() % 2 == 0) ? i : size - i - 1; + level[idx] = node->val; + if (node->left) q.push(node->left); + if (node->right) q.push(node->right); + } + res.push_back(level); + } + return res; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {number[][]} + */ + zigzagLevelOrder(root) { + const res = []; + if (!root) return res; + + const q = new Queue([root]); + + while (!q.isEmpty()) { + const size = q.size(); + const level = Array(size).fill(0); + + for (let i = 0; i < size; i++) { + const node = q.pop(); + const idx = res.length % 2 === 0 ? i : size - i - 1; + level[idx] = node.val; + if (node.left) q.push(node.left); + if (node.right) q.push(node.right); + } + res.push(level); + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Depth First Search + +::tabs-start + +```python +# 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 +class Solution: + def zigzagLevelOrder(self, root: Optional[TreeNode]) -> List[List[int]]: + res = [] + + def dfs(node, depth): + if not node: + return + if depth == len(res): + res.append([]) + res[depth].append(node.val) + dfs(node.left, depth + 1) + dfs(node.right, depth + 1) + + dfs(root, 0) + for i, level in enumerate(res): + if i & 1: + level.reverse() + + return res +``` + +```java +/** + * 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; + * } + * } + */ +public class Solution { + public List> zigzagLevelOrder(TreeNode root) { + List> res = new ArrayList<>(); + dfs(root, 0, res); + for (int i = 0; i < res.size(); i++) { + if ((i & 1) == 1) { + Collections.reverse(res.get(i)); + } + } + return res; + } + + private void dfs(TreeNode node, int depth, List> res) { + if (node == null) return; + if (depth == res.size()) { + res.add(new ArrayList<>()); + } + res.get(depth).add(node.val); + dfs(node.left, depth + 1, res); + dfs(node.right, depth + 1, res); + } +} +``` + +```cpp +/** + * 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: + vector> zigzagLevelOrder(TreeNode* root) { + vector> res; + dfs(root, 0, res); + for (int i = 0; i < res.size(); ++i) { + if (i & 1) { + reverse(res[i].begin(), res[i].end()); + } + } + return res; + } + +private: + void dfs(TreeNode* node, int depth, vector>& res) { + if (!node) return; + if (depth == res.size()) { + res.push_back({}); + } + res[depth].push_back(node->val); + dfs(node->left, depth + 1, res); + dfs(node->right, depth + 1, res); + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {number[][]} + */ + zigzagLevelOrder(root) { + const res = []; + const dfs = (node, depth) => { + if (!node) return; + if (depth === res.length) res.push([]); + res[depth].push(node.val); + dfs(node.left, depth + 1); + dfs(node.right, depth + 1); + }; + + dfs(root, 0); + for (let i = 0; i < res.length; i++) { + if (i % 2 === 1) res[i].reverse(); + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 4. Iterative DFS + +::tabs-start + +```python +# 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 +class Solution: + def zigzagLevelOrder(self, root: Optional[TreeNode]) -> List[List[int]]: + if not root: + return [] + + res = [] + stack = [(root, 0)] + + while stack: + node, depth = stack.pop() + if depth == len(res): + res.append([]) + + res[depth].append(node.val) + + if node.right: + stack.append((node.right, depth + 1)) + if node.left: + stack.append((node.left, depth + 1)) + + for i in range(len(res)): + if i % 2 == 1: + res[i].reverse() + + return res +``` + +```java +/** + * 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; + * } + * } + */ +public class Solution { + public List> zigzagLevelOrder(TreeNode root) { + if (root == null) return new ArrayList<>(); + + List> res = new ArrayList<>(); + Stack stack = new Stack<>(); + stack.push(new Pair(root, 0)); + + while (!stack.isEmpty()) { + Pair current = stack.pop(); + TreeNode node = current.getKey(); + int depth = current.getValue(); + + if (depth == res.size()) { + res.add(new ArrayList<>()); + } + res.get(depth).add(node.val); + + if (node.right != null) stack.push(new Pair<>(node.right, depth + 1)); + if (node.left != null) stack.push(new Pair<>(node.left, depth + 1)); + } + + for (int i = 0; i < res.size(); i++) { + if (i % 2 == 1) { + Collections.reverse(res.get(i)); + } + } + + return res; + } +} +``` + +```cpp +/** + * 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: + vector> zigzagLevelOrder(TreeNode* root) { + if (!root) return {}; + + vector> res; + stack> s; + s.push({root, 0}); + + while (!s.empty()) { + auto [node, depth] = s.top(); + s.pop(); + + if (depth == res.size()) { + res.push_back({}); + } + res[depth].push_back(node->val); + + if (node->right) s.push({node->right, depth + 1}); + if (node->left) s.push({node->left, depth + 1}); + } + + for (int i = 0; i < res.size(); i++) { + if (i % 2 == 1) { + reverse(res[i].begin(), res[i].end()); + } + } + + return res; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {number[][]} + */ + zigzagLevelOrder(root) { + if (!root) return []; + + const res = []; + const stack = [[root, 0]]; + + while (stack.length) { + const [node, depth] = stack.pop(); + + if (depth === res.length) res.push([]); + res[depth].push(node.val); + + if (node.right) stack.push([node.right, depth + 1]); + if (node.left) stack.push([node.left, depth + 1]); + } + + for (let i = 0; i < res.length; i++) { + if (i % 2 === 1) res[i].reverse(); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/check-completeness-of-a-binary-tree.md b/articles/check-completeness-of-a-binary-tree.md new file mode 100644 index 000000000..90a185822 --- /dev/null +++ b/articles/check-completeness-of-a-binary-tree.md @@ -0,0 +1,591 @@ +## 1. Breadth First Search + +::tabs-start + +```python +# 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 +class Solution: + def isCompleteTree(self, root: Optional[TreeNode]) -> bool: + q = deque([root]) + while q: + node = q.popleft() + if node: + q.append(node.left) + q.append(node.right) + else: + while q: + if q.popleft(): + return False + return True +``` + +```java +/** + * 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; + * } + * } + */ +public class Solution { + public boolean isCompleteTree(TreeNode root) { + Queue q = new LinkedList<>(); + q.add(root); + + while (!q.isEmpty()) { + TreeNode node = q.poll(); + if (node != null) { + q.add(node.left); + q.add(node.right); + } else { + while (!q.isEmpty()) { + if (q.poll() != null) { + return false; + } + } + } + } + return true; + } +} +``` + +```cpp +/** + * 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: + bool isCompleteTree(TreeNode* root) { + queue q; + q.push(root); + + while (!q.empty()) { + TreeNode* node = q.front(); + q.pop(); + if (node) { + q.push(node->left); + q.push(node->right); + } else { + while (!q.empty()) { + if (q.front()) { + return false; + } + q.pop(); + } + } + } + return true; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {boolean} + */ + isCompleteTree(root) { + const queue = new Queue([root]); + + while (!queue.isEmpty()) { + const node = queue.pop(); + if (node) { + queue.push(node.left); + queue.push(node.right); + } else { + while (!queue.isEmpty()) { + if (queue.pop()) { + return false; + } + } + } + } + return true; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Breadth First Search (Optimal) + +::tabs-start + +```python +# 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 +class Solution: + def isCompleteTree(self, root: Optional[TreeNode]) -> bool: + q = deque([root]) + nullSeen = False + while q: + node = q.popleft() + if node: + if nullSeen: + return False + q.append(node.left) + q.append(node.right) + else: + nullSeen = True + return True +``` + +```java +/** + * 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; + * } + * } + */ +public class Solution { + public boolean isCompleteTree(TreeNode root) { + Queue q = new LinkedList<>(); + q.add(root); + boolean nullSeen = false; + + while (!q.isEmpty()) { + TreeNode node = q.poll(); + if (node != null) { + if (nullSeen) return false; + q.add(node.left); + q.add(node.right); + } else { + nullSeen = true; + } + } + return true; + } +} +``` + +```cpp +/** + * 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: + bool isCompleteTree(TreeNode* root) { + queue q; + q.push(root); + bool nullSeen = false; + + while (!q.empty()) { + TreeNode* node = q.front(); + q.pop(); + if (node) { + if (nullSeen) return false; + q.push(node->left); + q.push(node->right); + } else { + nullSeen = true; + } + } + return true; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {boolean} + */ + isCompleteTree(root) { + const queue = new Queue([root]); + let nullSeen = false; + + while (!queue.isEmpty()) { + const node = queue.pop(); + if (node) { + if (nullSeen) return false; + queue.push(node.left); + queue.push(node.right); + } else { + nullSeen = true; + } + } + return true; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Depth First Search (Two Pass) + +::tabs-start + +```python +# 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 +class Solution: + def isCompleteTree(self, root: Optional[TreeNode]) -> bool: + def dfs(node, index, n): + if not node: + return True + if index >= n: + return False + + left = dfs(node.left, 2 * index + 1, n) + right = dfs(node.right, 2 * index + 2, n) + return left and right + + def countNodes(node): + if not node: + return 0 + return 1 + countNodes(node.left) + countNodes(node.right) + + n = countNodes(root) + return dfs(root, 0, n) +``` + +```java +/** + * 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; + * } + * } + */ +public class Solution { + private int countNodes(TreeNode root) { + if (root == null) return 0; + return 1 + countNodes(root.left) + countNodes(root.right); + } + + private boolean dfs(TreeNode node, int index, int n) { + if (node == null) return true; + if (index >= n) return false; + return dfs(node.left, 2 * index + 1, n) && dfs(node.right, 2 * index + 2, n); + } + + public boolean isCompleteTree(TreeNode root) { + int n = countNodes(root); + return dfs(root, 0, n); + } +} +``` + +```cpp +/** + * 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: + int countNodes(TreeNode* root) { + if (!root) return 0; + return 1 + countNodes(root->left) + countNodes(root->right); + } + + bool dfs(TreeNode* node, int index, int n) { + if (!node) return true; + if (index >= n) return false; + return dfs(node->left, 2 * index + 1, n) && dfs(node->right, 2 * index + 2, n); + } + + bool isCompleteTree(TreeNode* root) { + int n = countNodes(root); + return dfs(root, 0, n); + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {boolean} + */ + isCompleteTree(root) { + const countNodes = (node) => { + if (!node) return 0; + return 1 + countNodes(node.left) + countNodes(node.right); + }; + + const dfs = (node, index, n) => { + if (!node) return true; + if (index >= n) return false; + return dfs(node.left, 2 * index + 1, n) && dfs(node.right, 2 * index + 2, n); + }; + + const n = countNodes(root); + return dfs(root, 0, n); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ for recursion stack. + +--- + +## 4. Depth First Search (Optimal) + +::tabs-start + +```python +# 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 +class Solution: + def isCompleteTree(self, root: Optional[TreeNode]) -> bool: + treeHgt = 0 + nullSeen = False + + def dfs(node, hgt): + nonlocal treeHgt, nullSeen + if not node: + if treeHgt == 0: + treeHgt = hgt + elif hgt == treeHgt - 1: + nullSeen = True + elif hgt != treeHgt: + return False + return not (hgt == treeHgt and nullSeen) + + return dfs(node.left, hgt + 1) and dfs(node.right, hgt + 1) + + return dfs(root, 0) +``` + +```java +/** + * 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; + * } + * } + */ +public class Solution { + private int treeHgt = 0; + private boolean nullSeen = false; + + private boolean dfs(TreeNode node, int hgt) { + if (node == null) { + if (treeHgt == 0) { + treeHgt = hgt; + } else if (hgt == treeHgt - 1) { + nullSeen = true; + } else if (hgt != treeHgt) { + return false; + } + return !(hgt == treeHgt && nullSeen); + } + + return dfs(node.left, hgt + 1) && dfs(node.right, hgt + 1); + } + + public boolean isCompleteTree(TreeNode root) { + return dfs(root, 0); + } +} +``` + +```cpp +/** + * 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: + int treeHgt = 0; + bool nullSeen = false; + + bool dfs(TreeNode* node, int hgt) { + if (!node) { + if (treeHgt == 0) { + treeHgt = hgt; + } else if (hgt == treeHgt - 1) { + nullSeen = true; + } else if (hgt != treeHgt) { + return false; + } + return !(hgt == treeHgt && nullSeen); + } + + return dfs(node->left, hgt + 1) && dfs(node->right, hgt + 1); + } + + bool isCompleteTree(TreeNode* root) { + return dfs(root, 0); + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {boolean} + */ + isCompleteTree(root) { + let treeHgt = 0; + let nullSeen = false; + + const dfs = (node, hgt) => { + if (!node) { + if (treeHgt === 0) { + treeHgt = hgt; + } else if (hgt === treeHgt - 1) { + nullSeen = true; + } else if (hgt !== treeHgt) { + return false; + } + return !(hgt === treeHgt && nullSeen); + } + + return dfs(node.left, hgt + 1) && dfs(node.right, hgt + 1); + }; + + return dfs(root, 0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ for recursion stack. \ No newline at end of file diff --git a/articles/construct-binary-tree-from-inorder-and-postorder-traversal.md b/articles/construct-binary-tree-from-inorder-and-postorder-traversal.md new file mode 100644 index 000000000..eb086eacd --- /dev/null +++ b/articles/construct-binary-tree-from-inorder-and-postorder-traversal.md @@ -0,0 +1,470 @@ +## 1. Depth First Search + +::tabs-start + +```python +# 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 +class Solution: + def buildTree(self, inorder: List[int], postorder: List[int]) -> Optional[TreeNode]: + if not postorder or not inorder: + return None + + root = TreeNode(postorder[-1]) + mid = inorder.index(postorder[-1]) + root.left = self.buildTree(inorder[:mid], postorder[:mid]) + root.right = self.buildTree(inorder[mid + 1:], postorder[mid:-1]) + return root +``` + +```java +/** + * 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; + * } + * } + */ +public class Solution { + public TreeNode buildTree(int[] inorder, int[] postorder) { + if (postorder.length == 0 || inorder.length == 0) { + return null; + } + + TreeNode root = new TreeNode(postorder[postorder.length - 1]); + int mid = 0; + for (int i = 0; i < inorder.length; i++) { + if (inorder[i] == postorder[postorder.length - 1]) { + mid = i; + break; + } + } + + root.left = buildTree( + Arrays.copyOfRange(inorder, 0, mid), + Arrays.copyOfRange(postorder, 0, mid) + ); + root.right = buildTree( + Arrays.copyOfRange(inorder, mid + 1, inorder.length), + Arrays.copyOfRange(postorder, mid, postorder.length - 1) + ); + + return root; + } +} +``` + +```cpp +/** + * 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& inorder, vector& postorder) { + if (postorder.empty() || inorder.empty()) { + return nullptr; + } + + TreeNode* root = new TreeNode(postorder.back()); + auto it = find(inorder.begin(), inorder.end(), postorder.back()); + int mid = distance(inorder.begin(), it); + + vector leftInorder(inorder.begin(), inorder.begin() + mid); + vector rightInorder(inorder.begin() + mid + 1, inorder.end()); + vector leftPostorder(postorder.begin(), postorder.begin() + mid); + vector rightPostorder(postorder.begin() + mid, postorder.end() - 1); + + root->left = buildTree(leftInorder, leftPostorder); + root->right = buildTree(rightInorder, rightPostorder); + + return root; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {number[]} inorder + * @param {number[]} postorder + * @return {TreeNode} + */ + buildTree(inorder, postorder) { + if (postorder.length === 0 || inorder.length === 0) { + return null; + } + + const rootVal = postorder[postorder.length - 1]; + const root = new TreeNode(rootVal); + const mid = inorder.indexOf(rootVal); + + root.left = this.buildTree(inorder.slice(0, mid), postorder.slice(0, mid)); + root.right = this.buildTree(inorder.slice(mid + 1), postorder.slice(mid, postorder.length - 1)); + + return root; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n)$ + +--- + +## 2. Hash Map + Depth First Search + +::tabs-start + +```python +# 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 +class Solution: + def buildTree(self, inorder: List[int], postorder: List[int]) -> Optional[TreeNode]: + inorderIdx = {v: i for i, v in enumerate(inorder)} + + def dfs(l, r): + if l > r: + return None + + root = TreeNode(postorder.pop()) + idx = inorderIdx[root.val] + root.right = dfs(idx + 1, r) + root.left = dfs(l, idx - 1) + return root + + return dfs(0, len(inorder) - 1) +``` + +```java +/** + * 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; + * } + * } + */ +public class Solution { + private HashMap inorderIdx; + private int postIdx; + + public TreeNode buildTree(int[] inorder, int[] postorder) { + inorderIdx = new HashMap<>(); + for (int i = 0; i < inorder.length; i++) { + inorderIdx.put(inorder[i], i); + } + postIdx = postorder.length - 1; + + return dfs(0, inorder.length - 1, postorder); + } + + private TreeNode dfs(int l, int r, int[] postorder) { + if (l > r) { + return null; + } + + TreeNode root = new TreeNode(postorder[postIdx--]); + int idx = inorderIdx.get(root.val); + root.right = dfs(idx + 1, r, postorder); + root.left = dfs(l, idx - 1, postorder); + return root; + } +} +``` + +```cpp +/** + * 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: + unordered_map inorderIdx; + int postIdx; + + TreeNode* buildTree(vector& inorder, vector& postorder) { + for (int i = 0; i < inorder.size(); i++) { + inorderIdx[inorder[i]] = i; + } + postIdx = postorder.size() - 1; + + return dfs(0, inorder.size() - 1, postorder); + } + +private: + TreeNode* dfs(int l, int r, vector& postorder) { + if (l > r) { + return nullptr; + } + + TreeNode* root = new TreeNode(postorder[postIdx--]); + int idx = inorderIdx[root->val]; + root->right = dfs(idx + 1, r, postorder); + root->left = dfs(l, idx - 1, postorder); + return root; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {number[]} inorder + * @param {number[]} postorder + * @return {TreeNode} + */ + buildTree(inorder, postorder) { + const inorderIdx = new Map(); + inorder.forEach((val, idx) => inorderIdx.set(val, idx)); + let postIdx = postorder.length - 1; + + const dfs = (l, r) => { + if (l > r) return null; + + const root = new TreeNode(postorder[postIdx--]); + const idx = inorderIdx.get(root.val); + root.right = dfs(idx + 1, r); + root.left = dfs(l, idx - 1); + return root; + }; + + return dfs(0, inorder.length - 1); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Depth First Search (Optimal) + +::tabs-start + +```python +# 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 +class Solution: + def buildTree(self, inorder: List[int], postorder: List[int]) -> Optional[TreeNode]: + postIdx = inIdx = len(postorder) - 1 + def dfs(limit): + nonlocal postIdx, inIdx + if postIdx < 0: + return None + if inorder[inIdx] == limit: + inIdx -= 1 + return None + + root = TreeNode(postorder[postIdx]) + postIdx -= 1 + root.right = dfs(root.val) + root.left = dfs(limit) + return root + return dfs(float('inf')) +``` + +```java +/** + * 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; + * } + * } + */ +public class Solution { + private int postIdx; + private int inIdx; + + public TreeNode buildTree(int[] inorder, int[] postorder) { + postIdx = postorder.length - 1; + inIdx = inorder.length - 1; + + return dfs(postorder, inorder, Integer.MAX_VALUE); + } + + private TreeNode dfs(int[] postorder, int[] inorder, int limit) { + if (postIdx < 0) { + return null; + } + + if (inorder[inIdx] == limit) { + inIdx--; + return null; + } + + TreeNode root = new TreeNode(postorder[postIdx--]); + root.right = dfs(postorder, inorder, root.val); + root.left = dfs(postorder, inorder, limit); + return root; + } +} +``` + +```cpp +/** + * 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: + int postIdx; + int inIdx; + + TreeNode* buildTree(vector& inorder, vector& postorder) { + postIdx = postorder.size() - 1; + inIdx = inorder.size() - 1; + + return dfs(postorder, inorder, numeric_limits::max()); + } + +private: + TreeNode* dfs(vector& postorder, vector& inorder, int limit) { + if (postIdx < 0) { + return nullptr; + } + + if (inorder[inIdx] == limit) { + inIdx--; + return nullptr; + } + + TreeNode* root = new TreeNode(postorder[postIdx--]); + root->right = dfs(postorder, inorder, root->val); + root->left = dfs(postorder, inorder, limit); + return root; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {number[]} inorder + * @param {number[]} postorder + * @return {TreeNode} + */ + buildTree(inorder, postorder) { + let postIdx = postorder.length - 1; + let inIdx = inorder.length - 1; + + const dfs = (limit) => { + if (postIdx < 0) return null; + + if (inorder[inIdx] === limit) { + inIdx--; + return null; + } + + const root = new TreeNode(postorder[postIdx--]); + root.right = dfs(root.val); + root.left = dfs(limit); + return root; + }; + + return dfs(Infinity); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ for recursion stack. \ No newline at end of file diff --git a/articles/construct-string-from-binary-tree.md b/articles/construct-string-from-binary-tree.md new file mode 100644 index 000000000..98b9da146 --- /dev/null +++ b/articles/construct-string-from-binary-tree.md @@ -0,0 +1,518 @@ +## 1. Depth First Search + +::tabs-start + +```python +# 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 +class Solution: + def tree2str(self, root: Optional[TreeNode]) -> str: + if not root: + return "" + + cur = root.val + left = self.tree2str(root.left) + right = self.tree2str(root.right) + + if left and right: + return f"{cur}({left})({right})" + + if right: + return f"{cur}()({right})" + + if left: + return f"{cur}({left})" + + return str(cur) +``` + +```java +/** + * 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; + * } + * } + */ +public class Solution { + public String tree2str(TreeNode root) { + if (root == null) { + return ""; + } + + String cur = Integer.toString(root.val); + String left = tree2str(root.left); + String right = tree2str(root.right); + + if (!left.isEmpty() && !right.isEmpty()) { + return cur + "(" + left + ")" + "(" + right + ")"; + } + + if (!right.isEmpty()) { + return cur + "()" + "(" + right + ")"; + } + + if (!left.isEmpty()) { + return cur + "(" + left + ")"; + } + + return cur; + } +} +``` + +```cpp +/** + * 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: + string tree2str(TreeNode* root) { + if (!root) { + return ""; + } + + string cur = to_string(root->val); + string left = tree2str(root->left); + string right = tree2str(root->right); + + if (!left.empty() && !right.empty()) { + return cur + "(" + left + ")(" + right + ")"; + } + + if (!right.empty()) { + return cur + "()(" + right + ")"; + } + + if (!left.empty()) { + return cur + "(" + left + ")"; + } + + return cur; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {string} + */ + tree2str(root) { + if (!root) { + return ""; + } + + const cur = root.val.toString(); + const left = this.tree2str(root.left); + const right = this.tree2str(root.right); + + if (left && right) { + return `${cur}(${left})(${right})`; + } + + if (right) { + return `${cur}()(${right})`; + } + + if (left) { + return `${cur}(${left})`; + } + + return cur; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n)$ + +--- + +## 2. Depth First Search (Optimal) + +::tabs-start + +```python +# 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 +class Solution: + def tree2str(self, root: Optional[TreeNode]) -> str: + res = [] + + def preorder(root): + if not root: + return + res.append("(") + res.append(str(root.val)) + if not root.left and root.right: + res.append("()") + preorder(root.left) + preorder(root.right) + res.append(")") + + preorder(root) + return "".join(res)[1:-1] +``` + +```java +/** + * 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; + * } + * } + */ +public class Solution { + public String tree2str(TreeNode root) { + StringBuilder res = new StringBuilder(); + + preorder(root, res); + return res.substring(1, res.length() - 1); + } + + private void preorder(TreeNode root, StringBuilder res) { + if (root == null) return; + + res.append("(").append(root.val); + if (root.left == null && root.right != null) { + res.append("()"); + } + preorder(root.left, res); + preorder(root.right, res); + res.append(")"); + } +} +``` + +```cpp +/** + * 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: + string tree2str(TreeNode* root) { + string res; + preorder(root, res); + return res.substr(1, res.size() - 2); + } + +private: + void preorder(TreeNode* root, string& res) { + if (!root) return; + + res += "(" + to_string(root->val); + if (!root->left && root->right) { + res += "()"; + } + preorder(root->left, res); + preorder(root->right, res); + res += ")"; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {string} + */ + tree2str(root) { + let res = []; + + const preorder = (root) => { + if (!root) return; + + res.push("("); + res.push(root.val.toString()); + if (!root.left && root.right) { + res.push("()"); + } + preorder(root.left); + preorder(root.right); + res.push(")"); + }; + + preorder(root); + return res.join("").slice(1, -1); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Iterative DFS + +::tabs-start + +```python +# 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 +class Solution: + def tree2str(self, root: Optional[TreeNode]) -> str: + if not root: + return "" + + res = [] + stack = [] + last_visited = None + cur = root + + while cur or stack: + if cur: + res.append(f"({cur.val}") + if not cur.left and cur.right: + res.append("()") + + stack.append(cur) + cur = cur.left + else: + top = stack[-1] + if top.right and last_visited != top.right: + cur = top.right + else: + stack.pop() + res.append(")") + last_visited = top + + return "".join(res)[1:-1] +``` + +```java +/** + * 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; + * } + * } + */ +public class Solution { + public String tree2str(TreeNode root) { + if (root == null) { + return ""; + } + + StringBuilder res = new StringBuilder(); + Stack stack = new Stack<>(); + TreeNode lastVisited = null; + TreeNode cur = root; + + while (cur != null || !stack.isEmpty()) { + if (cur != null) { + res.append("(").append(cur.val); + if (cur.left == null && cur.right != null) { + res.append("()"); + } + + stack.push(cur); + cur = cur.left; + } else { + TreeNode top = stack.peek(); + if (top.right != null && lastVisited != top.right) { + cur = top.right; + } else { + stack.pop(); + res.append(")"); + lastVisited = top; + } + } + } + + return res.substring(1, res.length() - 1); + } +} +``` + +```cpp +/** + * 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: + string tree2str(TreeNode* root) { + if (!root) { + return ""; + } + + string res; + stack stack; + TreeNode* lastVisited = nullptr; + TreeNode* cur = root; + + while (cur || !stack.empty()) { + if (cur) { + res += "(" + to_string(cur->val); + if (!cur->left && cur->right) { + res += "()"; + } + + stack.push(cur); + cur = cur->left; + } else { + TreeNode* top = stack.top(); + if (top->right && lastVisited != top->right) { + cur = top->right; + } else { + stack.pop(); + res += ")"; + lastVisited = top; + } + } + } + + return res.substr(1, res.size() - 2); + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {string} + */ + tree2str(root) { + if (!root) { + return ""; + } + + let res = []; + let stack = []; + let lastVisited = null; + let cur = root; + + while (cur || stack.length > 0) { + if (cur) { + res.push(`(${cur.val}`); + if (!cur.left && cur.right) { + res.push("()"); + } + + stack.push(cur); + cur = cur.left; + } else { + let top = stack[stack.length - 1]; + if (top.right && lastVisited !== top.right) { + cur = top.right; + } else { + stack.pop(); + res.push(")"); + lastVisited = top; + } + } + } + + return res.join("").slice(1, -1); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/convert-sorted-array-to-binary-search-tree.md b/articles/convert-sorted-array-to-binary-search-tree.md new file mode 100644 index 000000000..dd933b982 --- /dev/null +++ b/articles/convert-sorted-array-to-binary-search-tree.md @@ -0,0 +1,437 @@ +## 1. Depth First Search + +::tabs-start + +```python +# 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 +class Solution: + def sortedArrayToBST(self, nums: List[int]) -> Optional[TreeNode]: + if not nums: + return None + + mid = len(nums) // 2 + root = TreeNode(nums[mid]) + root.left = self.sortedArrayToBST(nums[:mid]) + root.right = self.sortedArrayToBST(nums[mid + 1:]) + return root +``` + +```java +/** + * 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; + * } + * } + */ +public class Solution { + public TreeNode sortedArrayToBST(int[] nums) { + if (nums.length == 0) { + return null; + } + + int mid = nums.length / 2; + TreeNode root = new TreeNode(nums[mid]); + root.left = sortedArrayToBST(Arrays.copyOfRange(nums, 0, mid)); + root.right = sortedArrayToBST(Arrays.copyOfRange(nums, mid + 1, nums.length)); + return root; + } +} +``` + +```cpp +/** + * 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* sortedArrayToBST(vector& nums) { + if (nums.empty()) { + return nullptr; + } + + int mid = nums.size() / 2; + TreeNode* root = new TreeNode(nums[mid]); + vector left(nums.begin(), nums.begin() + mid); + vector right(nums.begin() + mid + 1, nums.end()); + root->left = sortedArrayToBST(left); + root->right = sortedArrayToBST(right); + return root; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {number[]} nums + * @return {TreeNode} + */ + sortedArrayToBST(nums) { + if (nums.length === 0) { + return null; + } + + const mid = Math.floor(nums.length / 2); + const root = new TreeNode(nums[mid]); + root.left = this.sortedArrayToBST(nums.slice(0, mid)); + root.right = this.sortedArrayToBST(nums.slice(mid + 1)); + return root; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Depth First Search (Optimal) + +::tabs-start + +```python +# 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 +class Solution: + def sortedArrayToBST(self, nums: List[int]) -> TreeNode: + def helper(l, r): + if l > r: + return None + m = (l + r) // 2 + root = TreeNode(nums[m]) + root.left = helper(l, m - 1) + root.right = helper(m + 1, r) + return root + + return helper(0, len(nums) - 1) +``` + +```java +/** + * 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; + * } + * } + */ +public class Solution { + public TreeNode sortedArrayToBST(int[] nums) { + return helper(nums, 0, nums.length - 1); + } + + private TreeNode helper(int[] nums, int l, int r) { + if (l > r) { + return null; + } + int m = (l + r) / 2; + TreeNode root = new TreeNode(nums[m]); + root.left = helper(nums, l, m - 1); + root.right = helper(nums, m + 1, r); + return root; + } +} +``` + +```cpp +/** + * 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* sortedArrayToBST(vector& nums) { + return helper(nums, 0, nums.size() - 1); + } + +private: + TreeNode* helper(vector& nums, int l, int r) { + if (l > r) { + return nullptr; + } + int m = (l + r) / 2; + TreeNode* root = new TreeNode(nums[m]); + root->left = helper(nums, l, m - 1); + root->right = helper(nums, m + 1, r); + return root; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {number[]} nums + * @return {TreeNode} + */ + sortedArrayToBST(nums) { + const helper = (l, r) => { + if (l > r) { + return null; + } + const m = Math.floor((l + r) / 2); + const root = new TreeNode(nums[m]); + root.left = helper(l, m - 1); + root.right = helper(m + 1, r); + return root; + }; + + return helper(0, nums.length - 1); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: + * $O(\log n)$ space for recursion stack. + * $O(n)$ space for the output. + +--- + +## 3. Iterative DFS + +::tabs-start + +```python +# 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 +class Solution: + def sortedArrayToBST(self, nums: List[int]) -> TreeNode: + if not nums: + return None + + root = TreeNode(0) + stack = [(root, 0, len(nums) - 1)] + + while stack: + node, l, r = stack.pop() + m = (l + r) // 2 + node.val = nums[m] + if l <= m - 1: + node.left = TreeNode(0) + stack.append((node.left, l, m - 1)) + if m + 1 <= r: + node.right = TreeNode(0) + stack.append((node.right, m + 1, r)) + + return root +``` + +```java +/** + * 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; + * } + * } + */ +public class Solution { + public TreeNode sortedArrayToBST(int[] nums) { + if (nums.length == 0) { + return null; + } + + TreeNode root = new TreeNode(0); + Stack stack = new Stack<>(); + Stack nodes = new Stack<>(); + stack.push(new int[]{0, nums.length - 1}); + nodes.push(root); + + while (!stack.isEmpty()) { + int[] range = stack.pop(); + TreeNode node = nodes.pop(); + int l = range[0], r = range[1]; + int m = (l + r) / 2; + node.val = nums[m]; + + if (l <= m - 1) { + node.left = new TreeNode(0); + stack.push(new int[]{l, m - 1}); + nodes.push(node.left); + } + if (m + 1 <= r) { + node.right = new TreeNode(0); + stack.push(new int[]{m + 1, r}); + nodes.push(node.right); + } + } + + return root; + } +} +``` + +```cpp +/** + * 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* sortedArrayToBST(vector& nums) { + if (nums.empty()) return nullptr; + + TreeNode* root = new TreeNode(0); + stack> stack; + stack.push({root, 0, (int)nums.size() - 1}); + + while (!stack.empty()) { + auto [node, l, r] = stack.top(); + stack.pop(); + int m = (l + r) / 2; + node->val = nums[m]; + + if (l <= m - 1) { + node->left = new TreeNode(0); + stack.push({node->left, l, m - 1}); + } + if (m + 1 <= r) { + node->right = new TreeNode(0); + stack.push({node->right, m + 1, r}); + } + } + + return root; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {number[]} nums + * @return {TreeNode} + */ + sortedArrayToBST(nums) { + if (nums.length === 0) { + return null; + } + + const root = new TreeNode(0); + const stack = [[root, 0, nums.length - 1]]; + + while (stack.length) { + const [node, l, r] = stack.pop(); + const m = Math.floor((l + r) / 2); + node.val = nums[m]; + + if (l <= m - 1) { + node.left = new TreeNode(0); + stack.push([node.left, l, m - 1]); + } + if (m + 1 <= r) { + node.right = new TreeNode(0); + stack.push([node.right, m + 1, r]); + } + } + + return root; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: + * $O(\log n)$ space for the stack. + * $O(n)$ space for the output. \ No newline at end of file diff --git a/articles/count-triplets-that-can-form-two-arrays-of-equal-xor.md b/articles/count-triplets-that-can-form-two-arrays-of-equal-xor.md new file mode 100644 index 000000000..129c14f82 --- /dev/null +++ b/articles/count-triplets-that-can-form-two-arrays-of-equal-xor.md @@ -0,0 +1,432 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def countTriplets(self, arr: List[int]) -> int: + N = len(arr) + res = 0 + + for i in range(N - 1): + for j in range(i + 1, N): + for k in range(j, N): + a = b = 0 + for idx in range(i, j): + a ^= arr[idx] + for idx in range(j, k + 1): + b ^= arr[idx] + if a == b: + res += 1 + + return res +``` + +```java +public class Solution { + public int countTriplets(int[] arr) { + int N = arr.length; + int res = 0; + + for (int i = 0; i < N - 1; i++) { + for (int j = i + 1; j < N; j++) { + for (int k = j; k < N; k++) { + int a = 0, b = 0; + for (int idx = i; idx < j; idx++) { + a ^= arr[idx]; + } + for (int idx = j; idx <= k; idx++) { + b ^= arr[idx]; + } + if (a == b) { + res++; + } + } + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int countTriplets(vector& arr) { + int N = arr.size(); + int res = 0; + + for (int i = 0; i < N - 1; ++i) { + for (int j = i + 1; j < N; ++j) { + for (int k = j; k < N; ++k) { + int a = 0, b = 0; + for (int idx = i; idx < j; ++idx) { + a ^= arr[idx]; + } + for (int idx = j; idx <= k; ++idx) { + b ^= arr[idx]; + } + if (a == b) { + res++; + } + } + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} arr + * @return {number} + */ + countTriplets(arr) { + const N = arr.length; + let res = 0; + + for (let i = 0; i < N - 1; i++) { + for (let j = i + 1; j < N; j++) { + for (let k = j; k < N; k++) { + let a = 0, b = 0; + for (let idx = i; idx < j; idx++) { + a ^= arr[idx]; + } + for (let idx = j; idx <= k; idx++) { + b ^= arr[idx]; + } + if (a === b) { + res++; + } + } + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 4)$ +* Space complexity: $O(1)$ extra space. + +--- + +## 2. Brute Force (Optimized) + +::tabs-start + +```python +class Solution: + def countTriplets(self, arr: List[int]) -> int: + N = len(arr) + res = 0 + + for i in range(N - 1): + a = 0 + for j in range(i + 1, N): + a ^= arr[j - 1] + b = 0 + for k in range(j, N): + b ^= arr[k] + if a == b: + res += 1 + + return res +``` + +```java +public class Solution { + public int countTriplets(int[] arr) { + int N = arr.length; + int res = 0; + + for (int i = 0; i < N - 1; i++) { + int a = 0; + for (int j = i + 1; j < N; j++) { + a ^= arr[j - 1]; + int b = 0; + for (int k = j; k < N; k++) { + b ^= arr[k]; + if (a == b) { + res++; + } + } + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int countTriplets(vector& arr) { + int N = arr.size(); + int res = 0; + + for (int i = 0; i < N - 1; ++i) { + int a = 0; + for (int j = i + 1; j < N; ++j) { + a ^= arr[j - 1]; + int b = 0; + for (int k = j; k < N; ++k) { + b ^= arr[k]; + if (a == b) { + res++; + } + } + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} arr + * @return {number} + */ + countTriplets(arr) { + const N = arr.length; + let res = 0; + + for (let i = 0; i < N - 1; i++) { + let a = 0; + for (let j = i + 1; j < N; j++) { + a ^= arr[j - 1]; + let b = 0; + for (let k = j; k < N; k++) { + b ^= arr[k]; + if (a === b) { + res++; + } + } + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 3)$ +* Space complexity: $O(1)$ extra space. + +--- + +## 3. Math + Bitwise XOR + +::tabs-start + +```python +class Solution: + def countTriplets(self, arr: List[int]) -> int: + N = len(arr) + res = 0 + + for i in range(N - 1): + cur_xor = arr[i] + for k in range(i + 1, N): + cur_xor ^= arr[k] + if cur_xor == 0: + res += k - i + + return res +``` + +```java +public class Solution { + public int countTriplets(int[] arr) { + int N = arr.length; + int res = 0; + + for (int i = 0; i < N - 1; i++) { + int curXor = arr[i]; + for (int k = i + 1; k < N; k++) { + curXor ^= arr[k]; + if (curXor == 0) { + res += k - i; + } + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int countTriplets(vector& arr) { + int N = arr.size(); + int res = 0; + + for (int i = 0; i < N - 1; ++i) { + int curXor = arr[i]; + for (int k = i + 1; k < N; ++k) { + curXor ^= arr[k]; + if (curXor == 0) { + res += k - i; + } + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} arr + * @return {number} + */ + countTriplets(arr) { + const N = arr.length; + let res = 0; + + for (let i = 0; i < N - 1; i++) { + let curXor = arr[i]; + for (let k = i + 1; k < N; k++) { + curXor ^= arr[k]; + if (curXor === 0) { + res += k - i; + } + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(1)$ extra space. + +--- + +## 4. Math + Bitwise XOR (Optimal) + +::tabs-start + +```python +class Solution: + def countTriplets(self, arr: List[int]) -> int: + N = len(arr) + res = prefix = 0 + count = defaultdict(int) # number of prefixes + index_sum = defaultdict(int) # sum of indices with that prefix + count[0] = 1 + + for i in range(N): + prefix ^= arr[i] + if prefix in count: + res += i * count[prefix] - index_sum[prefix] + count[prefix] += 1 + index_sum[prefix] += i + 1 + + return res +``` + +```java +public class Solution { + public int countTriplets(int[] arr) { + int N = arr.length, res = 0, prefix = 0; + Map count = new HashMap<>(); // number of prefixes + Map indexSum = new HashMap<>(); // sum of indices with that prefix + count.put(0, 1); + + for (int i = 0; i < N; i++) { + prefix ^= arr[i]; + if (count.containsKey(prefix)) { + res += i * count.get(prefix) - indexSum.getOrDefault(prefix, 0); + } + count.put(prefix, count.getOrDefault(prefix, 0) + 1); + indexSum.put(prefix, indexSum.getOrDefault(prefix, 0) + i + 1); + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int countTriplets(vector& arr) { + int N = arr.size(), res = 0, prefix = 0; + unordered_map count; // number of prefixes + unordered_map indexSum; // sum of indices with that prefix + count[0] = 1; + + for (int i = 0; i < N; i++) { + prefix ^= arr[i]; + if (count.count(prefix)) { + res += i * count[prefix] - indexSum[prefix]; + } + count[prefix]++; + indexSum[prefix] += i + 1; + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} arr + * @return {number} + */ + countTriplets(arr) { + const N = arr.length; + let res = 0, prefix = 0; + const count = new Map(); // number of prefixes + const indexSum = new Map(); // sum of indices with that prefix + count.set(0, 1); + + for (let i = 0; i < N; i++) { + prefix ^= arr[i]; + if (count.has(prefix)) { + res += i * count.get(prefix) - (indexSum.get(prefix) || 0); + } + count.set(prefix, (count.get(prefix) || 0) + 1); + indexSum.set(prefix, (indexSum.get(prefix) || 0) + i + 1); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/design-twitter-feed.md b/articles/design-twitter-feed.md index 2c53e29a8..56d7a5848 100644 --- a/articles/design-twitter-feed.md +++ b/articles/design-twitter-feed.md @@ -486,13 +486,14 @@ public: ```javascript /** - * const { MaxPriorityQueue } = require('@datastructures-js/priority-queue'); + * const { PriorityQueue } = require('@datastructures-js/priority-queue'); */ class Twitter { constructor() { - this.users = new Map(); - this.timestamp = 0; + this.count = 0; + this.tweetMap = new Map(); // userId -> array of [count, tweetId] + this.followMap = new Map(); // userId -> set of followeeIds } /** @@ -501,13 +502,11 @@ class Twitter { * @return {void} */ postTweet(userId, tweetId) { - if (!this.users.has(userId)) { - this.users.set(userId, { tweets: [], following: new Set() }); + if (!this.tweetMap.has(userId)) { + this.tweetMap.set(userId, []); } - this.users - .get(userId) - .tweets.push({ timestamp: this.timestamp, tweetId }); - this.timestamp += 1; + this.tweetMap.get(userId).push([this.count, tweetId]); + this.count -= 1; } /** @@ -515,38 +514,34 @@ class Twitter { * @return {number[]} */ getNewsFeed(userId) { - if (!this.users.has(userId)) { - return []; + const res = []; + if (!this.followMap.has(userId)) { + this.followMap.set(userId, new Set()); } - - const maxPQ = new MaxPriorityQueue(tweet => tweet.timestamp); - const seenTweets = new Set(); - - const user = this.users.get(userId); - user.tweets.forEach(tweet => { - if (!seenTweets.has(tweet.tweetId)) { - maxPQ.enqueue(tweet); - seenTweets.add(tweet.tweetId); + this.followMap.get(userId).add(userId); + const minHeap = new PriorityQueue( + (a, b) => a[0] - b[0] + ); + + for (const followeeId of this.followMap.get(userId)) { + if (this.tweetMap.has(followeeId)) { + const tweets = this.tweetMap.get(followeeId); + const index = tweets.length - 1; + const [count, tweetId] = tweets[index]; + minHeap.enqueue([count, tweetId, followeeId, index - 1]); } - }); + } - user.following.forEach(followeeId => { - if (this.users.has(followeeId)) { - this.users.get(followeeId).tweets.forEach(tweet => { - if (!seenTweets.has(tweet.tweetId)) { - maxPQ.enqueue(tweet); - seenTweets.add(tweet.tweetId); - } - }); + while (!minHeap.isEmpty() && res.length < 10) { + const [count, tweetId, followeeId, nextIndex] = minHeap.dequeue(); + res.push(tweetId); + if (nextIndex >= 0) { + const [olderCount, olderTweetId] = this.tweetMap.get(followeeId)[nextIndex]; + minHeap.enqueue([olderCount, olderTweetId, followeeId, nextIndex - 1]); } - }); - - const newsFeed = []; - for (let i = 0; i < 10 && !maxPQ.isEmpty(); i++) { - newsFeed.push(maxPQ.dequeue().tweetId); } - return newsFeed; + return res; } /** @@ -555,10 +550,10 @@ class Twitter { * @return {void} */ follow(followerId, followeeId) { - if (!this.users.has(followerId)) { - this.users.set(followerId, { tweets: [], following: new Set() }); + if (!this.followMap.has(followerId)) { + this.followMap.set(followerId, new Set()); } - this.users.get(followerId).following.add(followeeId); + this.followMap.get(followerId).add(followeeId); } /** @@ -567,8 +562,8 @@ class Twitter { * @return {void} */ unfollow(followerId, followeeId) { - if (this.users.has(followerId)) { - this.users.get(followerId).following.delete(followeeId); + if (this.followMap.has(followerId)) { + this.followMap.get(followerId).delete(followeeId); } } } @@ -778,7 +773,663 @@ class Twitter { ### Time & Space Complexity +* Time complexity: $O(n \log n)$ for each $getNewsFeed()$ call and $O(1)$ for remaining methods. +* Space complexity: $O(N * m + N * M + n)$ + +> Where $n$ is the total number of $followeeIds$ associated with the $userId$, $m$ is the maximum number of tweets by any user, $N$ is the total number of $userIds$ and $M$ is the maximum number of followees for any user. + +--- + +## 3. Heap (Optimal) + +::tabs-start + +```python +class Twitter: + + def __init__(self): + self.count = 0 + self.tweetMap = defaultdict(list) # userId -> list of [count, tweetIds] + self.followMap = defaultdict(set) # userId -> set of followeeId + + def postTweet(self, userId: int, tweetId: int) -> None: + self.tweetMap[userId].append([self.count, tweetId]) + if len(self.tweetMap[userId]) > 10: + self.tweetMap[userId].pop(0) + self.count -= 1 + + def getNewsFeed(self, userId: int) -> List[int]: + res = [] + minHeap = [] + self.followMap[userId].add(userId) + if len(self.followMap[userId]) >= 10: + maxHeap = [] + for followeeId in self.followMap[userId]: + if followeeId in self.tweetMap: + index = len(self.tweetMap[followeeId]) - 1 + count, tweetId = self.tweetMap[followeeId][index] + heapq.heappush(maxHeap, [-count, tweetId, followeeId, index - 1]) + if len(maxHeap) > 10: + heapq.heappop(maxHeap) + while maxHeap: + count, tweetId, followeeId, index = heapq.heappop(maxHeap) + heapq.heappush(minHeap, [-count, tweetId, followeeId, index]) + else: + for followeeId in self.followMap[userId]: + if followeeId in self.tweetMap: + index = len(self.tweetMap[followeeId]) - 1 + count, tweetId = self.tweetMap[followeeId][index] + heapq.heappush(minHeap, [count, tweetId, followeeId, index - 1]) + + while minHeap and len(res) < 10: + count, tweetId, followeeId, index = heapq.heappop(minHeap) + res.append(tweetId) + if index >= 0: + count, tweetId = self.tweetMap[followeeId][index] + heapq.heappush(minHeap, [count, tweetId, followeeId, index - 1]) + + return res + + def follow(self, followerId: int, followeeId: int) -> None: + self.followMap[followerId].add(followeeId) + + def unfollow(self, followerId: int, followeeId: int) -> None: + if followeeId in self.followMap[followerId]: + self.followMap[followerId].remove(followeeId) +``` + +```java +public class Twitter { + + private int count; + private Map> tweetMap; + private Map> followMap; + + public Twitter() { + this.count = 0; + this.tweetMap = new HashMap<>(); + this.followMap = new HashMap<>(); + } + + public void postTweet(int userId, int tweetId) { + tweetMap.computeIfAbsent(userId, k -> new ArrayList<>()) + .add(new int[]{count, tweetId}); + if (tweetMap.get(userId).size() > 10) { + tweetMap.get(userId).remove(0); + } + count--; + } + + public List getNewsFeed(int userId) { + List res = new ArrayList<>(); + PriorityQueue minHeap = new PriorityQueue<>( + (a, b) -> Integer.compare(a[0], b[0]) + ); + followMap.computeIfAbsent(userId, k -> new HashSet<>()).add(userId); + if (followMap.get(userId).size() >= 10) { + PriorityQueue maxHeap = new PriorityQueue<>( + (a, b) -> Integer.compare(a[0], b[0]) + ); + for (int followeeId : followMap.get(userId)) { + if (!tweetMap.containsKey(followeeId)) continue; + List tweets = tweetMap.get(followeeId); + int index = tweets.size() - 1; + int[] tweet = tweets.get(index); + maxHeap.offer(new int[]{-tweet[0], tweet[1], followeeId, index - 1}); + if (maxHeap.size() > 10) { + maxHeap.poll(); + } + } + while (!maxHeap.isEmpty()) { + int[] top = maxHeap.poll(); + minHeap.offer(new int[]{-top[0], top[1], top[2], top[3]}); + } + } else { + for (int followeeId : followMap.get(userId)) { + if (!tweetMap.containsKey(followeeId)) continue; + List tweets = tweetMap.get(followeeId); + int index = tweets.size() - 1; + int[] tweet = tweets.get(index); + minHeap.offer(new int[]{tweet[0], tweet[1], followeeId, index - 1}); + } + } + + while (!minHeap.isEmpty() && res.size() < 10) { + int[] top = minHeap.poll(); + res.add(top[1]); + int nextIndex = top[3]; + if (nextIndex >= 0) { + List tweets = tweetMap.get(top[2]); + int[] nextTweet = tweets.get(nextIndex); + minHeap.offer(new int[]{nextTweet[0], nextTweet[1], top[2], nextIndex - 1}); + } + } + return res; + } + + public void follow(int followerId, int followeeId) { + followMap.computeIfAbsent(followerId, k -> new HashSet<>()).add(followeeId); + } + + public void unfollow(int followerId, int followeeId) { + if (followMap.containsKey(followerId)) { + followMap.get(followerId).remove(followeeId); + } + } +} +``` + +```cpp +class Twitter { +public: + int count; + unordered_map>> tweetMap; + unordered_map> followMap; + + Twitter() { + count = 0; + } + + void postTweet(int userId, int tweetId) { + tweetMap[userId].push_back({count, tweetId}); + if (tweetMap[userId].size() > 10) { + tweetMap[userId].erase(tweetMap[userId].begin()); + } + count--; + } + + vector getNewsFeed(int userId) { + vector res; + followMap[userId].insert(userId); + priority_queue, vector>, greater>> minHeap; + if (followMap[userId].size() >= 10) { + priority_queue> maxHeap; + for (auto f : followMap[userId]) { + if (!tweetMap.count(f)) continue; + int idx = tweetMap[f].size() - 1; + auto &p = tweetMap[f][idx]; + maxHeap.push({-p.first, p.second, f, idx - 1}); + if (maxHeap.size() > 10) maxHeap.pop(); + } + while (!maxHeap.empty()) { + auto t = maxHeap.top(); + maxHeap.pop(); + minHeap.push({-t[0], t[1], t[2], t[3]}); + } + } else { + for (auto f : followMap[userId]) { + if (!tweetMap.count(f)) continue; + int idx = tweetMap[f].size() - 1; + auto &p = tweetMap[f][idx]; + minHeap.push({p.first, p.second, f, idx - 1}); + } + } + while (!minHeap.empty() && res.size() < 10) { + auto t = minHeap.top(); + minHeap.pop(); + res.push_back(t[1]); + int idx = t[3]; + if (idx >= 0) { + auto &p = tweetMap[t[2]][idx]; + minHeap.push({p.first, p.second, t[2], idx - 1}); + } + } + return res; + } + + void follow(int followerId, int followeeId) { + followMap[followerId].insert(followeeId); + } + + void unfollow(int followerId, int followeeId) { + if (followMap[followerId].count(followeeId)) { + followMap[followerId].erase(followeeId); + } + } +}; +``` + +```javascript +/** + * const { PriorityQueue } = require('@datastructures-js/priority-queue'); + */ +class Twitter { + constructor() { + this.count = 0; + this.tweetMap = new Map(); + this.followMap = new Map(); + } + + /** + * @param {number} userId + * @param {number} tweetId + * @return {void} + */ + postTweet(userId, tweetId) { + if (!this.tweetMap.has(userId)) { + this.tweetMap.set(userId, []); + } + const tweets = this.tweetMap.get(userId); + tweets.push([this.count, tweetId]); + if (tweets.length > 10) { + tweets.shift(); + } + this.count--; + } + + /** + * @param {number} userId + * @return {number[]} + */ + getNewsFeed(userId) { + const res = []; + if (!this.followMap.has(userId)) { + this.followMap.set(userId, new Set()); + } + this.followMap.get(userId).add(userId); + const minHeap = new PriorityQueue((a, b) => a[0] - b[0]); + + if (this.followMap.get(userId).size >= 10) { + const maxHeap = new PriorityQueue((a, b) => a[0] - b[0]); + for (const followeeId of this.followMap.get(userId)) { + if (!this.tweetMap.has(followeeId)) continue; + const tweets = this.tweetMap.get(followeeId); + const idx = tweets.length - 1; + const [cnt, tId] = tweets[idx]; + maxHeap.enqueue([-cnt, tId, followeeId, idx - 1]); + if (maxHeap.size() > 10) { + maxHeap.dequeue(); + } + } + while (maxHeap.size() > 0) { + const [negCount, tId, fId, idx] = maxHeap.dequeue(); + minHeap.enqueue([-negCount, tId, fId, idx]); + } + + } else { + for (const followeeId of this.followMap.get(userId)) { + if (!this.tweetMap.has(followeeId)) continue; + const tweets = this.tweetMap.get(followeeId); + const idx = tweets.length - 1; + const [cnt, tId] = tweets[idx]; + minHeap.enqueue([cnt, tId, followeeId, idx - 1]); + } + } + + while (minHeap.size() > 0 && res.length < 10) { + const [cnt, tId, fId, idx] = minHeap.dequeue(); + res.push(tId); + if (idx >= 0) { + const [olderCount, olderTId] = this.tweetMap.get(fId)[idx]; + minHeap.enqueue([olderCount, olderTId, fId, idx - 1]); + } + } + return res; + } + + /** + * @param {number} followerId + * @param {number} followeeId + * @return {void} + */ + follow(followerId, followeeId) { + if (!this.followMap.has(followerId)) { + this.followMap.set(followerId, new Set()); + } + this.followMap.get(followerId).add(followeeId); + } + + /** + * @param {number} followerId + * @param {number} followeeId + * @return {void} + */ + unfollow(followerId, followeeId) { + if (this.followMap.has(followerId)) { + this.followMap.get(followerId).delete(followeeId); + } + } +} +``` + +```csharp +public class Twitter +{ + private int count; + private Dictionary> tweetMap; // userId -> list of (count, tweetId) + private Dictionary> followMap; // userId -> set of followeeId + + public Twitter() + { + count = 0; + tweetMap = new Dictionary>(); + followMap = new Dictionary>(); + } + + public void PostTweet(int userId, int tweetId) + { + if (!tweetMap.ContainsKey(userId)) + { + tweetMap[userId] = new List<(int, int)>(); + } + tweetMap[userId].Add((count, tweetId)); + if (tweetMap[userId].Count > 10) + { + tweetMap[userId].RemoveAt(0); + } + count--; + } + + public List GetNewsFeed(int userId) + { + var res = new List(); + if (!followMap.ContainsKey(userId)) + { + followMap[userId] = new HashSet(); + } + followMap[userId].Add(userId); + var minHeap = new PriorityQueue<(int, int, int, int), int>(); + if (followMap[userId].Count >= 10) + { + var maxHeap = new PriorityQueue<(int, int, int, int), int>(); + foreach (var fId in followMap[userId]) + { + if (tweetMap.ContainsKey(fId)) + { + var tweets = tweetMap[fId]; + int idx = tweets.Count - 1; + var (c, tId) = tweets[idx]; + maxHeap.Enqueue( + ( -c, tId, fId, idx - 1 ), + -c + ); + if (maxHeap.Count > 10) + { + maxHeap.Dequeue(); + } + } + } + + while (maxHeap.Count > 0) + { + var item = maxHeap.Dequeue(); + var negCount = item.Item1; + var tId = item.Item2; + var fId = item.Item3; + var idx = item.Item4; + + int originalCount = -negCount; + minHeap.Enqueue( + ( originalCount, tId, fId, idx ), + originalCount + ); + } + } + else + { + foreach (var fId in followMap[userId]) + { + if (tweetMap.ContainsKey(fId)) + { + var tweets = tweetMap[fId]; + int idx = tweets.Count - 1; + var (c, tId) = tweets[idx]; + minHeap.Enqueue( + ( c, tId, fId, idx - 1 ), + c + ); + } + } + } + + while (minHeap.Count > 0 && res.Count < 10) + { + var (c, tId, fId, idx) = minHeap.Dequeue(); + res.Add(tId); + if (idx >= 0) + { + var (olderCount, olderTid) = tweetMap[fId][idx]; + minHeap.Enqueue( + ( olderCount, olderTid, fId, idx - 1 ), + olderCount + ); + } + } + + return res; + } + + public void Follow(int followerId, int followeeId) + { + if (!followMap.ContainsKey(followerId)) + { + followMap[followerId] = new HashSet(); + } + followMap[followerId].Add(followeeId); + } + + public void Unfollow(int followerId, int followeeId) + { + if (followMap.ContainsKey(followerId)) + { + followMap[followerId].Remove(followeeId); + } + } +} +``` + +```go +type Twitter struct { + count int + tweetMap map[int][][2]int // userId -> [count, tweetId] + followMap map[int]map[int]bool // userId -> set of followeeIds +} + +func Constructor() Twitter { + return Twitter{ + count: 0, + tweetMap: make(map[int][][2]int), + followMap: make(map[int]map[int]bool), + } +} + +func (t *Twitter) PostTweet(userId int, tweetId int) { + if _, exists := t.tweetMap[userId]; !exists { + t.tweetMap[userId] = make([][2]int, 0, 10) + } + t.tweetMap[userId] = append(t.tweetMap[userId], [2]int{t.count, tweetId}) + if len(t.tweetMap[userId]) > 10 { + t.tweetMap[userId] = t.tweetMap[userId][1:] + } + t.count-- +} + +func maxHeapComparator(a, b interface{}) int { + A := a.([]int) + B := b.([]int) + switch { + case A[0] < B[0]: + return -1 + case A[0] > B[0]: + return 1 + default: + return 0 + } +} + +func minHeapComparator(a, b interface{}) int { + A := a.([]int) + B := b.([]int) + switch { + case A[0] < B[0]: + return -1 + case A[0] > B[0]: + return 1 + default: + return 0 + } +} + +func (t *Twitter) GetNewsFeed(userId int) []int { + res := []int{} + if _, ok := t.followMap[userId]; !ok { + t.followMap[userId] = make(map[int]bool) + } + t.followMap[userId][userId] = true + minHeap := priorityqueue.NewWith(minHeapComparator) + + if len(t.followMap[userId]) >= 10 { + maxHeap := priorityqueue.NewWith(maxHeapComparator) + for fId := range t.followMap[userId] { + if tweets, exists := t.tweetMap[fId]; exists && len(tweets) > 0 { + idx := len(tweets) - 1 + c := tweets[idx][0] + tId := tweets[idx][1] + maxHeap.Enqueue([]int{-c, tId, fId, idx - 1}) + if maxHeap.Size() > 10 { + maxHeap.Dequeue() + } + } + } + + for !maxHeap.Empty() { + item, _ := maxHeap.Dequeue() + arr := item.([]int) + negCount := arr[0] + tId := arr[1] + fId := arr[2] + nextIdx := arr[3] + realCount := -negCount + minHeap.Enqueue([]int{realCount, tId, fId, nextIdx}) + } + } else { + for fId := range t.followMap[userId] { + if tweets, exists := t.tweetMap[fId]; exists && len(tweets) > 0 { + idx := len(tweets) - 1 + c := tweets[idx][0] + tId := tweets[idx][1] + minHeap.Enqueue([]int{c, tId, fId, idx - 1}) + } + } + } + + for !minHeap.Empty() && len(res) < 10 { + top, _ := minHeap.Dequeue() + arr := top.([]int) + tId := arr[1] + fId := arr[2] + nextIdx := arr[3] + + res = append(res, tId) + if nextIdx >= 0 { + older := t.tweetMap[fId][nextIdx] + minHeap.Enqueue([]int{older[0], older[1], fId, nextIdx - 1}) + } + } + + return res +} + +func (t *Twitter) Follow(followerId, followeeId int) { + if _, ok := t.followMap[followerId]; !ok { + t.followMap[followerId] = make(map[int]bool) + } + t.followMap[followerId][followeeId] = true +} + +func (t *Twitter) Unfollow(followerId, followeeId int) { + if _, ok := t.followMap[followerId]; ok { + delete(t.followMap[followerId], followeeId) + } +} +``` + +```kotlin +class Twitter { + private var count = 0 + private val tweetMap = mutableMapOf>>() + private val followMap = mutableMapOf>() + + fun postTweet(userId: Int, tweetId: Int) { + if (!tweetMap.containsKey(userId)) { + tweetMap[userId] = mutableListOf() + } + val tweets = tweetMap[userId]!! + tweets.add(Pair(count, tweetId)) + if (tweets.size > 10) { + tweets.removeAt(0) + } + count-- + } + + fun getNewsFeed(userId: Int): List { + val res = mutableListOf() + if (!followMap.containsKey(userId)) { + followMap[userId] = mutableSetOf() + } + followMap[userId]!!.add(userId) + val minHeap = PriorityQueue> { a, b -> a[0].compareTo(b[0]) } + if (followMap[userId]!!.size >= 10) { + val maxHeap = PriorityQueue> { a, b -> a[0].compareTo(b[0]) } + for (fId in followMap[userId]!!) { + if (!tweetMap.containsKey(fId)) continue + val tweets = tweetMap[fId]!! + if (tweets.isEmpty()) continue + val idx = tweets.size - 1 + val (c, tId) = tweets[idx] + maxHeap.offer(listOf(-c, tId, fId, idx - 1)) + if (maxHeap.size > 10) { + maxHeap.poll() + } + } + while (maxHeap.isNotEmpty()) { + val (negCount, tId, fId, nextIdx) = maxHeap.poll() + val realCount = -negCount + minHeap.offer(listOf(realCount, tId, fId, nextIdx)) + } + } else { + for (fId in followMap[userId]!!) { + if (!tweetMap.containsKey(fId)) continue + val tweets = tweetMap[fId]!! + if (tweets.isEmpty()) continue + val idx = tweets.size - 1 + val (c, tId) = tweets[idx] + minHeap.offer(listOf(c, tId, fId, idx - 1)) + } + } + + while (minHeap.isNotEmpty() && res.size < 10) { + val (c, tId, fId, idx) = minHeap.poll() + res.add(tId) + if (idx >= 0) { + val (olderCount, olderTid) = tweetMap[fId]!![idx] + minHeap.offer(listOf(olderCount, olderTid, fId, idx - 1)) + } + } + + return res + } + + fun follow(followerId: Int, followeeId: Int) { + if (!followMap.containsKey(followerId)) { + followMap[followerId] = mutableSetOf() + } + followMap[followerId]!!.add(followeeId) + } + + fun unfollow(followerId: Int, followeeId: Int) { + if (followMap.containsKey(followerId)) { + followMap[followerId]!!.remove(followeeId) + } + } +} +``` + +::tabs-end + +### Time & Space Complexity + * Time complexity: $O(n)$ for each $getNewsFeed()$ call and $O(1)$ for remaining methods. * Space complexity: $O(N * m + N * M + n)$ -> Where $n$ is the total number of $followeeIds$ associated with the $userId$, $m$ is the maximum number of tweets by any user, $N$ is the total number of $userIds$ and $M$ is the maximum number of followees for any user. \ No newline at end of file +> Where $n$ is the total number of $followeeIds$ associated with the $userId$, $m$ is the maximum number of tweets by any user ($m$ can be at most $10$), $N$ is the total number of $userIds$ and $M$ is the maximum number of followees for any user. \ No newline at end of file diff --git a/articles/evaluate-boolean-binary-tree.md b/articles/evaluate-boolean-binary-tree.md new file mode 100644 index 000000000..4b8fd67ca --- /dev/null +++ b/articles/evaluate-boolean-binary-tree.md @@ -0,0 +1,309 @@ +## 1. Depth First Search + +::tabs-start + +```python +# 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 +class Solution: + def evaluateTree(self, root: Optional[TreeNode]) -> bool: + if not root.left: + return root.val == 1 + + if root.val == 2: + return self.evaluateTree(root.left) or self.evaluateTree(root.right) + + if root.val == 3: + return self.evaluateTree(root.left) and self.evaluateTree(root.right) +``` + +```java +/** + * 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; + * } + * } + */ +public class Solution { + public boolean evaluateTree(TreeNode root) { + if (root.left == null) { + return root.val == 1; + } + + if (root.val == 2) { + return evaluateTree(root.left) || evaluateTree(root.right); + } + + if (root.val == 3) { + return evaluateTree(root.left) && evaluateTree(root.right); + } + + return false; + } +} +``` + +```cpp +/** + * 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: + bool evaluateTree(TreeNode* root) { + if (!root->left) { + return root->val == 1; + } + + if (root->val == 2) { + return evaluateTree(root->left) || evaluateTree(root->right); + } + + if (root->val == 3) { + return evaluateTree(root->left) && evaluateTree(root->right); + } + + return false; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {boolean} + */ + evaluateTree(root) { + if (!root.left) { + return root.val === 1; + } + + if (root.val === 2) { + return this.evaluateTree(root.left) || this.evaluateTree(root.right); + } + + if (root.val === 3) { + return this.evaluateTree(root.left) && this.evaluateTree(root.right); + } + + return false; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ for recursion stack. + +--- + +## 2. Iterative DFS + +::tabs-start + +```python +# 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 +class Solution: + def evaluateTree(self, root: Optional[TreeNode]) -> bool: + stack = [root] + value = {} # map (node -> value) + + while stack: + node = stack.pop() + + if not node.left: + value[node] = node.val == 1 + elif node.left in value: + if node.val == 2: + value[node] = value[node.left] or value[node.right] + if node.val == 3: + value[node] = value[node.left] and value[node.right] + else: + stack.extend([node, node.left, node.right]) + + return value[root] +``` + +```java +/** + * 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; + * } + * } + */ +public class Solution { + public boolean evaluateTree(TreeNode root) { + Stack stack = new Stack<>(); + Map value = new HashMap<>(); + stack.push(root); + + while (!stack.isEmpty()) { + TreeNode node = stack.pop(); + + if (node.left == null) { + value.put(node, node.val == 1); + } else if (value.containsKey(node.left)) { + boolean leftValue = value.get(node.left); + boolean rightValue = value.get(node.right); + + if (node.val == 2) { + value.put(node, leftValue || rightValue); + } else if (node.val == 3) { + value.put(node, leftValue && rightValue); + } + } else { + stack.push(node); + stack.push(node.right); + stack.push(node.left); + } + } + + return value.get(root); + } +} +``` + +```cpp +/** + * 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: + bool evaluateTree(TreeNode* root) { + stack stk; + unordered_map value; + stk.push(root); + + while (!stk.empty()) { + TreeNode* node = stk.top(); + stk.pop(); + + if (!node->left) { + value[node] = node->val == 1; + } else if (value.count(node->left)) { + bool leftValue = value[node->left]; + bool rightValue = value[node->right]; + + if (node->val == 2) { + value[node] = leftValue || rightValue; + } else if (node->val == 3) { + value[node] = leftValue && rightValue; + } + } else { + stk.push(node); + stk.push(node->right); + stk.push(node->left); + } + } + + return value[root]; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {boolean} + */ + evaluateTree(root) { + const stack = [root]; + const value = new Map(); + + while (stack.length) { + const node = stack.pop(); + + if (!node.left) { + value.set(node, node.val === 1); + } else if (value.has(node.left)) { + const leftValue = value.get(node.left); + const rightValue = value.get(node.right); + + if (node.val === 2) { + value.set(node, leftValue || rightValue); + } else if (node.val === 3) { + value.set(node, leftValue && rightValue); + } + } else { + stack.push(node, node.right, node.left); + } + } + + return value.get(root); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/find-duplicate-subtrees.md b/articles/find-duplicate-subtrees.md new file mode 100644 index 000000000..bf3d6d7e9 --- /dev/null +++ b/articles/find-duplicate-subtrees.md @@ -0,0 +1,550 @@ +## 1. Brute Force (DFS) + +::tabs-start + +```python +# 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 +class Solution: + def findDuplicateSubtrees(self, root: Optional[TreeNode]) -> List[Optional[TreeNode]]: + def same(node1, node2): + if not node1 and not node2: + return True + if not node1 or not node2: + return False + return (node1.val == node2.val and + same(node1.left, node2.left) and + same(node1.right, node2.right)) + + subTree = [] + def dfs(root): + if not root: + return + subTree.append(root) + dfs(root.left) + dfs(root.right) + + dfs(root) + res = [] + seen = set() + + for i in range(len(subTree)): + if subTree[i] in seen: + continue + for j in range(i + 1, len(subTree)): + if subTree[j] in seen: + continue + + if same(subTree[i], subTree[j]): + if subTree[i] not in seen: + res.append(subTree[i]) + seen.add(subTree[i]) + seen.add(subTree[j]) + return res +``` + +```java +/** + * 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; + * } + * } + */ +public class Solution { + public List findDuplicateSubtrees(TreeNode root) { + List res = new ArrayList<>(); + Set seen = new HashSet<>(); + List subTree = new ArrayList<>(); + dfs(root, subTree); + + for (int i = 0; i < subTree.size(); i++) { + if (seen.contains(subTree.get(i))) continue; + for (int j = i + 1; j < subTree.size(); j++) { + if (seen.contains(subTree.get(j))) continue; + + if (same(subTree.get(i), subTree.get(j))) { + if (!seen.contains(subTree.get(i))) { + res.add(subTree.get(i)); + seen.add(subTree.get(i)); + } + seen.add(subTree.get(j)); + } + } + } + return res; + } + + private boolean same(TreeNode node1, TreeNode node2) { + if (node1 == null && node2 == null) return true; + if (node1 == null || node2 == null) return false; + return node1.val == node2.val && + same(node1.left, node2.left) && + same(node1.right, node2.right); + } + + private void dfs(TreeNode root, List subTree) { + if (root == null) return; + subTree.add(root); + dfs(root.left, subTree); + dfs(root.right, subTree); + } +} +``` + +```cpp +/** + * 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: + vector findDuplicateSubtrees(TreeNode* root) { + vector res; + unordered_set seen; + vector subTree; + dfs(root, subTree); + + for (int i = 0; i < subTree.size(); i++) { + if (seen.count(subTree[i])) continue; + for (int j = i + 1; j < subTree.size(); j++) { + if (seen.count(subTree[j])) continue; + + if (same(subTree[i], subTree[j])) { + if (!seen.count(subTree[i])) { + res.push_back(subTree[i]); + seen.insert(subTree[i]); + } + seen.insert(subTree[j]); + } + } + } + return res; + } + +private: + bool same(TreeNode* node1, TreeNode* node2) { + if (!node1 && !node2) return true; + if (!node1 || !node2) return false; + return node1->val == node2->val && + same(node1->left, node2->left) && + same(node1->right, node2->right); + } + + void dfs(TreeNode* root, vector& subTree) { + if (!root) return; + subTree.push_back(root); + dfs(root->left, subTree); + dfs(root->right, subTree); + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {TreeNode[]} + */ + findDuplicateSubtrees(root) { + const res = []; + const seen = new Set(); + + const subTree = []; + const dfs = (root) => { + if (!root) return; + subTree.push(root); + dfs(root.left); + dfs(root.right); + }; + dfs(root); + + const same = (node1, node2) => { + if (!node1 && !node2) return true; + if (!node1 || !node2) return false; + return node1.val === node2.val && + same(node1.left, node2.left) && + same(node1.right, node2.right); + }; + + + for (let i = 0; i < subTree.length; i++) { + if (seen.has(subTree[i])) continue; + for (let j = i + 1; j < subTree.length; j++) { + if (seen.has(subTree[j])) continue; + + if (same(subTree[i], subTree[j])) { + if (!seen.has(subTree[i])) { + res.push(subTree[i]); + seen.add(subTree[i]); + } + seen.add(subTree[j]); + } + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 3)$ +* Space complexity: $O(n)$ + +--- + +## 2. DFS + Serialization + +::tabs-start + +```python +# 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 +class Solution: + def findDuplicateSubtrees(self, root: Optional[TreeNode]) -> List[Optional[TreeNode]]: + subtrees = defaultdict(list) + res = [] + + def dfs(node): + if not node: + return "null" + s = ",".join([str(node.val), dfs(node.left), dfs(node.right)]) + if len(subtrees[s]) == 1: + res.append(node) + subtrees[s].append(node) + return s + + dfs(root) + return res +``` + +```java +/** + * 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; + * } + * } + */ +public class Solution { + private Map> subtrees; + private List res; + + public List findDuplicateSubtrees(TreeNode root) { + subtrees = new HashMap<>(); + res = new ArrayList<>(); + dfs(root); + return res; + } + + private String dfs(TreeNode node) { + if (node == null) return "null"; + String s = node.val + "," + dfs(node.left) + "," + dfs(node.right); + subtrees.putIfAbsent(s, new ArrayList<>()); + if (subtrees.get(s).size() == 1) { + res.add(node); + } + subtrees.get(s).add(node); + return s; + } +} +``` + +```cpp +/** + * 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 { + unordered_map> subtrees; + vector res; + +public: + vector findDuplicateSubtrees(TreeNode* root) { + dfs(root); + return res; + } + +private: + string dfs(TreeNode* node) { + if (!node) return "null"; + string s = to_string(node->val) + "," + dfs(node->left) + "," + dfs(node->right); + if (subtrees[s].size() == 1) { + res.push_back(node); + } + subtrees[s].push_back(node); + return s; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {TreeNode[]} + */ + findDuplicateSubtrees(root) { + const subtrees = new Map(); + const res = []; + + const dfs = (node) => { + if (!node) return "null"; + const s = `${node.val},${dfs(node.left)},${dfs(node.right)}`; + if (!subtrees.has(s)) { + subtrees.set(s, []); + } + if (subtrees.get(s).length === 1) { + res.push(node); + } + subtrees.get(s).push(node); + return s; + }; + + dfs(root); + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n ^ 2)$ + +--- + +## 3. Depth First Search (Optimal) + +::tabs-start + +```python +# 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 +class Solution: + def findDuplicateSubtrees(self, root: Optional[TreeNode]) -> List[Optional[TreeNode]]: + id_map = {} + count = defaultdict(int) + res = [] + + def dfs(node): + if not node: + return -1 + cur = (dfs(node.left), node.val, dfs(node.right)) + if cur not in id_map: + id_map[cur] = len(id_map) + 1 + + curId = id_map[cur] + if count[curId] == 1: + res.append(node) + count[curId] += 1 + return curId + + dfs(root) + return res +``` + +```java +/** + * 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; + * } + * } + */ +public class Solution { + private Map idMap; + private Map count; + private List res; + + public List findDuplicateSubtrees(TreeNode root) { + idMap = new HashMap<>(); + count = new HashMap<>(); + res = new ArrayList<>(); + + dfs(root); + return res; + } + + private int dfs(TreeNode node) { + if (node == null) return -1; + String cur = dfs(node.left) + "," + node.val + "," + dfs(node.right); + idMap.putIfAbsent(cur, idMap.size()); + int curId = idMap.get(cur); + count.put(curId, count.getOrDefault(curId, 0) + 1); + if (count.get(curId) == 2) { + res.add(node); + } + return curId; + } +} +``` + +```cpp +/** + * 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 { + unordered_map idMap; + unordered_map count; + vector res; + +public: + vector findDuplicateSubtrees(TreeNode* root) { + dfs(root); + return res; + } + +private: + int dfs(TreeNode* node) { + if (!node) return -1; + string cur = to_string(dfs(node->left)) + "," + + to_string(node->val) + "," + + to_string(dfs(node->right)); + if (idMap.find(cur) == idMap.end()) { + idMap[cur] = idMap.size(); + } + int curId = idMap[cur]; + count[curId]++; + if (count[curId] == 2) { + res.push_back(node); + } + return curId; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {TreeNode[]} + */ + findDuplicateSubtrees(root) { + const idMap = new Map(); + const count = new Map(); + const res = []; + + const dfs = (node) => { + if (!node) return -1; + + const cur = `${dfs(node.left)},${node.val},${dfs(node.right)}`; + if (!idMap.has(cur)) { + idMap.set(cur, idMap.size + 1); + } + + const curId = idMap.get(cur); + count.set(curId, (count.get(curId) || 0) + 1); + if (count.get(curId) === 2) { + res.push(node); + } + + return curId; + }; + + dfs(root); + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/invert-a-binary-tree.md b/articles/invert-a-binary-tree.md index ed6a548cc..2c7622067 100644 --- a/articles/invert-a-binary-tree.md +++ b/articles/invert-a-binary-tree.md @@ -387,7 +387,7 @@ public class Solution { --- -## 3. Depth First Search (Stack) +## 3. Iterative DFS ::tabs-start diff --git a/articles/leaf-similar-trees.md b/articles/leaf-similar-trees.md new file mode 100644 index 000000000..5e92ef739 --- /dev/null +++ b/articles/leaf-similar-trees.md @@ -0,0 +1,504 @@ +## 1. Depth First Search + +::tabs-start + +```python +# 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 +class Solution: + def leafSimilar(self, root1: Optional[TreeNode], root2: Optional[TreeNode]) -> bool: + def dfs(root, leaf): + if not root: + return + if not root.left and not root.right: + leaf.append(root.val) + return + dfs(root.left, leaf) + dfs(root.right, leaf) + + leaf1, leaf2 = [], [] + dfs(root1, leaf1) + dfs(root2, leaf2) + return leaf1 == leaf2 +``` + +```java +/** + * 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; + * } + * } + */ +public class Solution { + public boolean leafSimilar(TreeNode root1, TreeNode root2) { + List leaf1 = new ArrayList<>(); + List leaf2 = new ArrayList<>(); + + dfs(root1, leaf1); + dfs(root2, leaf2); + + return leaf1.equals(leaf2); + } + + private void dfs(TreeNode root, List leaf) { + if (root == null) return; + if (root.left == null && root.right == null) { + leaf.add(root.val); + return; + } + dfs(root.left, leaf); + dfs(root.right, leaf); + } +} +``` + +```cpp +/** + * 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: + bool leafSimilar(TreeNode* root1, TreeNode* root2) { + vector leaf1, leaf2; + dfs(root1, leaf1); + dfs(root2, leaf2); + return leaf1 == leaf2; + } + +private: + void dfs(TreeNode* root, vector& leaf) { + if (!root) return; + if (!root->left && !root->right) { + leaf.push_back(root->val); + return; + } + dfs(root->left, leaf); + dfs(root->right, leaf); + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root1 + * @param {TreeNode} root2 + * @return {boolean} + */ + leafSimilar(root1, root2) { + const dfs = (root, leaf) => { + if (!root) return; + if (!root.left && !root.right) { + leaf.push(root.val); + return; + } + dfs(root.left, leaf); + dfs(root.right, leaf); + }; + + const leaf1 = []; + const leaf2 = []; + dfs(root1, leaf1); + dfs(root2, leaf2); + + return JSON.stringify(leaf1) === JSON.stringify(leaf2); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n + m)$ +* Space complexity: $O(n + m)$ + +> Where $n$ and $m$ are the number of nodes in the given trees. + +--- + +## 2. Depth First Search (Space Optimized) + +::tabs-start + +```python +# 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 +class Solution: + def leafSimilar(self, root1: Optional[TreeNode], root2: Optional[TreeNode]) -> bool: + def dfs(root, leaf): + if not root: + return + if not root.left and not root.right: + leaf.append(root.val) + return + dfs(root.left, leaf) + dfs(root.right, leaf) + + leaf1 = [] + dfs(root1, leaf1) + + def dfs1(root, leaf): + if not root: + return True + if not root.left and not root.right: + if not leaf: + return False + return leaf.pop() == root.val + return dfs1(root.right, leaf) and dfs1(root.left, leaf) + + return dfs1(root2, leaf1) and not leaf1 +``` + +```java +/** + * 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; + * } + * } + */ +public class Solution { + public boolean leafSimilar(TreeNode root1, TreeNode root2) { + Stack leaf1 = new Stack<>(); + dfs(root1, leaf1); + return dfs1(root2, leaf1) && leaf1.isEmpty(); + } + + private void dfs(TreeNode root, Stack leaf) { + if (root == null) return; + if (root.left == null && root.right == null) { + leaf.push(root.val); + return; + } + dfs(root.left, leaf); + dfs(root.right, leaf); + } + + private boolean dfs1(TreeNode root, Stack leaf) { + if (root == null) return true; + if (root.left == null && root.right == null) { + if (leaf.isEmpty()) return false; + return leaf.pop() == root.val; + } + return dfs1(root.right, leaf) && dfs1(root.left, leaf); + } +} +``` + +```cpp +/** + * 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: + bool leafSimilar(TreeNode* root1, TreeNode* root2) { + vector leaf1; + dfs(root1, leaf1); + return dfs1(root2, leaf1) && leaf1.empty(); + } + +private: + void dfs(TreeNode* root, vector& leaf) { + if (!root) return; + if (!root->left && !root->right) { + leaf.push_back(root->val); + return; + } + dfs(root->left, leaf); + dfs(root->right, leaf); + } + + bool dfs1(TreeNode* root, vector& leaf) { + if (!root) return true; + if (!root->left && !root->right) { + if (leaf.empty() || leaf.back() != root->val) { + return false; + } + leaf.pop_back(); + return true; + } + return dfs1(root->right, leaf) && dfs1(root->left, leaf); + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root1 + * @param {TreeNode} root2 + * @return {boolean} + */ + leafSimilar(root1, root2) { + const dfs = (root, leaf) => { + if (!root) return; + if (!root.left && !root.right) { + leaf.push(root.val); + return; + } + dfs(root.left, leaf); + dfs(root.right, leaf); + }; + + const dfs1 = (root, leaf) => { + if (!root) return true; + if (!root.left && !root.right) { + if (!leaf.length) return false; + return leaf.pop() === root.val; + } + return dfs1(root.right, leaf) && dfs1(root.left, leaf); + }; + + const leaf1 = []; + dfs(root1, leaf1); + return dfs1(root2, leaf1) && leaf1.length === 0; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n + m)$ +* Space complexity: $O(n + m)$ + +> Where $n$ and $m$ are the number of nodes in the given trees. + +--- + +## 3. Iterative DFS + +::tabs-start + +```python +# 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 +class Solution: + def leafSimilar(self, root1: Optional[TreeNode], root2: Optional[TreeNode]) -> bool: + def getPathLeaf(stack): + while stack: + node = stack.pop() + if node.right: + stack.append(node.right) + if node.left: + stack.append(node.left) + if not node.left and not node.right: + return node.val + + stack1, stack2 = [root1], [root2] + while stack1 and stack2: + if getPathLeaf(stack1) != getPathLeaf(stack2): + return False + + return not stack1 and not stack2 +``` + +```java +/** + * 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; + * } + * } + */ +public class Solution { + public boolean leafSimilar(TreeNode root1, TreeNode root2) { + Stack stack1 = new Stack<>(); + Stack stack2 = new Stack<>(); + stack1.push(root1); + stack2.push(root2); + + while (!stack1.isEmpty() && !stack2.isEmpty()) { + if (getPathLeaf(stack1) != getPathLeaf(stack2)) { + return false; + } + } + return stack1.isEmpty() && stack2.isEmpty(); + } + + private int getPathLeaf(Stack stack) { + while (!stack.isEmpty()) { + TreeNode node = stack.pop(); + if (node.right != null) { + stack.push(node.right); + } + if (node.left != null) { + stack.push(node.left); + } + if (node.left == null && node.right == null) { + return node.val; + } + } + return -1; + } +} +``` + +```cpp +/** + * 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: + bool leafSimilar(TreeNode* root1, TreeNode* root2) { + stack stack1, stack2; + stack1.push(root1); + stack2.push(root2); + + while (!stack1.empty() && !stack2.empty()) { + if (getPathLeaf(stack1) != getPathLeaf(stack2)) { + return false; + } + } + return stack1.empty() && stack2.empty(); + } + +private: + int getPathLeaf(stack& stack) { + while (!stack.empty()) { + TreeNode* node = stack.top(); + stack.pop(); + if (node->right) { + stack.push(node->right); + } + if (node->left) { + stack.push(node->left); + } + if (!node->left && !node->right) { + return node->val; + } + } + return -1; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root1 + * @param {TreeNode} root2 + * @return {boolean} + */ + leafSimilar(root1, root2) { + const getPathLeaf = (stack) => { + while (stack.length) { + const node = stack.pop(); + if (node.right) stack.push(node.right); + if (node.left) stack.push(node.left); + if (!node.left && !node.right) return node.val; + } + }; + + const stack1 = [root1], stack2 = [root2]; + while (stack1.length && stack2.length) { + if (getPathLeaf(stack1) !== getPathLeaf(stack2)) { + return false; + } + } + return stack1.length === 0 && stack2.length === 0; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n + m)$ +* Space complexity: $O(n + m)$ + +> Where $n$ and $m$ are the number of nodes in the given trees. \ No newline at end of file diff --git a/articles/maximum-width-of-binary-tree.md b/articles/maximum-width-of-binary-tree.md new file mode 100644 index 000000000..ba741fb5d --- /dev/null +++ b/articles/maximum-width-of-binary-tree.md @@ -0,0 +1,513 @@ +## 1. Breadth First Search + +::tabs-start + +```python +# 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 +class Solution: + def widthOfBinaryTree(self, root: Optional[TreeNode]) -> int: + res = 0 + q = deque([(root, 1, 0)]) # [node, num, level] + prevLevel, prevNum = 0, 1 + + while q: + node, num, level = q.popleft() + + if level > prevLevel: + prevLevel = level + prevNum = num + + res = max(res, num - prevNum + 1) + if node.left: + q.append((node.left, 2 * num, level + 1)) + if node.right: + q.append((node.right, 2 * num + 1, level + 1)) + + return res +``` + +```java +/** + * 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; + * } + * } + */ +public class Solution { + public int widthOfBinaryTree(TreeNode root) { + if (root == null) return 0; + + int res = 0; + Queue queue = new LinkedList<>(); + queue.offer(new Tuple(root, 1, 0)); // [node, num, level] + int prevLevel = 0, prevNum = 1; + + while (!queue.isEmpty()) { + Tuple current = queue.poll(); + TreeNode node = current.node; + int num = current.num; + int level = current.level; + + if (level > prevLevel) { + prevLevel = level; + prevNum = num; + } + + res = Math.max(res, num - prevNum + 1); + if (node.left != null) { + queue.offer(new Tuple(node.left, 2 * num, level + 1)); + } + if (node.right != null) { + queue.offer(new Tuple(node.right, 2 * num + 1, level + 1)); + } + } + + return res; + } + + class Tuple { + TreeNode node; + int num, level; + + Tuple(TreeNode node, int num, int level) { + this.node = node; + this.num = num; + this.level = level; + } + } +} +``` + +```cpp +/** + * 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: + int widthOfBinaryTree(TreeNode* root) { + if (!root) return 0; + + int res = 0; + queue> q; // [node, num, level] + q.push({root, 1, 0}); + int prevLevel = 0, prevNum = 1; + + while (!q.empty()) { + auto [node, num, level] = q.front(); + q.pop(); + + if (level > prevLevel) { + prevLevel = level; + prevNum = num; + } + + res = max(res, int(num - prevNum) + 1); + if (node->left) { + q.push({node->left, 2 * num, level + 1}); + } + if (node->right) { + q.push({node->right, 2 * num + 1, level + 1}); + } + } + + return res; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {number} + */ + widthOfBinaryTree(root) { + if (!root) return 0; + + let res = 0n; + const queue = new Queue([[root, 1n, 0]]); // [node, num, level] + let prevLevel = 0, prevNum = 1n; + + while (!queue.isEmpty()) { + const [node, num, level] = queue.pop(); + + if (level > prevLevel) { + prevLevel = level; + prevNum = num; + } + + res = res > (num - prevNum + 1n) ? res : (num - prevNum + 1n); + if (node.left) { + queue.push([node.left, 2n * num, level + 1]); + } + if (node.right) { + queue.push([node.right, 2n * num + 1n, level + 1]); + } + } + + return Number(res); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Breadth First Search (Optimal) + +::tabs-start + +```python +# 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 +class Solution: + def widthOfBinaryTree(self, root: Optional[TreeNode]) -> int: + res = 0 + q = deque([(root, 0)]) + + while q: + start = q[0][1] + for _ in range(len(q)): + node, num = q.popleft() + curNum = num - start + res = max(res, curNum + 1) + if node.left: + q.append((node.left, 2 * curNum)) + if node.right: + q.append((node.right, 2 * curNum + 1)) + + return res +``` + +```java +/** + * 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; + * } + * } + */ +public class Solution { + public int widthOfBinaryTree(TreeNode root) { + int res = 0; + Queue> q = new LinkedList<>(); + q.offer(new Pair<>(root, 0)); + + while (!q.isEmpty()) { + int start = q.peek().getValue(); + for (int i = q.size(); i > 0; i--) { + Pair pair = q.poll(); + TreeNode node = pair.getKey(); + int num = pair.getValue() - start; + + res = Math.max(res, num + 1); + if (node.left != null) { + q.offer(new Pair<>(node.left, 2 * num)); + } + if (node.right != null) { + q.offer(new Pair<>(node.right, 2 * num + 1)); + } + } + } + + return res; + } +} +``` + +```cpp +/** + * 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: + int widthOfBinaryTree(TreeNode* root) { + int res = 0; + queue> q; + q.push({root, 0}); + + while (!q.empty()) { + int start = q.front().second; + + for (int i = q.size(); i > 0; --i) { + auto [node, num] = q.front(); + q.pop(); + uint curNum = num - start; + res = max(res, int(curNum) + 1); + if (node->left) { + q.push({node->left, 2 * curNum}); + } + if (node->right) { + q.push({node->right, 2 * curNum + 1}); + } + } + } + + return res; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {number} + */ + widthOfBinaryTree(root) { + let res = 0; + const q = new Queue([[root, 0]]); + + while (!q.isEmpty()) { + const start = q.front()[1]; + + for (let i = q.size(); i > 0; i--) { + const [node, num] = q.pop(); + const curNum = num - start; + res = Math.max(res, curNum + 1); + if (node.left) { + q.push([node.left, 2 * curNum]); + } + if (node.right) { + q.push([node.right, 2 * curNum + 1]); + } + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Depth First Search + +::tabs-start + +```python +# 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 +class Solution: + def widthOfBinaryTree(self, root: Optional[TreeNode]) -> int: + first = {} + res = 0 + + def dfs(node, level, num): + nonlocal res + if not node: + return + + if level not in first: + first[level] = num + + res = max(res, num - first[level] + 1) + dfs(node.left, level + 1, 2 * num) + dfs(node.right, level + 1, 2 * num + 1) + + dfs(root, 0, 0) + return res +``` + +```java +/** + * 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; + * } + * } + */ +public class Solution { + private Map first; + public int widthOfBinaryTree(TreeNode root) { + first = new HashMap<>(); + int[] res = new int[1]; + + dfs(root, 0, 0, res); + return res[0]; + } + + private void dfs(TreeNode node, int level, int num, int[] res) { + if (node == null) { + return; + } + + first.putIfAbsent(level, num); + res[0] = Math.max(res[0], num - first.get(level) + 1); + dfs(node.left, level + 1, 2 * num, res); + dfs(node.right, level + 1, 2 * num + 1, res); + } +} +``` + +```cpp +/** + * 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 { + unordered_map first; + +public: + int widthOfBinaryTree(TreeNode* root) { + unsigned long long res = 0; + + dfs(root, 0, 0, res); + return int(res); + } + +private: + void dfs(TreeNode* node, int level, unsigned long long num, unsigned long long& res) { + if (!node) { + return; + } + + if (!first.count(level)) { + first[level] = num; + } + + res = max(res, num - first[level] + 1); + dfs(node->left, level + 1, 2 * (num - first[level]), res); + dfs(node->right, level + 1, 2 * (num - first[level]) + 1, res); + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {number} + */ + widthOfBinaryTree(root) { + const first = new Map(); + let res = 0; + + const dfs = (node, level, curNum) => { + if (!node) return; + + if (!first.has(level)) { + first.set(level, curNum); + } + + res = Math.max(res, curNum - first.get(level) + 1); + dfs(node.left, level + 1, 2 * (curNum - first.get(level))); + dfs(node.right, level + 1, 2 * (curNum - first.get(level)) + 1); + }; + + dfs(root, 0, 0); + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/merge-two-binary-trees.md b/articles/merge-two-binary-trees.md new file mode 100644 index 000000000..0b7636434 --- /dev/null +++ b/articles/merge-two-binary-trees.md @@ -0,0 +1,658 @@ +## 1. Depth First Search (Creating New Tree) + +::tabs-start + +```python +# 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 +class Solution: + def mergeTrees(self, root1: Optional[TreeNode], root2: Optional[TreeNode]) -> Optional[TreeNode]: + if not root1 and not root2: + return None + + v1 = root1.val if root1 else 0 + v2 = root2.val if root2 else 0 + root = TreeNode(v1 + v2) + + root.left = self.mergeTrees(root1.left if root1 else None, root2.left if root2 else None) + root.right = self.mergeTrees(root1.right if root1 else None, root2.right if root2 else None) + + return root +``` + +```java +/** + * 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; + * } + * } + */ +public class Solution { + public TreeNode mergeTrees(TreeNode root1, TreeNode root2) { + if (root1 == null && root2 == null) { + return null; + } + + int v1 = (root1 != null) ? root1.val : 0; + int v2 = (root2 != null) ? root2.val : 0; + TreeNode root = new TreeNode(v1 + v2); + + root.left = mergeTrees( + (root1 != null) ? root1.left : null, (root2 != null) ? root2.left : null + ); + root.right = mergeTrees( + (root1 != null) ? root1.right : null, (root2 != null) ? root2.right : null + ); + + return root; + } +} +``` + +```cpp +/** + * 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* mergeTrees(TreeNode* root1, TreeNode* root2) { + if (!root1 && !root2) { + return nullptr; + } + + int v1 = root1 ? root1->val : 0; + int v2 = root2 ? root2->val : 0; + TreeNode* root = new TreeNode(v1 + v2); + + root->left = mergeTrees(root1 ? root1->left : nullptr, root2 ? root2->left : nullptr); + root->right = mergeTrees(root1 ? root1->right : nullptr, root2 ? root2->right : nullptr); + + return root; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root1 + * @param {TreeNode} root2 + * @return {TreeNode} + */ + mergeTrees(root1, root2) { + if (!root1 && !root2) { + return null; + } + + const v1 = root1 ? root1.val : 0; + const v2 = root2 ? root2.val : 0; + const root = new TreeNode(v1 + v2); + + root.left = this.mergeTrees(root1 ? root1.left : null, root2 ? root2.left : null); + root.right = this.mergeTrees(root1 ? root1.right : null, root2 ? root2.right : null); + + return root; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m + n)$ +* Space complexity: + * $O(m + n)$ space for recursion stack. + * $O(m + n)$ space for the output. + +> Where $m$ and $n$ are the number of nodes in the given trees. + +--- + +## 2. Depth First Search (In Place) + +::tabs-start + +```python +# 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 +class Solution: + def mergeTrees(self, root1: Optional[TreeNode], root2: Optional[TreeNode]) -> Optional[TreeNode]: + if not root1: + return root2 + if not root2: + return root1 + + root1.val += root2.val + root1.left = self.mergeTrees(root1.left, root2.left) + root1.right = self.mergeTrees(root1.right, root2.right) + return root1 +``` + +```java +/** + * 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; + * } + * } + */ +public class Solution { + public TreeNode mergeTrees(TreeNode root1, TreeNode root2) { + if (root1 == null) return root2; + if (root2 == null) return root1; + + root1.val += root2.val; + root1.left = mergeTrees(root1.left, root2.left); + root1.right = mergeTrees(root1.right, root2.right); + return root1; + } +} +``` + +```cpp +/** + * 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* mergeTrees(TreeNode* root1, TreeNode* root2) { + if (!root1) return root2; + if (!root2) return root1; + + root1->val += root2->val; + root1->left = mergeTrees(root1->left, root2->left); + root1->right = mergeTrees(root1->right, root2->right); + return root1; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root1 + * @param {TreeNode} root2 + * @return {TreeNode} + */ + mergeTrees(root1, root2) { + if (!root1) return root2; + if (!root2) return root1; + + root1.val += root2.val; + root1.left = this.mergeTrees(root1.left, root2.left); + root1.right = this.mergeTrees(root1.right, root2.right); + return root1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(min(m, n))$ +* Space complexity: $O(min(m, n))$ for recursion stack. + +> Where $m$ and $n$ are the number of nodes in the given trees. + +--- + +## 3. Iterative DFS (Creating New Tree) + +::tabs-start + +```python +# 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 +class Solution: + def mergeTrees(self, root1: Optional[TreeNode], root2: Optional[TreeNode]) -> Optional[TreeNode]: + if not root1: + return root2 + if not root2: + return root1 + + root = TreeNode(root1.val + root2.val) + stack = [(root1, root2, root)] + + while stack: + node1, node2, node = stack.pop() + + if node1.left and node2.left: + node.left = TreeNode(node1.left.val + node2.left.val) + stack.append((node1.left, node2.left, node.left)) + elif not node1.left: + node.left = node2.left + else: + node.left = node1.left + + if node1.right and node2.right: + node.right = TreeNode(node1.right.val + node2.right.val) + stack.append((node1.right, node2.right, node.right)) + elif not node1.right: + node.right = node2.right + else: + node.right = node1.right + + return root +``` + +```java +/** + * 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; + * } + * } + */ +public class Solution { + public TreeNode mergeTrees(TreeNode root1, TreeNode root2) { + if (root1 == null && root2 == null) return null; + + int val = (root1 != null ? root1.val : 0) + (root2 != null ? root2.val : 0); + TreeNode root = new TreeNode(val); + Stack stack = new Stack<>(); + stack.push(new TreeNode[]{root1, root2, root}); + + while (!stack.isEmpty()) { + TreeNode[] nodes = stack.pop(); + TreeNode node1 = nodes[0], node2 = nodes[1], node = nodes[2]; + + TreeNode left1 = node1 != null ? node1.left : null; + TreeNode left2 = node2 != null ? node2.left : null; + if (left1 != null || left2 != null) { + int leftVal = (left1 != null ? left1.val : 0) + (left2 != null ? left2.val : 0); + node.left = new TreeNode(leftVal); + stack.push(new TreeNode[]{left1, left2, node.left}); + } + + TreeNode right1 = node1 != null ? node1.right : null; + TreeNode right2 = node2 != null ? node2.right : null; + if (right1 != null || right2 != null) { + int rightVal = (right1 != null ? right1.val : 0) + (right2 != null ? right2.val : 0); + node.right = new TreeNode(rightVal); + stack.push(new TreeNode[]{right1, right2, node.right}); + } + } + + return root; + } +} +``` + +```cpp +/** + * 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* mergeTrees(TreeNode* root1, TreeNode* root2) { + if (!root1 && !root2) return nullptr; + + int val = (root1 ? root1->val : 0) + (root2 ? root2->val : 0); + TreeNode* root = new TreeNode(val); + stack> st; + st.push({root1, root2, root}); + + while (!st.empty()) { + auto [node1, node2, node] = st.top(); + st.pop(); + + TreeNode* left1 = node1 ? node1->left : nullptr; + TreeNode* left2 = node2 ? node2->left : nullptr; + if (left1 || left2) { + int leftVal = (left1 ? left1->val : 0) + (left2 ? left2->val : 0); + node->left = new TreeNode(leftVal); + st.push({left1, left2, node->left}); + } + + TreeNode* right1 = node1 ? node1->right : nullptr; + TreeNode* right2 = node2 ? node2->right : nullptr; + if (right1 || right2) { + int rightVal = (right1 ? right1->val : 0) + (right2 ? right2->val : 0); + node->right = new TreeNode(rightVal); + st.push({right1, right2, node->right}); + } + } + + return root; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root1 + * @param {TreeNode} root2 + * @return {TreeNode} + */ + mergeTrees(root1, root2) { + if (!root1 && !root2) return null; + + let val = (root1 ? root1.val : 0) + (root2 ? root2.val : 0); + let root = new TreeNode(val); + let stack = [[root1, root2, root]]; + + while (stack.length) { + let [node1, node2, node] = stack.pop(); + + let left1 = node1 ? node1.left : null; + let left2 = node2 ? node2.left : null; + if (left1 || left2) { + let leftVal = (left1 ? left1.val : 0) + (left2 ? left2.val : 0); + node.left = new TreeNode(leftVal); + stack.push([left1, left2, node.left]); + } + + let right1 = node1 ? node1.right : null; + let right2 = node2 ? node2.right : null; + if (right1 || right2) { + let rightVal = (right1 ? right1.val : 0) + (right2 ? right2.val : 0); + node.right = new TreeNode(rightVal); + stack.push([right1, right2, node.right]); + } + } + + return root; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m + n)$ +* Space complexity: + * $O(m + n)$ space for the stack. + * $O(m + n)$ space for the output. + +> Where $m$ and $n$ are the number of nodes in the given trees. + +--- + +## 4. Iterative DFS (In Place) + +::tabs-start + +```python +# 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 +class Solution: + def mergeTrees(self, root1: Optional[TreeNode], root2: Optional[TreeNode]) -> Optional[TreeNode]: + if not root1: + return root2 + if not root2: + return root1 + + stack = [(root1, root2)] + + while stack: + node1, node2 = stack.pop() + if not node1 or not node2: + continue + + node1.val += node2.val + + if node1.left and node2.left: + stack.append((node1.left, node2.left)) + elif not node1.left: + node1.left = node2.left + + if node1.right and node2.right: + stack.append((node1.right, node2.right)) + elif not node1.right: + node1.right = node2.right + + return root1 +``` + +```java +/** + * 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; + * } + * } + */ +public class Solution { + public TreeNode mergeTrees(TreeNode root1, TreeNode root2) { + if (root1 == null) return root2; + if (root2 == null) return root1; + + Stack stack = new Stack<>(); + stack.push(new TreeNode[] { root1, root2 }); + + while (!stack.isEmpty()) { + TreeNode[] nodes = stack.pop(); + TreeNode node1 = nodes[0]; + TreeNode node2 = nodes[1]; + + if (node2 == null) continue; + + node1.val += node2.val; + + if (node1.left == null) { + node1.left = node2.left; + } else { + stack.push(new TreeNode[] { node1.left, node2.left }); + } + + if (node1.right == null) { + node1.right = node2.right; + } else { + stack.push(new TreeNode[] { node1.right, node2.right }); + } + } + + return root1; + } +} +``` + +```cpp +/** + * 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* mergeTrees(TreeNode* root1, TreeNode* root2) { + if (!root1) return root2; + if (!root2) return root1; + + stack> stk; + stk.push({root1, root2}); + + while (!stk.empty()) { + auto [node1, node2] = stk.top(); + stk.pop(); + + if (!node2) continue; + + node1->val += node2->val; + + if (!node1->left) { + node1->left = node2->left; + } else { + stk.push({node1->left, node2->left}); + } + + if (!node1->right) { + node1->right = node2->right; + } else { + stk.push({node1->right, node2->right}); + } + } + + return root1; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root1 + * @param {TreeNode} root2 + * @return {TreeNode} + */ + mergeTrees(root1, root2) { + if (!root1) return root2; + if (!root2) return root1; + + let stack = [[root1, root2]]; + + while (stack.length) { + let [node1, node2] = stack.pop(); + if (!node1 || !node2) continue; + + node1.val += node2.val; + + if (node1.left && node2.left) { + stack.push([node1.left, node2.left]); + } else if (!node1.left) { + node1.left = node2.left; + } + + if (node1.right && node2.right) { + stack.push([node1.right, node2.right]); + } else if (!node1.right) { + node1.right = node2.right; + } + } + + return root1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(min(m, n))$ +* Space complexity: $O(min(m, n))$ for the stack. + +> Where $m$ and $n$ are the number of nodes in the given trees. \ No newline at end of file diff --git a/articles/minimum-distance-between-bst-nodes.md b/articles/minimum-distance-between-bst-nodes.md new file mode 100644 index 000000000..d8cd6bda0 --- /dev/null +++ b/articles/minimum-distance-between-bst-nodes.md @@ -0,0 +1,840 @@ +## 1. Brute Force (DFS) + +::tabs-start + +```python +# 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 +class Solution: + def minDiffInBST(self, root: Optional[TreeNode]) -> int: + def dfs(node): + if not node: + return float("inf") + res = dfs1(root, node) + res = min(res, dfs(node.left)) + res = min(res, dfs(node.right)) + return res + + def dfs1(root, node): + if not root: + return float("inf") + + res = float("inf") + if root != node: + res = abs(root.val - node.val) + res = min(res, dfs1(root.left, node)) + res = min(res, dfs1(root.right, node)) + return res + + return dfs(root) +``` + +```java +/** + * 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; + * } + * } + */ +public class Solution { + public int minDiffInBST(TreeNode root) { + return dfs(root, root); + } + + private int dfs(TreeNode root, TreeNode node) { + if (node == null) { + return Integer.MAX_VALUE; + } + int res = dfs1(root, node); + res = Math.min(res, dfs(root, node.left)); + res = Math.min(res, dfs(root, node.right)); + return res; + } + + private int dfs1(TreeNode root, TreeNode node) { + if (root == null) { + return Integer.MAX_VALUE; + } + int res = Integer.MAX_VALUE; + if (root != node) { + res = Math.abs(root.val - node.val); + } + res = Math.min(res, dfs1(root.left, node)); + res = Math.min(res, dfs1(root.right, node)); + return res; + } +} +``` + +```cpp +/** + * 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: + int minDiffInBST(TreeNode* root) { + return dfs(root, root); + } + +private: + int dfs(TreeNode* root, TreeNode* node) { + if (!node) { + return INT_MAX; + } + int res = dfs1(root, node); + res = min(res, dfs(root, node->left)); + res = min(res, dfs(root, node->right)); + return res; + } + + int dfs1(TreeNode* root, TreeNode* node) { + if (!root) { + return INT_MAX; + } + int res = INT_MAX; + if (root != node) { + res = abs(root->val - node->val); + } + res = min(res, dfs1(root->left, node)); + res = min(res, dfs1(root->right, node)); + return res; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {number} + */ + minDiffInBST(root) { + const dfs = (node) => { + if (!node) { + return Infinity; + } + let res = dfs1(root, node); + res = Math.min(res, dfs(node.left)); + res = Math.min(res, dfs(node.right)); + return res; + }; + + const dfs1 = (root, node) => { + if (!root) { + return Infinity; + } + let res = Infinity; + if (root !== node) { + res = Math.abs(root.val - node.val); + } + res = Math.min(res, dfs1(root.left, node)); + res = Math.min(res, dfs1(root.right, node)); + return res; + }; + + return dfs(root); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n)$ + +--- + +## 2. Inorder Traversal + +::tabs-start + +```python +# 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 +class Solution: + def minDiffInBST(self, root: Optional[TreeNode]) -> int: + arr = [] + + def dfs(node): + if not node: + return + dfs(node.left) + arr.append(node.val) + dfs(node.right) + + dfs(root) + res = arr[1] - arr[0] + for i in range(2, len(arr)): + res = min(res, arr[i] - arr[i - 1]) + return res +``` + +```java +/** + * 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; + * } + * } + */ +public class Solution { + public int minDiffInBST(TreeNode root) { + List arr = new ArrayList<>(); + + dfs(root, arr); + int res = arr.get(1) - arr.get(0); + for (int i = 2; i < arr.size(); i++) { + res = Math.min(res, arr.get(i) - arr.get(i - 1)); + } + return res; + } + + private void dfs(TreeNode node, List arr) { + if (node == null) { + return; + } + dfs(node.left, arr); + arr.add(node.val); + dfs(node.right, arr); + } +} +``` + +```cpp +/** + * 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: + int minDiffInBST(TreeNode* root) { + vector arr; + dfs(root, arr); + + int res = arr[1] - arr[0]; + for (int i = 2; i < arr.size(); i++) { + res = min(res, arr[i] - arr[i - 1]); + } + return res; + } + +private: + void dfs(TreeNode* node, vector& arr) { + if (!node) return; + dfs(node->left, arr); + arr.push_back(node->val); + dfs(node->right, arr); + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {number} + */ + minDiffInBST(root) { + const arr = []; + + const dfs = (node) => { + if (!node) return; + dfs(node.left); + arr.push(node.val); + dfs(node.right); + }; + + dfs(root); + let res = arr[1] - arr[0]; + for (let i = 2; i < arr.length; i++) { + res = Math.min(res, arr[i] - arr[i - 1]); + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Inorder Traversal (Space Optimized) + +::tabs-start + +```python +# 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 +class Solution: + def minDiffInBST(self, root: Optional[TreeNode]) -> int: + prev, res = None, float("inf") + + def dfs(node): + nonlocal prev, res + if not node: + return + + dfs(node.left) + if prev: + res = min(res, node.val - prev.val) + prev = node + dfs(node.right) + + dfs(root) + return res +``` + +```java +/** + * 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; + * } + * } + */ +public class Solution { + private TreeNode prev = null; + private int res = Integer.MAX_VALUE; + + public int minDiffInBST(TreeNode root) { + dfs(root); + return res; + } + + private void dfs(TreeNode node) { + if (node == null) return; + + dfs(node.left); + if (prev != null) { + res = Math.min(res, node.val - prev.val); + } + prev = node; + dfs(node.right); + } +} +``` + +```cpp +/** + * 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: + int minDiffInBST(TreeNode* root) { + TreeNode* prev = nullptr; + int res = INT_MAX; + + dfs(root, prev, res); + return res; + } + +private: + void dfs(TreeNode* node, TreeNode*& prev, int& res) { + if (!node) return; + + dfs(node->left, prev, res); + if (prev) { + res = min(res, node->val - prev->val); + } + prev = node; + dfs(node->right, prev, res); + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {number} + */ + minDiffInBST(root) { + let prev = null; + let res = Infinity; + + const dfs = (node) => { + if (!node) return; + + dfs(node.left); + if (prev !== null) { + res = Math.min(res, node.val - prev.val); + } + prev = node; + dfs(node.right); + }; + + dfs(root); + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ for recursion stack. + +--- + +## 4. Iterative DFS (Inorder Traversal) + +::tabs-start + +```python +# 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 +class Solution: + def minDiffInBST(self, root: Optional[TreeNode]) -> int: + stack, prev, res = [], None, float("inf") + cur = root + + while stack or cur: + while cur: + stack.append(cur) + cur = cur.left + + cur = stack.pop() + if prev: + res = min(res, cur.val - prev.val) + prev = cur + cur = cur.right + + return res +``` + +```java +/** + * 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; + * } + * } + */ +public class Solution { + public int minDiffInBST(TreeNode root) { + Stack stack = new Stack<>(); + TreeNode prev = null; + int res = Integer.MAX_VALUE; + TreeNode cur = root; + + while (!stack.isEmpty() || cur != null) { + while (cur != null) { + stack.push(cur); + cur = cur.left; + } + + cur = stack.pop(); + if (prev != null) { + res = Math.min(res, cur.val - prev.val); + } + prev = cur; + cur = cur.right; + } + + return res; + } +} +``` + +```cpp +/** + * 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: + int minDiffInBST(TreeNode* root) { + stack st; + TreeNode* prev = nullptr; + TreeNode* cur = root; + int res = INT_MAX; + + while (!st.empty() || cur) { + while (cur) { + st.push(cur); + cur = cur->left; + } + + cur = st.top(); + st.pop(); + if (prev) { + res = min(res, cur->val - prev->val); + } + prev = cur; + cur = cur->right; + } + + return res; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {number} + */ + minDiffInBST(root) { + let stack = []; + let prev = null; + let res = Infinity; + let cur = root; + + while (stack.length > 0 || cur !== null) { + while (cur !== null) { + stack.push(cur); + cur = cur.left; + } + + cur = stack.pop(); + if (prev !== null) { + res = Math.min(res, cur.val - prev.val); + } + prev = cur; + cur = cur.right; + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 5. Morris Traversal + +::tabs-start + +```python +# 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 +class Solution: + def minDiffInBST(self, root: Optional[TreeNode]) -> int: + prevVal = res = float("inf") + cur = root + + while cur: + if not cur.left: + if prevVal != float("inf"): + res = min(res, cur.val - prevVal) + prevVal = cur.val + cur = cur.right + else: + prev = cur.left + while prev.right and prev.right != cur: + prev = prev.right + + if not prev.right: + prev.right = cur + cur = cur.left + else: + prev.right = None + if prevVal != float("inf"): + res = min(res, cur.val - prevVal) + prevVal = cur.val + cur = cur.right + + return res +``` + +```java +/** + * 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; + * } + * } + */ +public class Solution { + public int minDiffInBST(TreeNode root) { + int prevVal = Integer.MAX_VALUE, res = Integer.MAX_VALUE; + TreeNode cur = root; + + while (cur != null) { + if (cur.left == null) { + if (prevVal != Integer.MAX_VALUE) { + res = Math.min(res, cur.val - prevVal); + } + prevVal = cur.val; + cur = cur.right; + } else { + TreeNode prev = cur.left; + while (prev.right != null && prev.right != cur) { + prev = prev.right; + } + + if (prev.right == null) { + prev.right = cur; + cur = cur.left; + } else { + prev.right = null; + if (prevVal != Integer.MAX_VALUE) { + res = Math.min(res, cur.val - prevVal); + } + prevVal = cur.val; + cur = cur.right; + } + } + } + + return res; + } +} +``` + +```cpp +/** + * 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: + int minDiffInBST(TreeNode* root) { + int prevVal = INT_MAX, res = INT_MAX; + TreeNode* cur = root; + + while (cur) { + if (!cur->left) { + if (prevVal != INT_MAX) { + res = min(res, cur->val - prevVal); + } + prevVal = cur->val; + cur = cur->right; + } else { + TreeNode* prev = cur->left; + while (prev->right && prev->right != cur) { + prev = prev->right; + } + + if (!prev->right) { + prev->right = cur; + cur = cur->left; + } else { + prev->right = nullptr; + if (prevVal != INT_MAX) { + res = min(res, cur->val - prevVal); + } + prevVal = cur->val; + cur = cur->right; + } + } + } + + return res; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {number} + */ + minDiffInBST(root) { + let prevVal = Infinity, res = Infinity; + let cur = root; + + while (cur !== null) { + if (cur.left === null) { + if (prevVal !== Infinity) { + res = Math.min(res, cur.val - prevVal); + } + prevVal = cur.val; + cur = cur.right; + } else { + let prev = cur.left; + while (prev.right !== null && prev.right !== cur) { + prev = prev.right; + } + + if (prev.right === null) { + prev.right = cur; + cur = cur.left; + } else { + prev.right = null; + if (prevVal !== Infinity) { + res = Math.min(res, cur.val - prevVal); + } + prevVal = cur.val; + cur = cur.right; + } + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. \ No newline at end of file diff --git a/articles/minimum-time-to-collect-all-apples-in-a-tree.md b/articles/minimum-time-to-collect-all-apples-in-a-tree.md new file mode 100644 index 000000000..2ea61fe99 --- /dev/null +++ b/articles/minimum-time-to-collect-all-apples-in-a-tree.md @@ -0,0 +1,312 @@ +## 1. Depth First Search + +::tabs-start + +```python +class Solution: + def minTime(self, n: int, edges: List[List[int]], hasApple: List[bool]) -> int: + adj = {i: [] for i in range(n)} + for par, child in edges: + adj[par].append(child) + adj[child].append(par) + + def dfs(cur, par): + time = 0 + for child in adj[cur]: + if child == par: + continue + childTime = dfs(child, cur) + if childTime > 0 or hasApple[child]: + time += 2 + childTime + return time + + return dfs(0, -1) +``` + +```java +public class Solution { + public int minTime(int n, int[][] edges, List hasApple) { + List[] adj = new ArrayList[n]; + for (int i = 0; i < n; i++) { + adj[i] = new ArrayList<>(); + } + for (int[] edge : edges) { + adj[edge[0]].add(edge[1]); + adj[edge[1]].add(edge[0]); + } + + return dfs(0, -1, adj, hasApple); + } + + private int dfs(int cur, int parent, List[] adj, List hasApple) { + int time = 0; + for (int child : adj[cur]) { + if (child == parent) continue; + int childTime = dfs(child, cur, adj, hasApple); + if (childTime > 0 || hasApple.get(child)) { + time += 2 + childTime; + } + } + return time; + } +} +``` + +```cpp +class Solution { +public: + int minTime(int n, vector>& edges, vector& hasApple) { + vector> adj(n); + for (const auto& edge : edges) { + adj[edge[0]].push_back(edge[1]); + adj[edge[1]].push_back(edge[0]); + } + + return dfs(0, -1, adj, hasApple); + } + +private: + int dfs(int cur, int parent, vector>& adj, vector& hasApple) { + int time = 0; + for (int child : adj[cur]) { + if (child == parent) continue; + int childTime = dfs(child, cur, adj, hasApple); + if (childTime > 0 || hasApple[child]) { + time += 2 + childTime; + } + } + return time; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number[][]} edges + * @param {boolean[]} hasApple + * @return {number} + */ + minTime(n, edges, hasApple) { + const adj = Array.from({ length: n }, () => []); + for (const [parent, child] of edges) { + adj[parent].push(child); + adj[child].push(parent); + } + + const dfs = (cur, parent) => { + let time = 0; + for (const child of adj[cur]) { + if (child === parent) continue; + const childTime = dfs(child, cur); + if (childTime > 0 || hasApple[child]) { + time += 2 + childTime; + } + } + return time; + }; + + return dfs(0, -1); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(V + E)$ +* Space complexity: $O(V + E)$ + +> Where $V$ is the number of vertices and $E$ is the number of edges. + +--- + +## 2. Topological Sort (Kahn's Algorithm) + +::tabs-start + +```python +class Solution: + def minTime(self, n: int, edges: List[List[int]], hasApple: List[bool]) -> int: + adj = defaultdict(list) + indegree = [0] * n + for u, v in edges: + adj[u].append(v) + adj[v].append(u) + indegree[u] += 1 + indegree[v] += 1 + + queue = deque() + for i in range(1, n): + if indegree[i] == 1: + queue.append(i) + indegree[i] = 0 + + time = [0] * n + while queue: + node = queue.popleft() + for nei in adj[node]: + if indegree[nei] <= 0: + continue + + indegree[nei] -= 1 + if hasApple[node] or time[node] > 0: + time[nei] += time[node] + 2 + if indegree[nei] == 1 and nei != 0: + queue.append(nei) + + return time[0] +``` + +```java +public class Solution { + public int minTime(int n, int[][] edges, List hasApple) { + List[] adj = new ArrayList[n]; + for (int i = 0; i < n; i++) { + adj[i] = new ArrayList<>(); + } + int[] indegree = new int[n]; + for (int[] edge : edges) { + adj[edge[0]].add(edge[1]); + adj[edge[1]].add(edge[0]); + indegree[edge[0]]++; + indegree[edge[1]]++; + } + + Queue queue = new LinkedList<>(); + for (int i = 1; i < n; i++) { + if (indegree[i] == 1) { + queue.offer(i); + indegree[i] = 0; + } + } + + int[] time = new int[n]; + while (!queue.isEmpty()) { + int node = queue.poll(); + for (int neighbor : adj[node]) { + if (indegree[neighbor] <= 0) { + continue; + } + + indegree[neighbor]--; + if (hasApple.get(node) || time[node] > 0) { + time[neighbor] += time[node] + 2; + } + if (indegree[neighbor] == 1 && neighbor != 0) { + queue.offer(neighbor); + } + } + } + + return time[0]; + } +} +``` + +```cpp +class Solution { +public: + int minTime(int n, vector>& edges, vector& hasApple) { + vector> adj(n); + vector indegree(n, 0); + + for (const auto& edge : edges) { + adj[edge[0]].push_back(edge[1]); + adj[edge[1]].push_back(edge[0]); + indegree[edge[0]]++; + indegree[edge[1]]++; + } + + queue q; + for (int i = 1; i < n; ++i) { + if (indegree[i] == 1) { + q.push(i); + indegree[i] = 0; + } + } + + vector time(n, 0); + while (!q.empty()) { + int node = q.front(); + q.pop(); + for (int neighbor : adj[node]) { + if (indegree[neighbor] <= 0) { + continue; + } + + indegree[neighbor]--; + if (hasApple[node] || time[node] > 0) { + time[neighbor] += time[node] + 2; + } + if (indegree[neighbor] == 1 && neighbor != 0) { + q.push(neighbor); + } + } + } + + return time[0]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number[][]} edges + * @param {boolean[]} hasApple + * @return {number} + */ + minTime(n, edges, hasApple) { + const adj = Array.from({ length: n }, () => []); + const indegree = Array(n).fill(0); + + for (const [u, v] of edges) { + adj[u].push(v); + adj[v].push(u); + indegree[u]++; + indegree[v]++; + } + + const queue = new Queue(); + for (let i = 1; i < n; i++) { + if (indegree[i] === 1) { + queue.push(i); + indegree[i] = 0; + } + } + + const time = Array(n).fill(0); + while (!queue.isEmpty()) { + const node = queue.pop(); + for (const neighbor of adj[node]) { + if (indegree[neighbor] <= 0) { + continue; + } + + indegree[neighbor]--; + if (hasApple[node] || time[node] > 0) { + time[neighbor] += time[node] + 2; + } + if (indegree[neighbor] === 1 && neighbor !== 0) { + queue.push(neighbor); + } + } + } + + return time[0]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(V + E)$ +* Space complexity: $O(V + E)$ + +> Where $V$ is the number of vertices and $E$ is the number of edges. \ No newline at end of file diff --git a/articles/path-sum.md b/articles/path-sum.md new file mode 100644 index 000000000..ce01351cf --- /dev/null +++ b/articles/path-sum.md @@ -0,0 +1,581 @@ +## 1. Depth First Search - I + +::tabs-start + +```python +# 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 +class Solution: + def hasPathSum(self, root: Optional[TreeNode], targetSum: int) -> bool: + def dfs(node, curSum): + if not node: + return False + + curSum += node.val + if not node.left and not node.right: + return curSum == targetSum + + return dfs(node.left, curSum) or dfs(node.right, curSum) + + return dfs(root, 0) +``` + +```java +/** + * 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; + * } + * } + */ +public class Solution { + public boolean hasPathSum(TreeNode root, int targetSum) { + return dfs(root, 0, targetSum); + } + + private boolean dfs(TreeNode node, int curSum, int targetSum) { + if (node == null) return false; + + curSum += node.val; + if (node.left == null && node.right == null) { + return curSum == targetSum; + } + + return dfs(node.left, curSum, targetSum) || dfs(node.right, curSum, targetSum); + } +} +``` + +```cpp +/** + * 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: + bool hasPathSum(TreeNode* root, int targetSum) { + return dfs(root, 0, targetSum); + } + +private: + bool dfs(TreeNode* node, int curSum, int targetSum) { + if (node == nullptr) return false; + + curSum += node->val; + if (node->left == nullptr && node->right == nullptr) { + return curSum == targetSum; + } + + return dfs(node->left, curSum, targetSum) || dfs(node->right, curSum, targetSum); + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @param {number} targetSum + * @return {boolean} + */ + hasPathSum(root, targetSum) { + const dfs = (node, curSum) => { + if (!node) return false; + + curSum += node.val; + if (!node.left && !node.right) { + return curSum === targetSum; + } + + return dfs(node.left, curSum) || dfs(node.right, curSum); + }; + + return dfs(root, 0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ for recursion stack. + +--- + +## 2. Depth First Search - II + +::tabs-start + +```python +# 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 +class Solution: + def hasPathSum(self, root: Optional[TreeNode], targetSum: int) -> bool: + if not root: + return False + + targetSum -= root.val + return (self.hasPathSum(root.left, targetSum) or + self.hasPathSum(root.right, targetSum) or + (not targetSum and not root.left and not root.right)) +``` + +```java +/** + * 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; + * } + * } + */ +public class Solution { + public boolean hasPathSum(TreeNode root, int targetSum) { + if (root == null) return false; + targetSum -= root.val; + return hasPathSum(root.left, targetSum) || + hasPathSum(root.right, targetSum) || + (targetSum == 0 && root.left == null && root.right == null); + } +} +``` + +```cpp +/** + * 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: + bool hasPathSum(TreeNode* root, int targetSum) { + if (!root) return false; + targetSum -= root->val; + return hasPathSum(root->left, targetSum) || + hasPathSum(root->right, targetSum) || + (targetSum == 0 && !root->left && !root->right); + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @param {number} targetSum + * @return {boolean} + */ + hasPathSum(root, targetSum) { + if (!root) return false; + targetSum -= root.val; + return this.hasPathSum(root.left, targetSum) || + this.hasPathSum(root.right, targetSum) || + (targetSum === 0 && !root.left && !root.right); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ for recursion stack. + +--- + +## 3. Iterative DFS + +::tabs-start + +```python +# 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 +class Solution: + def hasPathSum(self, root: Optional[TreeNode], targetSum: int) -> bool: + if not root: + return False + + stack = [(root, targetSum - root.val)] + while stack: + node, curr_sum = stack.pop() + if not node.left and not node.right and curr_sum == 0: + return True + if node.right: + stack.append((node.right, curr_sum - node.right.val)) + if node.left: + stack.append((node.left, curr_sum - node.left.val)) + return False +``` + +```java +/** + * 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; + * } + * } + */ +public class Solution { + public boolean hasPathSum(TreeNode root, int targetSum) { + if (root == null) return false; + + Stack stack = new Stack<>(); + Stack sumStack = new Stack<>(); + stack.push(root); + sumStack.push(targetSum - root.val); + + while (!stack.isEmpty()) { + TreeNode node = stack.pop(); + int currSum = sumStack.pop(); + + if (node.left == null && node.right == null && currSum == 0) { + return true; + } + + if (node.right != null) { + stack.push(node.right); + sumStack.push(currSum - node.right.val); + } + + if (node.left != null) { + stack.push(node.left); + sumStack.push(currSum - node.left.val); + } + } + + return false; + } +} +``` + +```cpp +/** + * 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: + bool hasPathSum(TreeNode* root, int targetSum) { + if (!root) return false; + + stack> s; + s.push({root, targetSum - root->val}); + + while (!s.empty()) { + auto [node, currSum] = s.top(); + s.pop(); + + if (!node->left && !node->right && currSum == 0) { + return true; + } + + if (node->right) { + s.push({node->right, currSum - node->right->val}); + } + + if (node->left) { + s.push({node->left, currSum - node->left->val}); + } + } + + return false; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @param {number} targetSum + * @return {boolean} + */ + hasPathSum(root, targetSum) { + if (!root) return false; + + const stack = [[root, targetSum - root.val]]; + while (stack.length) { + const [node, currSum] = stack.pop(); + + if (!node.left && !node.right && currSum === 0) { + return true; + } + + if (node.right) { + stack.push([node.right, currSum - node.right.val]); + } + + if (node.left) { + stack.push([node.left, currSum - node.left.val]); + } + } + + return false; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 4. Breadth First Search + +::tabs-start + +```python +# 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 +class Solution: + def hasPathSum(self, root: Optional[TreeNode], targetSum: int) -> bool: + if not root: + return False + + queue = deque([(root, targetSum - root.val)]) + while queue: + node, curr_sum = queue.popleft() + if not node.left and not node.right and curr_sum == 0: + return True + if node.left: + queue.append((node.left, curr_sum - node.left.val)) + if node.right: + queue.append((node.right, curr_sum - node.right.val)) + return False +``` + +```java +/** + * 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; + * } + * } + */ +public class Solution { + public boolean hasPathSum(TreeNode root, int targetSum) { + if (root == null) return false; + + Queue nodeQueue = new LinkedList<>(); + Queue sumQueue = new LinkedList<>(); + nodeQueue.add(root); + sumQueue.add(targetSum - root.val); + + while (!nodeQueue.isEmpty()) { + TreeNode node = nodeQueue.poll(); + int currSum = sumQueue.poll(); + + if (node.left == null && node.right == null && currSum == 0) { + return true; + } + + if (node.left != null) { + nodeQueue.add(node.left); + sumQueue.add(currSum - node.left.val); + } + + if (node.right != null) { + nodeQueue.add(node.right); + sumQueue.add(currSum - node.right.val); + } + } + + return false; + } +} +``` + +```cpp +/** + * 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: + bool hasPathSum(TreeNode* root, int targetSum) { + if (!root) return false; + + queue> q; + q.push({root, targetSum - root->val}); + + while (!q.empty()) { + auto [node, currSum] = q.front(); + q.pop(); + + if (!node->left && !node->right && currSum == 0) { + return true; + } + + if (node->left) { + q.push({node->left, currSum - node->left->val}); + } + + if (node->right) { + q.push({node->right, currSum - node->right->val}); + } + } + + return false; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @param {number} targetSum + * @return {boolean} + */ + hasPathSum(root, targetSum) { + if (!root) return false; + + const queue = new Queue([[root, targetSum - root.val]]); + while (!queue.isEmpty()) { + const [node, currSum] = queue.pop(); + + if (!node.left && !node.right && currSum === 0) { + return true; + } + + if (node.left) { + queue.push([node.left, currSum - node.left.val]); + } + + if (node.right) { + queue.push([node.right, currSum - node.right.val]); + } + } + + return false; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/range-sum-of-bst.md b/articles/range-sum-of-bst.md new file mode 100644 index 000000000..cd0873dec --- /dev/null +++ b/articles/range-sum-of-bst.md @@ -0,0 +1,408 @@ +## 1. Depth First Search + +::tabs-start + +```python +# 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 +class Solution: + def rangeSumBST(self, root: Optional[TreeNode], low: int, high: int) -> int: + if not root: + return 0 + + res = root.val if low <= root.val <= high else 0 + res += self.rangeSumBST(root.left, low, high) + res += self.rangeSumBST(root.right, low, high) + return res +``` + +```java +/** + * 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; + * } + * } + */ +public class Solution { + public int rangeSumBST(TreeNode root, int low, int high) { + if (root == null) return 0; + + int res = (low <= root.val && root.val <= high) ? root.val : 0; + res += rangeSumBST(root.left, low, high); + res += rangeSumBST(root.right, low, high); + return res; + } +} +``` + +```cpp +/** + * 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: + int rangeSumBST(TreeNode* root, int low, int high) { + if (!root) return 0; + + int res = (low <= root->val && root->val <= high) ? root->val : 0; + res += rangeSumBST(root->left, low, high); + res += rangeSumBST(root->right, low, high); + return res; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @param {number} low + * @param {number} high + * @return {number} + */ + rangeSumBST(root, low, high) { + if (!root) return 0; + + let res = (low <= root.val && root.val <= high) ? root.val : 0; + res += this.rangeSumBST(root.left, low, high); + res += this.rangeSumBST(root.right, low, high); + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ for recursion stack. + +--- + +## 2. Depth First Search (Optimal) + +::tabs-start + +```python +# 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 +class Solution: + def rangeSumBST(self, root: Optional[TreeNode], low: int, high: int) -> int: + if not root: + return 0 + + if root.val > high: + return self.rangeSumBST(root.left, low, high) + if root.val < low: + return self.rangeSumBST(root.right, low, high) + + return ( + root.val + + self.rangeSumBST(root.left, low, high) + + self.rangeSumBST(root.right, low, high) + ) +``` + +```java +/** + * 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; + * } + * } + */ +public class Solution { + public int rangeSumBST(TreeNode root, int low, int high) { + if (root == null) return 0; + + if (root.val > high) { + return rangeSumBST(root.left, low, high); + } + if (root.val < low) { + return rangeSumBST(root.right, low, high); + } + + return root.val + rangeSumBST(root.left, low, high) + + rangeSumBST(root.right, low, high); + } +} +``` + +```cpp +/** + * 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: + int rangeSumBST(TreeNode* root, int low, int high) { + if (!root) return 0; + + if (root->val > high) { + return rangeSumBST(root->left, low, high); + } + if (root->val < low) { + return rangeSumBST(root->right, low, high); + } + + return root->val + rangeSumBST(root->left, low, high) + + rangeSumBST(root->right, low, high); + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @param {number} low + * @param {number} high + * @return {number} + */ + rangeSumBST(root, low, high) { + if (!root) return 0; + + if (root.val > high) { + return rangeSumBST(root.left, low, high); + } + if (root.val < low) { + return rangeSumBST(root.right, low, high); + } + + return root.val + this.rangeSumBST(root.left, low, high) + + this.rangeSumBST(root.right, low, high); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ for recursion stack. + +--- + +## 3. Iterative DFS + +::tabs-start + +```python +# 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 +class Solution: + def rangeSumBST(self, root: Optional[TreeNode], low: int, high: int) -> int: + stack = [root] + res = 0 + + while stack: + node = stack.pop() + if not node: + continue + + if low <= node.val <= high: + res += node.val + if node.val > low: + stack.append(node.left) + if node.val < high: + stack.append(node.right) + + return res +``` + +```java +/** + * 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; + * } + * } + */ +public class Solution { + public int rangeSumBST(TreeNode root, int low, int high) { + int res = 0; + Stack stack = new Stack<>(); + stack.push(root); + + while (!stack.isEmpty()) { + TreeNode node = stack.pop(); + if (node == null) continue; + + if (low <= node.val && node.val <= high) { + res += node.val; + } + if (node.val > low) { + stack.push(node.left); + } + if (node.val < high) { + stack.push(node.right); + } + } + + return res; + } +} +``` + +```cpp +/** + * 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: + int rangeSumBST(TreeNode* root, int low, int high) { + int res = 0; + stack stack; + stack.push(root); + + while (!stack.empty()) { + TreeNode* node = stack.top(); + stack.pop(); + if (!node) continue; + + if (low <= node->val && node->val <= high) { + res += node->val; + } + if (node->val > low) { + stack.push(node->left); + } + if (node->val < high) { + stack.push(node->right); + } + } + + return res; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @param {number} low + * @param {number} high + * @return {number} + */ + rangeSumBST(root, low, high) { + let res = 0; + let stack = [root]; + + while (stack.length > 0) { + let node = stack.pop(); + if (!node) continue; + + if (low <= node.val && node.val <= high) { + res += node.val; + } + if (node.val > low) { + stack.push(node.left); + } + if (node.val < high) { + stack.push(node.right); + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/same-binary-tree.md b/articles/same-binary-tree.md index 6cbfc12aa..aa0f1a59a 100644 --- a/articles/same-binary-tree.md +++ b/articles/same-binary-tree.md @@ -191,7 +191,7 @@ class Solution { ### Time & Space Complexity * Time complexity: $O(n)$ -* Space complexity: $O(h)$ +* Space complexity: $O(n)$ * Best Case ([balanced tree](https://www.geeksforgeeks.org/balanced-binary-tree/)): $O(log(n))$ * Worst Case ([degenerate tree](https://www.geeksforgeeks.org/introduction-to-degenerate-binary-tree/)): $O(n)$ @@ -199,7 +199,261 @@ class Solution { --- -## 2. Breadth First Search +## 2. Iterative DFS + +::tabs-start + +```python +# 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 + +class Solution: + def isSameTree(self, p: Optional[TreeNode], q: Optional[TreeNode]) -> bool: + stack = [(p, q)] + + while stack: + node1, node2 = stack.pop() + + if not node1 and not node2: + continue + if not node1 or not node2 or node1.val != node2.val: + return False + + stack.append((node1.right, node2.right)) + stack.append((node1.left, node2.left)) + + return True +``` + +```java +/** + * 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; + * } + * } + */ + +public class Solution { + public boolean isSameTree(TreeNode p, TreeNode q) { + Stack stack = new Stack<>(); + stack.push(new TreeNode[]{p, q}); + + while (!stack.isEmpty()) { + TreeNode[] nodes = stack.pop(); + TreeNode node1 = nodes[0], node2 = nodes[1]; + + if (node1 == null && node2 == null) continue; + if (node1 == null || node2 == null || node1.val != node2.val) { + return false; + } + stack.push(new TreeNode[]{node1.right, node2.right}); + stack.push(new TreeNode[]{node1.left, node2.left}); + } + + return true; + } +} +``` + +```cpp +/** + * 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: + bool isSameTree(TreeNode* p, TreeNode* q) { + stack> stk; + stk.push({p, q}); + + while (!stk.empty()) { + auto [node1, node2] = stk.top(); + stk.pop(); + + if (!node1 && !node2) continue; + if (!node1 || !node2 || node1->val != node2->val) return false; + + stk.push({node1->right, node2->right}); + stk.push({node1->left, node2->left}); + } + + return true; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ + +class Solution { + /** + * @param {TreeNode} p + * @param {TreeNode} q + * @return {boolean} + */ + isSameTree(p, q) { + const stack = [[p, q]]; + + while (stack.length) { + const [node1, node2] = stack.pop(); + + if (!node1 && !node2) continue; + if (!node1 || !node2 || node1.val !== node2.val) { + return false; + } + stack.push([node1.right, node2.right]); + stack.push([node1.left, node2.left]); + } + + return true; + } +} +``` + +```csharp +/** + * Definition for a binary tree node. + * public class TreeNode { + * public int val; + * public TreeNode left; + * public TreeNode right; + * public TreeNode(int val=0, TreeNode left=null, TreeNode right=null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ + +public class Solution { + public bool IsSameTree(TreeNode p, TreeNode q) { + var stack = new Stack<(TreeNode, TreeNode)>(); + stack.Push((p, q)); + + while (stack.Count > 0) { + var (node1, node2) = stack.Pop(); + + if (node1 == null && node2 == null) continue; + if (node1 == null || node2 == null || node1.val != node2.val) { + return false; + } + stack.Push((node1.right, node2.right)); + stack.Push((node1.left, node2.left)); + } + + return true; + } +} +``` + +```go +/** + * Definition for a binary tree node. + * type TreeNode struct { + * Val int + * Left *TreeNode + * Right *TreeNode + * } + */ +func isSameTree(p *TreeNode, q *TreeNode) bool { + type Pair struct { + first, second *TreeNode + } + + stack := []Pair{{p, q}} + + for len(stack) > 0 { + lastIdx := len(stack) - 1 + node1, node2 := stack[lastIdx].first, stack[lastIdx].second + stack = stack[:lastIdx] + + if node1 == nil && node2 == nil { + continue + } + if node1 == nil || node2 == nil || node1.Val != node2.Val { + return false + } + + stack = append(stack, Pair{node1.Right, node2.Right}) + stack = append(stack, Pair{node1.Left, node2.Left}) + } + + return true +} +``` + +```kotlin +/** + * Example: + * var ti = TreeNode(5) + * var v = ti.`val` + * Definition for a binary tree node. + * class TreeNode(var `val`: Int) { + * var left: TreeNode? = null + * var right: TreeNode? = null + * } + */ +class Solution { + fun isSameTree(p: TreeNode?, q: TreeNode?): Boolean { + val stack = ArrayDeque>() + stack.addLast(Pair(p, q)) + + while (stack.isNotEmpty()) { + val (node1, node2) = stack.removeLast() + + if (node1 == null && node2 == null) continue + if (node1 == null || node2 == null || node1.`val` != node2.`val`) { + return false + } + stack.addLast(Pair(node1.right, node2.right)) + stack.addLast(Pair(node1.left, node2.left)) + } + + return true + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Breadth First Search ::tabs-start @@ -217,20 +471,21 @@ class Solution: q2 = deque([q]) while q1 and q2: - nodeP = q1.popleft() - nodeQ = q2.popleft() + for _ in range(len(q1)): + nodeP = q1.popleft() + nodeQ = q2.popleft() - if nodeP is None and nodeQ is None: - continue - if nodeP is None or nodeQ is None or nodeP.val != nodeQ.val: - return False + if nodeP is None and nodeQ is None: + continue + if nodeP is None or nodeQ is None or nodeP.val != nodeQ.val: + return False - q1.append(nodeP.left) - q1.append(nodeP.right) - q2.append(nodeQ.left) - q2.append(nodeQ.right) + q1.append(nodeP.left) + q1.append(nodeP.right) + q2.append(nodeQ.left) + q2.append(nodeQ.right) - return True + return True ``` ```java @@ -258,17 +513,19 @@ public class Solution { q2.add(q); while (!q1.isEmpty() && !q2.isEmpty()) { - TreeNode nodeP = q1.poll(); - TreeNode nodeQ = q2.poll(); - - if (nodeP == null && nodeQ == null) continue; - if (nodeP == null || nodeQ == null || nodeP.val != nodeQ.val) - return false; - - q1.add(nodeP.left); - q1.add(nodeP.right); - q2.add(nodeQ.left); - q2.add(nodeQ.right); + for (int i = q1.size(); i > 0; i--) { + TreeNode nodeP = q1.poll(); + TreeNode nodeQ = q2.poll(); + + if (nodeP == null && nodeQ == null) continue; + if (nodeP == null || nodeQ == null || nodeP.val != nodeQ.val) + return false; + + q1.add(nodeP.left); + q1.add(nodeP.right); + q2.add(nodeQ.left); + q2.add(nodeQ.right); + } } return true; @@ -298,17 +555,19 @@ public: q2.push(q); while (!q1.empty() && !q2.empty()) { - TreeNode* nodeP = q1.front(); q1.pop(); - TreeNode* nodeQ = q2.front(); q2.pop(); - - if (!nodeP && !nodeQ) continue; - if (!nodeP || !nodeQ || nodeP->val != nodeQ->val) - return false; - - q1.push(nodeP->left); - q1.push(nodeP->right); - q2.push(nodeQ->left); - q2.push(nodeQ->right); + for (int i = q1.size(); i > 0; i--) { + TreeNode* nodeP = q1.front(); q1.pop(); + TreeNode* nodeQ = q2.front(); q2.pop(); + + if (!nodeP && !nodeQ) continue; + if (!nodeP || !nodeQ || nodeP->val != nodeQ->val) + return false; + + q1.push(nodeP->left); + q1.push(nodeP->right); + q2.push(nodeQ->left); + q2.push(nodeQ->right); + } } return true; @@ -341,17 +600,20 @@ class Solution { q2.push(q); while (!q1.isEmpty() && !q2.isEmpty()) { - let nodeP = q1.pop(); - let nodeQ = q2.pop(); - - if (nodeP === null && nodeQ === null) continue; - if (nodeP === null || nodeQ === null || nodeP.val !== nodeQ.val) - return false; - - q1.push(nodeP.left); - q1.push(nodeP.right); - q2.push(nodeQ.left); - q2.push(nodeQ.right); + for (let i = q1.size(); i > 0; i--) { + let nodeP = q1.pop(); + let nodeQ = q2.pop(); + + if (nodeP === null && nodeQ === null) continue; + if (nodeP === null || nodeQ === null || nodeP.val !== nodeQ.val) { + return false; + } + + q1.push(nodeP.left); + q1.push(nodeP.right); + q2.push(nodeQ.left); + q2.push(nodeQ.right); + } } return true; @@ -380,17 +642,20 @@ public class Solution { var q2 = new Queue(new[] { q }); while (q1.Count > 0 && q2.Count > 0) { - var nodeP = q1.Dequeue(); - var nodeQ = q2.Dequeue(); - - if (nodeP == null && nodeQ == null) continue; - if (nodeP == null || nodeQ == null || nodeP.val != nodeQ.val) - return false; - - q1.Enqueue(nodeP.left); - q1.Enqueue(nodeP.right); - q2.Enqueue(nodeQ.left); - q2.Enqueue(nodeQ.right); + for (int i = q1.Count; i > 0; i--) { + var nodeP = q1.Dequeue(); + var nodeQ = q2.Dequeue(); + + if (nodeP == null && nodeQ == null) continue; + if (nodeP == null || nodeQ == null || nodeP.val != nodeQ.val) { + return false; + } + + q1.Enqueue(nodeP.left); + q1.Enqueue(nodeP.right); + q2.Enqueue(nodeQ.left); + q2.Enqueue(nodeQ.right); + } } return true; @@ -412,20 +677,22 @@ func isSameTree(p *TreeNode, q *TreeNode) bool { queue2 := []*TreeNode{q} for len(queue1) > 0 && len(queue2) > 0 { - nodeP := queue1[0] - nodeQ := queue2[0] - queue1 = queue1[1:] - queue2 = queue2[1:] + for i := len(queue1); i > 0; i-- { + nodeP := queue1[0] + nodeQ := queue2[0] + queue1 = queue1[1:] + queue2 = queue2[1:] - if nodeP == nil && nodeQ == nil { - continue - } - if nodeP == nil || nodeQ == nil || nodeP.Val != nodeQ.Val { - return false - } + if nodeP == nil && nodeQ == nil { + continue + } + if nodeP == nil || nodeQ == nil || nodeP.Val != nodeQ.Val { + return false + } - queue1 = append(queue1, nodeP.Left, nodeP.Right) - queue2 = append(queue2, nodeQ.Left, nodeQ.Right) + queue1 = append(queue1, nodeP.Left, nodeP.Right) + queue2 = append(queue2, nodeQ.Left, nodeQ.Right) + } } return len(queue1) == 0 && len(queue2) == 0 @@ -452,20 +719,22 @@ class Solution { q2.add(q) while (q1.isNotEmpty() && q2.isNotEmpty()) { - val nodeP = q1.removeFirst() - val nodeQ = q2.removeFirst() - - if (nodeP == null && nodeQ == null) { - continue + for (i in q1.size downTo 1) { + val nodeP = q1.removeFirst() + val nodeQ = q2.removeFirst() + + if (nodeP == null && nodeQ == null) { + continue + } + if (nodeP == null || nodeQ == null || nodeP.`val` != nodeQ.`val`) { + return false + } + + q1.add(nodeP.left) + q1.add(nodeP.right) + q2.add(nodeQ.left) + q2.add(nodeQ.right) } - if (nodeP == null || nodeQ == null || nodeP.`val` != nodeQ.`val`) { - return false - } - - q1.add(nodeP.left) - q1.add(nodeP.right) - q2.add(nodeQ.left) - q2.add(nodeQ.right) } return true diff --git a/articles/sliding-window-median.md b/articles/sliding-window-median.md index e328fe000..b49ca4071 100644 --- a/articles/sliding-window-median.md +++ b/articles/sliding-window-median.md @@ -342,8 +342,12 @@ class Solution: ```java public class Solution { public double[] medianSlidingWindow(int[] nums, int k) { - TreeSet small = new TreeSet<>((a, b) -> nums[a] != nums[b] ? Integer.compare(nums[a], nums[b]) : Integer.compare(a, b)); - TreeSet large = new TreeSet<>((a, b) -> nums[a] != nums[b] ? Integer.compare(nums[a], nums[b]) : Integer.compare(a, b)); + TreeSet small = new TreeSet<>((a, b) -> + nums[a] != nums[b] ? Integer.compare(nums[a], nums[b]) : Integer.compare(a, b) + ); + TreeSet large = new TreeSet<>((a, b) -> + nums[a] != nums[b] ? Integer.compare(nums[a], nums[b]) : Integer.compare(a, b) + ); double[] res = new double[nums.length - k + 1]; for (int i = 0; i < nums.length; i++) { if (small.isEmpty() || nums[i] <= nums[small.last()]) small.add(i); @@ -355,7 +359,8 @@ public class Solution { while (small.size() > large.size() + 1) large.add(small.pollLast()); while (large.size() > small.size()) small.add(large.pollFirst()); if (i >= k - 1) { - res[i - k + 1] = k % 2 == 1 ? nums[small.last()] : (nums[small.last()] + 0L + nums[large.first()]) / 2.0; + res[i - k + 1] = k % 2 == 1 ? nums[small.last()] : + (nums[small.last()] + 0L + nums[large.first()]) / 2.0; } } return res; @@ -385,7 +390,10 @@ public: large.erase(large.begin()); } if (i >= k - 1) { - res.push_back(k % 2 == 1 ? *small.rbegin() : (*small.rbegin() + 0LL + *large.begin()) / 2.0); + res.push_back( + k % 2 == 1 ? *small.rbegin() : + ((*small.rbegin() + 0LL + *large.begin()) / 2.0) + ); } } return res; diff --git a/articles/symmetric-tree.md b/articles/symmetric-tree.md new file mode 100644 index 000000000..615d4dd34 --- /dev/null +++ b/articles/symmetric-tree.md @@ -0,0 +1,447 @@ +## 1. Depth First Search + +::tabs-start + +```python +# 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 +class Solution: + def isSymmetric(self, root: Optional[TreeNode]) -> bool: + def dfs(left, right): + if not left and not right: + return True + if not left or not right: + return False + return ( + left.val == right.val and + dfs(left.left, right.right) and + dfs(left.right, right.left) + ) + return dfs(root.left, root.right) +``` + +```java +/** + * 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; + * } + * } + */ +public class Solution { + public boolean isSymmetric(TreeNode root) { + return dfs(root.left, root.right); + } + + private boolean dfs(TreeNode left, TreeNode right) { + if (left == null && right == null) { + return true; + } + if (left == null || right == null) { + return false; + } + return left.val == right.val && + dfs(left.left, right.right) && + dfs(left.right, right.left); + } +} +``` + +```cpp +/** + * 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: + bool isSymmetric(TreeNode* root) { + return dfs(root->left, root->right); + } + +private: + bool dfs(TreeNode* left, TreeNode* right) { + if (!left && !right) { + return true; + } + if (!left || !right) { + return false; + } + return (left->val == right->val) && + dfs(left->left, right->right) && + dfs(left->right, right->left); + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {boolean} + */ + isSymmetric(root) { + const dfs = (left, right) => { + if (!left && !right) { + return true; + } + if (!left || !right) { + return false; + } + return ( + left.val === right.val && + dfs(left.left, right.right) && + dfs(left.right, right.left) + ); + }; + return dfs(root.left, root.right); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ for recursion stack. + +--- + +## 2. Iterative DFS + +::tabs-start + +```python +# 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 +class Solution: + def isSymmetric(self, root: Optional[TreeNode]) -> bool: + if not root: + return True + + stack = [(root.left, root.right)] + while stack: + left, right = stack.pop() + if not left and not right: + continue + if not left or not right or left.val != right.val: + return False + stack.append((left.left, right.right)) + stack.append((left.right, right.left)) + return True +``` + +```java +/** + * 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; + * } + * } + */ +public class Solution { + public boolean isSymmetric(TreeNode root) { + if (root == null) return true; + + Stack stack = new Stack<>(); + stack.push(new TreeNode[]{root.left, root.right}); + + while (!stack.isEmpty()) { + TreeNode[] nodes = stack.pop(); + TreeNode left = nodes[0], right = nodes[1]; + + if (left == null && right == null) continue; + if (left == null || right == null || left.val != right.val) { + return false; + } + + stack.push(new TreeNode[]{left.left, right.right}); + stack.push(new TreeNode[]{left.right, right.left}); + } + + return true; + } +} +``` + +```cpp +/** + * 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: + bool isSymmetric(TreeNode* root) { + if (!root) return true; + + std::stack> stack; + stack.push({root->left, root->right}); + + while (!stack.empty()) { + auto [left, right] = stack.top(); + stack.pop(); + + if (!left && !right) continue; + if (!left || !right || left->val != right->val) { + return false; + } + stack.push({left->left, right->right}); + stack.push({left->right, right->left}); + } + + return true; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {boolean} + */ + isSymmetric(root) { + if (!root) return true; + + const stack = [[root.left, root.right]]; + + while (stack.length > 0) { + const [left, right] = stack.pop(); + + if (!left && !right) continue; + if (!left || !right || left.val !== right.val) { + return false; + } + + stack.push([left.left, right.right]); + stack.push([left.right, right.left]); + } + + return true; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Breadth First Search + +::tabs-start + +```python +# 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 +class Solution: + def isSymmetric(self, root: Optional[TreeNode]) -> bool: + if not root: + return True + + queue = deque([(root.left, root.right)]) + while queue: + for _ in range(len(queue)): + left, right = queue.popleft() + if not left and not right: + continue + if not left or not right or left.val != right.val: + return False + queue.append((left.left, right.right)) + queue.append((left.right, right.left)) + + return True +``` + +```java +/** + * 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; + * } + * } + */ +public class Solution { + public boolean isSymmetric(TreeNode root) { + if (root == null) return true; + + Queue queue = new LinkedList<>(); + queue.add(new TreeNode[]{root.left, root.right}); + + while (!queue.isEmpty()) { + for (int i = queue.size(); i > 0; i--) { + TreeNode[] nodes = queue.poll(); + TreeNode left = nodes[0], right = nodes[1]; + + if (left == null && right == null) continue; + if (left == null || right == null || left.val != right.val) { + return false; + } + queue.add(new TreeNode[]{left.left, right.right}); + queue.add(new TreeNode[]{left.right, right.left}); + } + } + + return true; + } +} +``` + +```cpp +/** + * 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: + bool isSymmetric(TreeNode* root) { + if (!root) return true; + + queue> queue; + queue.push({root->left, root->right}); + + while (!queue.empty()) { + for (int i = queue.size(); i > 0; i--) { + auto [left, right] = queue.front(); + queue.pop(); + + if (!left && !right) continue; + if (!left || !right || left->val != right->val) { + return false; + } + queue.push({left->left, right->right}); + queue.push({left->right, right->left}); + } + } + + return true; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {boolean} + */ + isSymmetric(root) { + if (!root) return true; + + const queue = new Queue([[root.left, root.right]]); + + while (!queue.isEmpty()) { + for (let i = queue.size(); i > 0; i--) { + const [left, right] = queue.pop(); + + if (!left && !right) continue; + if (!left || !right || left.val !== right.val) { + return false; + } + queue.push([left.left, right.right]); + queue.push([left.right, right.left]); + } + } + + return true; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/time-needed-to-inform-all-employees.md b/articles/time-needed-to-inform-all-employees.md new file mode 100644 index 000000000..7a80a7c02 --- /dev/null +++ b/articles/time-needed-to-inform-all-employees.md @@ -0,0 +1,479 @@ +## 1. Depth First Search + +::tabs-start + +```python +class Solution: + def numOfMinutes(self, n: int, headID: int, manager: List[int], informTime: List[int]) -> int: + adj = [[] for _ in range(n)] + for i in range(n): + if i != headID: + adj[manager[i]].append(i) + + def dfs(node): + res = 0 + for child in adj[node]: + res = max(res, informTime[node] + dfs(child)) + return res + + return dfs(headID) +``` + +```java +public class Solution { + public int numOfMinutes(int n, int headID, int[] manager, int[] informTime) { + List[] adj = new ArrayList[n]; + for (int i = 0; i < n; i++) { + adj[i] = new ArrayList<>(); + } + for (int i = 0; i < n; i++) { + if (i != headID) { + adj[manager[i]].add(i); + } + } + + return dfs(headID, adj, informTime); + } + + private int dfs(int node, List[] adj, int[] informTime) { + int res = 0; + for (int child : adj[node]) { + res = Math.max(res, informTime[node] + dfs(child, adj, informTime)); + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int numOfMinutes(int n, int headID, vector& manager, vector& informTime) { + vector> adj(n); + for (int i = 0; i < n; i++) { + if (i != headID) { + adj[manager[i]].push_back(i); + } + } + return dfs(headID, adj, informTime); + } + +private: + int dfs(int node, vector>& adj, vector& informTime) { + int res = 0; + for (int child : adj[node]) { + res = max(res, informTime[node] + dfs(child, adj, informTime)); + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number} headID + * @param {number[]} manager + * @param {number[]} informTime + * @return {number} + */ + numOfMinutes(n, headID, manager, informTime) { + const adj = Array.from({ length: n }, () => []); + for (let i = 0; i < n; i++) { + if (i !== headID) { + adj[manager[i]].push(i); + } + } + + const dfs = (node) => { + let res = 0; + for (const child of adj[node]) { + res = Math.max(res, informTime[node] + dfs(child)); + } + return res; + }; + + return dfs(headID); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Breadth First Search + +::tabs-start + +```python +class Solution: + def numOfMinutes(self, n: int, headID: int, manager: List[int], informTime: List[int]) -> int: + adj = defaultdict(list) + for i in range(n): + adj[manager[i]].append(i) + + q = deque([(headID, 0)]) # (id, time) + res = 0 + + while q: + node, time = q.popleft() + res = max(res, time) + for emp in adj[node]: + q.append((emp, time + informTime[node])) + + return res +``` + +```java +public class Solution { + public int numOfMinutes(int n, int headID, int[] manager, int[] informTime) { + Map> adj = new HashMap<>(); + for (int i = 0; i < n; i++) { + adj.computeIfAbsent(manager[i], k -> new ArrayList<>()).add(i); + } + + Queue queue = new LinkedList<>(); + queue.add(new int[]{headID, 0}); + int res = 0; + + while (!queue.isEmpty()) { + int[] curr = queue.poll(); + int id = curr[0], time = curr[1]; + res = Math.max(res, time); + for (int emp : adj.getOrDefault(id, new ArrayList<>())) { + queue.add(new int[]{emp, time + informTime[id]}); + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int numOfMinutes(int n, int headID, vector& manager, vector& informTime) { + unordered_map> adj; + for (int i = 0; i < n; ++i) { + adj[manager[i]].push_back(i); + } + + queue> q; // {id, time} + q.push({headID, 0}); + int res = 0; + + while (!q.empty()) { + auto [id, time] = q.front(); + q.pop(); + res = max(res, time); + for (int emp : adj[id]) { + q.push({emp, time + informTime[id]}); + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number} headID + * @param {number[]} manager + * @param {number[]} informTime + * @return {number} + */ + numOfMinutes(n, headID, manager, informTime) { + const adj = new Map(); + for (let i = 0; i < n; i++) { + if (!adj.has(manager[i])) adj.set(manager[i], []); + adj.get(manager[i]).push(i); + } + + const queue = new Queue([[headID, 0]]); // [id, time] + let res = 0; + + while (!queue.isEmpty()) { + const [id, time] = queue.pop(); + res = Math.max(res, time); + if (adj.has(id)) { + for (const emp of adj.get(id)) { + queue.push([emp, time + informTime[id]]); + } + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Topological Sort (Kahn's Algorithm) + +::tabs-start + +```python +class Solution: + def numOfMinutes(self, n: int, headID: int, manager: List[int], informTime: List[int]) -> int: + indegree = [0] * n + time = [0] * n + + for i in range(n): + if manager[i] != -1: + indegree[manager[i]] += 1 + + queue = deque() + for i in range(n): + if indegree[i] == 0: + queue.append(i) + + while queue: + node = queue.popleft() + time[node] += informTime[node] + if manager[node] != -1: + time[manager[node]] = max(time[manager[node]], time[node]) + indegree[manager[node]] -= 1 + if indegree[manager[node]] == 0: + queue.append(manager[node]) + + return time[headID] +``` + +```java +public class Solution { + public int numOfMinutes(int n, int headID, int[] manager, int[] informTime) { + int[] indegree = new int[n]; + int[] time = new int[n]; + + for (int i = 0; i < n; i++) { + if (manager[i] != -1) { + indegree[manager[i]]++; + } + } + + Queue queue = new LinkedList<>(); + for (int i = 0; i < n; i++) { + if (indegree[i] == 0) { + queue.add(i); + } + } + + while (!queue.isEmpty()) { + int node = queue.poll(); + time[node] += informTime[node]; + if (manager[node] != -1) { + time[manager[node]] = Math.max(time[manager[node]], time[node]); + indegree[manager[node]]--; + if (indegree[manager[node]] == 0) { + queue.add(manager[node]); + } + } + } + + return time[headID]; + } +} +``` + +```cpp +class Solution { +public: + int numOfMinutes(int n, int headID, vector& manager, vector& informTime) { + vector indegree(n, 0); + vector time(n, 0); + + for (int i = 0; i < n; ++i) { + if (manager[i] != -1) { + indegree[manager[i]]++; + } + } + + queue queue; + for (int i = 0; i < n; ++i) { + if (indegree[i] == 0) { + queue.push(i); + } + } + + while (!queue.empty()) { + int node = queue.front(); + queue.pop(); + time[node] += informTime[node]; + if (manager[node] != -1) { + time[manager[node]] = max(time[manager[node]], time[node]); + indegree[manager[node]]--; + if (indegree[manager[node]] == 0) { + queue.push(manager[node]); + } + } + } + + return time[headID]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number} headID + * @param {number[]} manager + * @param {number[]} informTime + * @return {number} + */ + numOfMinutes(n, headID, manager, informTime) { + const indegree = Array(n).fill(0); + const time = Array(n).fill(0); + + for (let i = 0; i < n; i++) { + if (manager[i] !== -1) { + indegree[manager[i]]++; + } + } + + const queue = new Queue(); + for (let i = 0; i < n; i++) { + if (indegree[i] === 0) { + queue.push(i); + } + } + + while (!queue.isEmpty()) { + const node = queue.pop(); + time[node] += informTime[node]; + if (manager[node] !== -1) { + time[manager[node]] = Math.max(time[manager[node]], time[node]); + indegree[manager[node]]--; + if (indegree[manager[node]] === 0) { + queue.push(manager[node]); + } + } + } + + return time[headID]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 4. Depth First Search (Optimal) + +::tabs-start + +```python +class Solution: + def numOfMinutes(self, n: int, headID: int, manager: List[int], informTime: List[int]) -> int: + def dfs(node): + if manager[node] != -1: + informTime[node] += dfs(manager[node]) + manager[node] = -1 + return informTime[node] + + res = 0 + for node in range(n): + res = max(res, dfs(node)) + return res +``` + +```java +public class Solution { + public int numOfMinutes(int n, int headID, int[] manager, int[] informTime) { + int res = 0; + for (int node = 0; node < n; node++) { + res = Math.max(res, dfs(node, manager, informTime)); + } + return res; + } + + private int dfs(int node, int[] manager, int[] informTime) { + if (manager[node] != -1) { + informTime[node] += dfs(manager[node], manager, informTime); + manager[node] = -1; + } + return informTime[node]; + } +} +``` + +```cpp +class Solution { +public: + int numOfMinutes(int n, int headID, vector& manager, vector& informTime) { + function dfs = [&](int node) { + if (manager[node] != -1) { + informTime[node] += dfs(manager[node]); + manager[node] = -1; + } + return informTime[node]; + }; + + int res = 0; + for (int node = 0; node < n; ++node) { + res = max(res, dfs(node)); + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number} headID + * @param {number[]} manager + * @param {number[]} informTime + * @return {number} + */ + numOfMinutes(n, headID, manager, informTime) { + const dfs = (node) => { + if (manager[node] !== -1) { + informTime[node] += dfs(manager[node]); + manager[node] = -1; + } + return informTime[node]; + }; + + let res = 0; + for (let node = 0; node < n; node++) { + res = Math.max(res, dfs(node)); + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ for recursion stack. \ No newline at end of file diff --git a/articles/unique-binary-search-trees.md b/articles/unique-binary-search-trees.md new file mode 100644 index 000000000..499868203 --- /dev/null +++ b/articles/unique-binary-search-trees.md @@ -0,0 +1,411 @@ +## 1. Recursion + +::tabs-start + +```python +class Solution: + def numTrees(self, n: int) -> int: + if n <= 1: + return 1 + + res = 0 + for i in range(1, n + 1): + res += self.numTrees(i - 1) * self.numTrees(n - i) + return res +``` + +```java +public class Solution { + public int numTrees(int n) { + if (n <= 1) { + return 1; + } + + int res = 0; + for (int i = 1; i <= n; i++) { + res += numTrees(i - 1) * numTrees(n - i); + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int numTrees(int n) { + if (n <= 1) { + return 1; + } + + int res = 0; + for (int i = 1; i <= n; i++) { + res += numTrees(i - 1) * numTrees(n - i); + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {number} + */ + numTrees(n) { + if (n <= 1) { + return 1; + } + + let res = 0; + for (let i = 1; i <= n; i++) { + res += this.numTrees(i - 1) * this.numTrees(n - i); + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(3 ^ n)$ +* Space complexity: $O(n)$ for recursion stack. + +--- + +## 2. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + + def __init__(self): + self.dp = {} + + def numTrees(self, n: int) -> int: + if n <= 1: + return 1 + if n in self.dp: + return self.dp[n] + + res = 0 + for i in range(1, n + 1): + res += self.numTrees(i - 1) * self.numTrees(n - i) + + self.dp[n] = res + return res +``` + +```java +public class Solution { + private Map dp = new HashMap<>(); + + public int numTrees(int n) { + if (n <= 1) { + return 1; + } + if (dp.containsKey(n)) { + return dp.get(n); + } + + int res = 0; + for (int i = 1; i <= n; i++) { + res += numTrees(i - 1) * numTrees(n - i); + } + + dp.put(n, res); + return res; + } +} +``` + +```cpp +class Solution { +private: + unordered_map dp; + +public: + int numTrees(int n) { + if (n <= 1) { + return 1; + } + if (dp.find(n) != dp.end()) { + return dp[n]; + } + + int res = 0; + for (int i = 1; i <= n; i++) { + res += numTrees(i - 1) * numTrees(n - i); + } + + dp[n] = res; + return res; + } +}; +``` + +```javascript +class Solution { + constructor() { + this.dp = new Map(); + } + + /** + * @param {number} n + * @return {number} + */ + numTrees(n) { + if (n <= 1) { + return 1; + } + if (this.dp.has(n)) { + return this.dp.get(n); + } + + let res = 0; + for (let i = 1; i <= n; i++) { + res += this.numTrees(i - 1) * this.numTrees(n - i); + } + + this.dp.set(n, res); + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n)$ + +--- + +## 3. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def numTrees(self, n: int) -> int: + numTree = [1] * (n + 1) + + for nodes in range(2, n + 1): + total = 0 + for root in range(1, nodes + 1): + left = root - 1 + right = nodes - root + total += numTree[left] * numTree[right] + numTree[nodes] = total + + return numTree[n] +``` + +```java +public class Solution { + public int numTrees(int n) { + int[] numTree = new int[n + 1]; + numTree[0] = 1; + numTree[1] = 1; + + for (int nodes = 2; nodes <= n; nodes++) { + int total = 0; + for (int root = 1; root <= nodes; root++) { + int left = root - 1; + int right = nodes - root; + total += numTree[left] * numTree[right]; + } + numTree[nodes] = total; + } + + return numTree[n]; + } +} +``` + +```cpp +class Solution { +public: + int numTrees(int n) { + vector numTree(n + 1, 1); + + for (int nodes = 2; nodes <= n; ++nodes) { + int total = 0; + for (int root = 1; root <= nodes; ++root) { + int left = root - 1; + int right = nodes - root; + total += numTree[left] * numTree[right]; + } + numTree[nodes] = total; + } + + return numTree[n]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {number} + */ + numTrees(n) { + const numTree = Array(n + 1).fill(1); + + for (let nodes = 2; nodes <= n; nodes++) { + let total = 0; + for (let root = 1; root <= nodes; root++) { + let left = root - 1; + let right = nodes - root; + total += numTree[left] * numTree[right]; + } + numTree[nodes] = total; + } + + return numTree[n]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n)$ + +--- + +## 4. Catalan Numbers - I + +::tabs-start + +```python +class Solution: + def numTrees(self, n: int) -> int: + res = 1 + for i in range(1, n): + res *= (n + i + 1) + res //= i + return res // n +``` + +```java +public class Solution { + public int numTrees(int n) { + long res = 1; + for (int i = 1; i < n; i++) { + res *= (n + i + 1); + res /= i; + } + return (int) (res / n); + } +} +``` + +```cpp +class Solution { +public: + int numTrees(int n) { + long long res = 1; + for (int i = 1; i < n; i++) { + res *= (n + i + 1); + res /= i; + } + return res / n; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {number} + */ + numTrees(n) { + let res = 1n; + for (let i = 1n; i < BigInt(n); i++) { + res *= BigInt(n) + i + 1n; + res /= i; + } + return Number(res / BigInt(n)); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. + +--- + +## 5. Catalan Numbers - II + +::tabs-start + +```python +class Solution: + def numTrees(self, n: int) -> int: + res = 1 + for i in range(n): + res *= (4 * i + 2) / (i + 2) + return int(res) +``` + +```java +public class Solution { + public int numTrees(int n) { + long res = 1; + for (int i = 0; i < n; i++) { + res *= (4 * i + 2) / (i + 2.0); + } + return (int) res; + } +} +``` + +```cpp +class Solution { +public: + int numTrees(int n) { + long long res = 1; + for (int i = 0; i < n; i++) { + res *= (4 * i + 2) / (i + 2.0); + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {number} + */ + numTrees(n) { + let res = 1; + for (let i = 0; i < n; i++) { + res *= (4 * i + 2) / (i + 2); + } + return Math.floor(res); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. \ No newline at end of file diff --git a/hints/design-twitter-feed.md b/hints/design-twitter-feed.md index 4dcbd38e4..8fc7ab4f8 100644 --- a/hints/design-twitter-feed.md +++ b/hints/design-twitter-feed.md @@ -2,7 +2,7 @@
Recommended Time & Space Complexity

- You should aim for a solution with O(n) time for each getNewsFeed() function call, O(1) time for the remaining methods, and O((N * m) + (N * M) + n) space, where n is the number of followeeIds associated with the userId, m is the maximum number of tweets by any user, N is the total number of userIds, and M is the maximum number of followees for any user. + You should aim for a solution with O(nlogn) time for each getNewsFeed() function call, O(1) time for the remaining methods, and O((N * m) + (N * M) + n) space, where n is the number of followeeIds associated with the userId, m is the maximum number of tweets by any user, N is the total number of userIds, and M is the maximum number of followees for any user.

From e4fc2bc8d4add4ae1dbfbc6f8aa649cfad134086 Mon Sep 17 00:00:00 2001 From: Sri Hari <94112314+Srihari2222@users.noreply.github.com> Date: Mon, 27 Jan 2025 06:21:42 +0530 Subject: [PATCH 30/45] Sri Hari: Batch-5/Neetcode-ALL/Added-articles (#3829) * Batch-5/Neetcode-ALL/Added-articles * Batch-5/Neetcode-ALL/Added-articles * Batch-5/Neetcode-ALL/Added-articles * Batch-5/Neetcode-ALL/Added-articles --- ...ments-not-equal-to-average-of-neighbors.md | 338 ++++++++++ articles/bag-of-tokens.md | 109 ++++ articles/binary-subarrays-with-sum.md | 381 +++++++++++ ...re-max-element-appears-at-least-k-times.md | 589 ++++++++++++++++++ articles/find-all-duplicates-in-an-array.md | 440 +++++++++++++ ...find-polygon-with-the-largest-perimeter.md | 295 +++++++++ .../get-equal-substrings-within-budget.md | 274 ++++++++ articles/k-th-symbol-in-grammar.md | 413 ++++++++++++ ...ngest-subarray-with-at-most-k-frequency.md | 278 +++++++++ articles/make-the-string-great.md | 334 ++++++++++ ...h-of-string-after-deleting-similar-ends.md | 86 +++ ...-of-operations-to-make-array-continuous.md | 449 +++++++++++++ .../minimum-operations-to-reduce-x-to-zero.md | 577 +++++++++++++++++ ...inimum-remove-to-make-valid-parentheses.md | 522 ++++++++++++++++ .../minimum-time-to-make-rope-colorful.md | 270 ++++++++ ...es-that-satisfy-the-given-sum-condition.md | 514 +++++++++++++++ articles/rearrange-array-elements-by-sign.md | 310 +++++++++ .../remove-duplicates-from-sorted-array-ii.md | 421 +++++++++++++ articles/sequential-digits.md | 579 +++++++++++++++++ articles/subarray-product-less-than-k.md | 313 ++++++++++ .../subarrays-with-k-different-integers.md | 546 ++++++++++++++++ 21 files changed, 8038 insertions(+) create mode 100644 articles/array-with-elements-not-equal-to-average-of-neighbors.md create mode 100644 articles/bag-of-tokens.md create mode 100644 articles/binary-subarrays-with-sum.md create mode 100644 articles/count-subarrays-where-max-element-appears-at-least-k-times.md create mode 100644 articles/find-all-duplicates-in-an-array.md create mode 100644 articles/find-polygon-with-the-largest-perimeter.md create mode 100644 articles/get-equal-substrings-within-budget.md create mode 100644 articles/k-th-symbol-in-grammar.md create mode 100644 articles/length-of-longest-subarray-with-at-most-k-frequency.md create mode 100644 articles/make-the-string-great.md create mode 100644 articles/minimum-length-of-string-after-deleting-similar-ends.md create mode 100644 articles/minimum-number-of-operations-to-make-array-continuous.md create mode 100644 articles/minimum-operations-to-reduce-x-to-zero.md create mode 100644 articles/minimum-remove-to-make-valid-parentheses.md create mode 100644 articles/minimum-time-to-make-rope-colorful.md create mode 100644 articles/number-of-subsequences-that-satisfy-the-given-sum-condition.md create mode 100644 articles/rearrange-array-elements-by-sign.md create mode 100644 articles/remove-duplicates-from-sorted-array-ii.md create mode 100644 articles/sequential-digits.md create mode 100644 articles/subarray-product-less-than-k.md create mode 100644 articles/subarrays-with-k-different-integers.md diff --git a/articles/array-with-elements-not-equal-to-average-of-neighbors.md b/articles/array-with-elements-not-equal-to-average-of-neighbors.md new file mode 100644 index 000000000..4e4a1daa1 --- /dev/null +++ b/articles/array-with-elements-not-equal-to-average-of-neighbors.md @@ -0,0 +1,338 @@ +## 1. Greedy + +::tabs-start + +```python +class Solution: + def rearrangeArray(self, nums: List[int]) -> List[int]: + nums.sort() + res = [] + l, r = 0, len(nums) - 1 + while len(res) != len(nums): + res.append(nums[l]) + l += 1 + if l <= r: + res.append(nums[r]) + r -= 1 + return res +``` + +```java +public class Solution { + public int[] rearrangeArray(int[] nums) { + Arrays.sort(nums); + int[] res = new int[nums.length]; + int l = 0, r = nums.length - 1; + int idx = 0; + + while (idx != nums.length) { + res[idx++] = nums[l++]; + if (l <= r) { + res[idx++] = nums[r--]; + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector rearrangeArray(vector& nums) { + sort(nums.begin(), nums.end()); + vector res; + int l = 0, r = nums.size() - 1; + + while (res.size() != nums.size()) { + res.push_back(nums[l++]); + if (l <= r) { + res.push_back(nums[r--]); + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number[]} + */ + rearrangeArray(nums) { + nums.sort((a, b) => a - b); + const res = []; + let l = 0, r = nums.length - 1; + + while (res.length !== nums.length) { + res.push(nums[l++]); + if (l <= r) { + res.push(nums[r--]); + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n\log n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Greedy (Space Optimized) + +::tabs-start + +```python +class Solution: + def rearrangeArray(self, nums: List[int]) -> List[int]: + nums.sort() + for i in range(1, len(nums), 2): + nums[i], nums[i - 1] = nums[i - 1], nums[i] + return nums +``` + +```java +public class Solution { + public int[] rearrangeArray(int[] nums) { + Arrays.sort(nums); + for (int i = 1; i < nums.length; i += 2) { + int temp = nums[i]; + nums[i] = nums[i - 1]; + nums[i - 1] = temp; + } + return nums; + } +} +``` + +```cpp +class Solution { +public: + vector rearrangeArray(vector& nums) { + sort(nums.begin(), nums.end()); + for (int i = 1; i < nums.size(); i += 2) { + swap(nums[i], nums[i - 1]); + } + return nums; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number[]} + */ + rearrangeArray(nums) { + nums.sort((a, b) => a - b); + for (let i = 1; i < nums.length; i += 2) { + [nums[i], nums[i - 1]] = [nums[i - 1], nums[i]]; + } + return nums; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(1)$ or $O(n)$ depending on the sorting algorithm. + +--- + +## 3. Greedy (Optimal) - I + +::tabs-start + +```python +class Solution: + def rearrangeArray(self, nums: List[int]) -> List[int]: + n = len(nums) + + for i in range(1, n - 1): + if 2 * nums[i] == (nums[i - 1] + nums[i + 1]): + nums[i], nums[i + 1] = nums[i + 1], nums[i] + + for i in range(n - 2, 0, -1): + if 2 * nums[i] == (nums[i - 1] + nums[i + 1]): + nums[i], nums[i - 1] = nums[i - 1], nums[i] + + return nums +``` + +```java +public class Solution { + public int[] rearrangeArray(int[] nums) { + int n = nums.length; + + for (int i = 1; i < n - 1; i++) { + if (2 * nums[i] == (nums[i - 1] + nums[i + 1])) { + int temp = nums[i]; + nums[i] = nums[i + 1]; + nums[i + 1] = temp; + } + } + + for (int i = n - 2; i > 0; i--) { + if (2 * nums[i] == (nums[i - 1] + nums[i + 1])) { + int temp = nums[i]; + nums[i] = nums[i - 1]; + nums[i - 1] = temp; + } + } + + return nums; + } +} +``` + +```cpp +class Solution { +public: + vector rearrangeArray(vector& nums) { + int n = nums.size(); + + for (int i = 1; i < n - 1; i++) { + if (2 * nums[i] == (nums[i - 1] + nums[i + 1])) { + swap(nums[i], nums[i + 1]); + } + } + + for (int i = n - 2; i > 0; i--) { + if (2 * nums[i] == (nums[i - 1] + nums[i + 1])) { + swap(nums[i], nums[i - 1]); + } + } + + return nums; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number[]} + */ + rearrangeArray(nums) { + const n = nums.length; + + for (let i = 1; i < n - 1; i++) { + if (2 * nums[i] === nums[i - 1] + nums[i + 1]) { + [nums[i], nums[i + 1]] = [nums[i + 1], nums[i]]; + } + } + + for (let i = n - 2; i > 0; i--) { + if (2 * nums[i] === nums[i - 1] + nums[i + 1]) { + [nums[i], nums[i - 1]] = [nums[i - 1], nums[i]]; + } + } + + return nums; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. + +--- + +## 4. Greedy Optimal - II + +::tabs-start + +```python +class Solution: + def rearrangeArray(self, nums: List[int]) -> List[int]: + increase = nums[0] < nums[1] + for i in range(1, len(nums) - 1): + if ((increase and nums[i] < nums[i + 1]) or + (not increase and nums[i] > nums[i + 1]) + ): + nums[i], nums[i + 1] = nums[i + 1], nums[i] + increase = not increase + return nums +``` + +```java +public class Solution { + public int[] rearrangeArray(int[] nums) { + boolean increase = nums[0] < nums[1]; + for (int i = 1; i < nums.length - 1; i++) { + if ((increase && nums[i] < nums[i + 1]) || + (!increase && nums[i] > nums[i + 1])) { + int temp = nums[i]; + nums[i] = nums[i + 1]; + nums[i + 1] = temp; + } + increase = !increase; + } + return nums; + } +} +``` + +```cpp +class Solution { +public: + vector rearrangeArray(vector& nums) { + bool increase = nums[0] < nums[1]; + for (int i = 1; i < nums.size() - 1; i++) { + if ((increase && nums[i] < nums[i + 1]) || + (!increase && nums[i] > nums[i + 1])) { + swap(nums[i], nums[i + 1]); + } + increase = !increase; + } + return nums; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number[]} + */ + rearrangeArray(nums) { + let increase = nums[0] < nums[1]; + for (let i = 1; i < nums.length - 1; i++) { + if ((increase && nums[i] < nums[i + 1]) || + (!increase && nums[i] > nums[i + 1])) { + [nums[i], nums[i + 1]] = [nums[i + 1], nums[i]]; + } + increase = !increase; + } + return nums; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. \ No newline at end of file diff --git a/articles/bag-of-tokens.md b/articles/bag-of-tokens.md new file mode 100644 index 000000000..e6db737b8 --- /dev/null +++ b/articles/bag-of-tokens.md @@ -0,0 +1,109 @@ +## 1. Greedy + Two Pointers + +::tabs-start + +```python +class Solution: + def bagOfTokensScore(self, tokens: List[int], power: int) -> int: + res = score = 0 + tokens.sort() + l, r = 0, len(tokens) - 1 + while l <= r: + if power >= tokens[l]: + power -= tokens[l] + l += 1 + score += 1 + res = max(res, score) + elif score > 0: + power += tokens[r] + r -= 1 + score -= 1 + else: + break + return res +``` + +```java +public class Solution { + public int bagOfTokensScore(int[] tokens, int power) { + Arrays.sort(tokens); + int res = 0, score = 0, l = 0, r = tokens.length - 1; + + while (l <= r) { + if (power >= tokens[l]) { + power -= tokens[l++]; + score++; + res = Math.max(res, score); + } else if (score > 0) { + power += tokens[r--]; + score--; + } else { + break; + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int bagOfTokensScore(vector& tokens, int power) { + sort(tokens.begin(), tokens.end()); + int res = 0, score = 0, l = 0, r = tokens.size() - 1; + + while (l <= r) { + if (power >= tokens[l]) { + power -= tokens[l++]; + score++; + res = max(res, score); + } else if (score > 0) { + power += tokens[r--]; + score--; + } else { + break; + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} tokens + * @param {number} power + * @return {number} + */ + bagOfTokensScore(tokens, power) { + tokens.sort((a, b) => a - b); + let res = 0, score = 0, l = 0, r = tokens.length - 1; + + while (l <= r) { + if (power >= tokens[l]) { + power -= tokens[l++]; + score++; + res = Math.max(res, score); + } else if (score > 0) { + power += tokens[r--]; + score--; + } else { + break; + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(1)$ or $O(n)$ depending on the sorting algorithm. \ No newline at end of file diff --git a/articles/binary-subarrays-with-sum.md b/articles/binary-subarrays-with-sum.md new file mode 100644 index 000000000..65a07ca78 --- /dev/null +++ b/articles/binary-subarrays-with-sum.md @@ -0,0 +1,381 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def numSubarraysWithSum(self, nums: List[int], goal: int) -> int: + n, res = len(nums), 0 + + for i in range(n): + curSum = 0 + for j in range(i, n): + curSum += nums[j] + if curSum == goal: + res += 1 + + return res +``` + +```java +public class Solution { + public int numSubarraysWithSum(int[] nums, int goal) { + int n = nums.length, res = 0; + + for (int i = 0; i < n; i++) { + int curSum = 0; + for (int j = i; j < n; j++) { + curSum += nums[j]; + if (curSum == goal) { + res++; + } + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int numSubarraysWithSum(vector& nums, int goal) { + int n = nums.size(), res = 0; + + for (int i = 0; i < n; i++) { + int curSum = 0; + for (int j = i; j < n; j++) { + curSum += nums[j]; + if (curSum == goal) { + res++; + } + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} goal + * @return {number} + */ + numSubarraysWithSum(nums, goal) { + const n = nums.length; + let res = 0; + + for (let i = 0; i < n; i++) { + let curSum = 0; + for (let j = i; j < n; j++) { + curSum += nums[j]; + if (curSum === goal) { + res++; + } + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(1)$ + +--- + +## 2. Prefix Sum + Hash Map + +::tabs-start + +```python +class Solution: + def numSubarraysWithSum(self, nums: List[int], goal: int) -> int: + prefixSum = 0 + count = { 0 : 1 } # prefixSum -> count + res = 0 + + for num in nums: + prefixSum += num + res += count.get(prefixSum - goal, 0) + count[prefixSum] = count.get(prefixSum, 0) + 1 + + return res +``` + +```java +public class Solution { + public int numSubarraysWithSum(int[] nums, int goal) { + int prefixSum = 0, res = 0; + HashMap count = new HashMap<>(); + count.put(0, 1); + + for (int num : nums) { + prefixSum += num; + res += count.getOrDefault(prefixSum - goal, 0); + count.put(prefixSum, count.getOrDefault(prefixSum, 0) + 1); + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int numSubarraysWithSum(vector& nums, int goal) { + int prefixSum = 0, res = 0; + unordered_map count; + count[0] = 1; + + for (int& num : nums) { + prefixSum += num; + res += count[prefixSum - goal]; + count[prefixSum]++; + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} goal + * @return {number} + */ + numSubarraysWithSum(nums, goal) { + let prefixSum = 0, res = 0; + const count = new Map(); + count.set(0, 1); + + for (const num of nums) { + prefixSum += num; + res += count.get(prefixSum - goal) || 0; + count.set(prefixSum, (count.get(prefixSum) || 0) + 1); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Prefix Sum + Array + +::tabs-start + +```python +class Solution: + def numSubarraysWithSum(self, nums: List[int], goal: int) -> int: + n = len(nums) + count = [0] * (n + 1) + count[0] = 1 + prefixSum, res = 0, 0 + + for num in nums: + prefixSum += num + if prefixSum >= goal: + res += count[prefixSum - goal] + count[prefixSum] += 1 + + return res +``` + +```java +public class Solution { + public int numSubarraysWithSum(int[] nums, int goal) { + int n = nums.length, prefixSum = 0, res = 0; + int[] count = new int[n + 1]; + count[0] = 1; + + for (int num : nums) { + prefixSum += num; + if (prefixSum >= goal) { + res += count[prefixSum - goal]; + } + count[prefixSum]++; + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int numSubarraysWithSum(vector& nums, int goal) { + int n = nums.size(), prefixSum = 0, res = 0; + vector count(n + 1, 0); + count[0] = 1; + + for (int num : nums) { + prefixSum += num; + if (prefixSum >= goal) { + res += count[prefixSum - goal]; + } + count[prefixSum]++; + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} goal + * @return {number} + */ + numSubarraysWithSum(nums, goal) { + const n = nums.length; + const count = Array(n + 1).fill(0); + count[0] = 1; + let prefixSum = 0, res = 0; + + for (const num of nums) { + prefixSum += num; + if (prefixSum >= goal) { + res += count[prefixSum - goal]; + } + count[prefixSum]++; + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 4. Sliding Window + +::tabs-start + +```python +class Solution: + def numSubarraysWithSum(self, nums: List[int], goal: int) -> int: + def helper(x): + if x < 0: + return 0 + res = l = cur = 0 + for r in range(len(nums)): + cur += nums[r] + while cur > x: + cur -= nums[l] + l += 1 + res += (r - l + 1) + return res + + return helper(goal) - helper(goal - 1) +``` + +```java +public class Solution { + public int numSubarraysWithSum(int[] nums, int goal) { + return helper(nums, goal) - helper(nums, goal - 1); + } + + private int helper(int[] nums, int x) { + if (x < 0) return 0; + int res = 0, l = 0, cur = 0; + for (int r = 0; r < nums.length; r++) { + cur += nums[r]; + while (cur > x) { + cur -= nums[l]; + l++; + } + res += (r - l + 1); + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int numSubarraysWithSum(vector& nums, int goal) { + return helper(nums, goal) - helper(nums, goal - 1); + } + +private: + int helper(vector& nums, int x) { + if (x < 0) return 0; + int res = 0, l = 0, cur = 0; + for (int r = 0; r < nums.size(); r++) { + cur += nums[r]; + while (cur > x) { + cur -= nums[l]; + l++; + } + res += (r - l + 1); + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} goal + * @return {number} + */ + numSubarraysWithSum(nums, goal) { + const helper = (x) => { + if (x < 0) return 0; + let res = 0, l = 0, cur = 0; + for (let r = 0; r < nums.length; r++) { + cur += nums[r]; + while (cur > x) { + cur -= nums[l]; + l++; + } + res += (r - l + 1); + } + return res; + }; + + return helper(goal) - helper(goal - 1); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/count-subarrays-where-max-element-appears-at-least-k-times.md b/articles/count-subarrays-where-max-element-appears-at-least-k-times.md new file mode 100644 index 000000000..c4d2a5914 --- /dev/null +++ b/articles/count-subarrays-where-max-element-appears-at-least-k-times.md @@ -0,0 +1,589 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def countSubarrays(self, nums: List[int], k: int) -> int: + n, res = len(nums), 0 + maxi = max(nums) + + for i in range(n): + cnt = 0 + for j in range(i, n): + if nums[j] == maxi: + cnt += 1 + + if cnt >= k: + res += 1 + + return res +``` + +```java +public class Solution { + public long countSubarrays(int[] nums, int k) { + int n = nums.length; + long res = 0; + int maxi = Integer.MIN_VALUE; + + for (int num : nums) { + maxi = Math.max(maxi, num); + } + + for (int i = 0; i < n; i++) { + int cnt = 0; + for (int j = i; j < n; j++) { + if (nums[j] == maxi) { + cnt++; + } + + if (cnt >= k) { + res++; + } + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + long long countSubarrays(vector& nums, int k) { + int n = nums.size(); + long long res = 0; + int maxi = *max_element(nums.begin(), nums.end()); + + for (int i = 0; i < n; i++) { + int cnt = 0; + for (int j = i; j < n; j++) { + if (nums[j] == maxi) { + cnt++; + } + + if (cnt >= k) { + res++; + } + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {number} + */ + countSubarrays(nums, k) { + let n = nums.length, res = 0; + let maxi = Math.max(...nums); + + for (let i = 0; i < n; i++) { + let cnt = 0; + for (let j = i; j < n; j++) { + if (nums[j] === maxi) { + cnt++; + } + + if (cnt >= k) { + res++; + } + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(1)$ + +--- + +## 2. Variable Size Sliding Window + +::tabs-start + +```python +class Solution: + def countSubarrays(self, nums: List[int], k: int) -> int: + max_n, max_cnt = max(nums), 0 + l = 0 + res = 0 + + for r in range(len(nums)): + if nums[r] == max_n: + max_cnt += 1 + + while max_cnt > k or (l <= r and max_cnt == k and nums[l] != max_n): + if nums[l] == max_n: + max_cnt -= 1 + l += 1 + + if max_cnt == k: + res += l + 1 + + return res +``` + +```java +public class Solution { + public long countSubarrays(int[] nums, int k) { + int maxN = Integer.MIN_VALUE, maxCnt = 0, l = 0; + long res = 0; + for (int num : nums) { + maxN = Math.max(maxN, num); + } + + for (int r = 0; r < nums.length; r++) { + if (nums[r] == maxN) { + maxCnt++; + } + + while (maxCnt > k || (l <= r && maxCnt == k && nums[l] != maxN)) { + if (nums[l] == maxN) { + maxCnt--; + } + l++; + } + + if (maxCnt == k) { + res += l + 1; + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + long long countSubarrays(vector& nums, int k) { + int maxN = *max_element(nums.begin(), nums.end()); + int maxCnt = 0, l = 0; + long long res = 0; + + for (int r = 0; r < nums.size(); r++) { + if (nums[r] == maxN) { + maxCnt++; + } + + while (maxCnt > k || (l <= r && maxCnt == k && nums[l] != maxN)) { + if (nums[l] == maxN) { + maxCnt--; + } + l++; + } + + if (maxCnt == k) { + res += l + 1; + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {number} + */ + countSubarrays(nums, k) { + let maxN = Math.max(...nums); + let maxCnt = 0, l = 0, res = 0; + + for (let r = 0; r < nums.length; r++) { + if (nums[r] === maxN) { + maxCnt++; + } + + while (maxCnt > k || (l <= r && maxCnt === k && nums[l] !== maxN)) { + if (nums[l] === maxN) { + maxCnt--; + } + l++; + } + + if (maxCnt === k) { + res += l + 1; + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ + +--- + +## 3. Variable Size Sliding Window (Optimal) + +::tabs-start + +```python +class Solution: + def countSubarrays(self, nums: List[int], k: int) -> int: + max_n, max_cnt = max(nums), 0 + l = res = 0 + + for r in range(len(nums)): + if nums[r] == max_n: + max_cnt += 1 + while max_cnt == k: + if nums[l] == max_n: + max_cnt -= 1 + l += 1 + res += l + + return res +``` + +```java +public class Solution { + public long countSubarrays(int[] nums, int k) { + int max_n = Integer.MIN_VALUE, max_cnt = 0, l = 0; + long res = 0; + for (int num : nums) { + max_n = Math.max(max_n, num); + } + + for (int r = 0; r < nums.length; r++) { + if (nums[r] == max_n) { + max_cnt++; + } + while (max_cnt == k) { + if (nums[l] == max_n) { + max_cnt--; + } + l++; + } + res += l; + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + long long countSubarrays(vector& nums, int k) { + int max_n = *max_element(nums.begin(), nums.end()); + int max_cnt = 0, l = 0; + long long res = 0; + + for (int r = 0; r < nums.size(); r++) { + if (nums[r] == max_n) { + max_cnt++; + } + while (max_cnt == k) { + if (nums[l] == max_n) { + max_cnt--; + } + l++; + } + res += l; + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {number} + */ + countSubarrays(nums, k) { + let max_n = Math.max(...nums), max_cnt = 0, l = 0, res = 0; + + for (let r = 0; r < nums.length; r++) { + if (nums[r] === max_n) { + max_cnt++; + } + while (max_cnt === k) { + if (nums[l] === max_n) { + max_cnt--; + } + l++; + } + res += l; + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ + +--- + +## 4. Fixed Size Sliding Window + Math + +::tabs-start + +```python +class Solution: + def countSubarrays(self, nums: List[int], k: int) -> int: + n = len(nums) + max_n = max(nums) + max_indexes = [-1] + for i, num in enumerate(nums): + if num == max_n: + max_indexes.append(i) + + res = 0 + for i in range(1, len(max_indexes) - k + 1): + cur = (max_indexes[i] - max_indexes[i - 1]) + cur *= (n - max_indexes[i + k - 1]) + res += cur + + return res +``` + +```java +public class Solution { + public long countSubarrays(int[] nums, int k) { + int n = nums.length; + int max_n = Integer.MIN_VALUE; + for (int num : nums) { + max_n = Math.max(max_n, num); + } + + List max_indexes = new ArrayList<>(); + max_indexes.add(-1); + for (int i = 0; i < n; i++) { + if (nums[i] == max_n) { + max_indexes.add(i); + } + } + + long res = 0; + for (int i = 1; i <= max_indexes.size() - k; i++) { + long cur = (max_indexes.get(i) - max_indexes.get(i - 1)); + cur *= (n - max_indexes.get(i + k - 1)); + res += cur; + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + long long countSubarrays(vector& nums, int k) { + int n = nums.size(); + int max_n = *max_element(nums.begin(), nums.end()); + vector max_indexes = {-1}; + + for (int i = 0; i < n; i++) { + if (nums[i] == max_n) { + max_indexes.push_back(i); + } + } + + long long res = 0; + for (int i = 1; i <= int(max_indexes.size()) - k; i++) { + long long cur = (max_indexes[i] - max_indexes[i - 1]); + cur *= (n - max_indexes[i + k - 1]); + res += cur; + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {number} + */ + countSubarrays(nums, k) { + const n = nums.length; + const max_n = Math.max(...nums); + const max_indexes = [-1]; + + for (let i = 0; i < n; i++) { + if (nums[i] === max_n) { + max_indexes.push(i); + } + } + + let res = 0; + for (let i = 1; i <= max_indexes.length - k; i++) { + res += (max_indexes[i] - max_indexes[i - 1]) * (n - max_indexes[i + k - 1]); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 5. Fixed Size Sliding Window (Optimal) + +::tabs-start + +```python +class Solution: + def countSubarrays(self, nums: List[int], k: int) -> int: + max_n = max(nums) + max_indexes = deque() + res = 0 + + for i, num in enumerate(nums): + if num == max_n: + max_indexes.append(i) + + if len(max_indexes) > k: + max_indexes.popleft() + + if len(max_indexes) == k: + res += max_indexes[0] + 1 + + return res +``` + +```java +public class Solution { + public long countSubarrays(int[] nums, int k) { + int maxN = Integer.MIN_VALUE; + for (int num : nums) { + maxN = Math.max(maxN, num); + } + + Queue maxIndexes = new LinkedList<>(); + long res = 0; + + for (int i = 0; i < nums.length; i++) { + if (nums[i] == maxN) { + maxIndexes.add(i); + } + + if (maxIndexes.size() > k) { + maxIndexes.poll(); + } + + if (maxIndexes.size() == k) { + res += maxIndexes.peek() + 1; + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + long long countSubarrays(vector& nums, int k) { + int maxN = *max_element(nums.begin(), nums.end()); + queue maxIndexes; + long long res = 0; + + for (int i = 0; i < nums.size(); i++) { + if (nums[i] == maxN) { + maxIndexes.push(i); + } + + if (maxIndexes.size() > k) { + maxIndexes.pop(); + } + + if (maxIndexes.size() == k) { + res += maxIndexes.front() + 1; + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {number} + */ + countSubarrays(nums, k) { + const maxN = Math.max(...nums); + const maxIndexes = new Queue(); + let res = 0; + + for (let i = 0; i < nums.length; i++) { + if (nums[i] === maxN) { + maxIndexes.push(i); + } + + if (maxIndexes.size() > k) { + maxIndexes.pop(); + } + + if (maxIndexes.size() === k) { + res += maxIndexes.front() + 1; + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/find-all-duplicates-in-an-array.md b/articles/find-all-duplicates-in-an-array.md new file mode 100644 index 000000000..3d1f99fde --- /dev/null +++ b/articles/find-all-duplicates-in-an-array.md @@ -0,0 +1,440 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def findDuplicates(self, nums: List[int]) -> List[int]: + n = len(nums) + res = [] + + for i in range(n): + for j in range(i + 1, n): + if nums[i] == nums[j]: + res.append(nums[i]) + break + + return res +``` + +```java +public class Solution { + public List findDuplicates(int[] nums) { + int n = nums.length; + List res = new ArrayList<>(); + + for (int i = 0; i < n; i++) { + for (int j = i + 1; j < n; j++) { + if (nums[i] == nums[j]) { + res.add(nums[i]); + break; + } + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector findDuplicates(vector& nums) { + int n = nums.size(); + vector res; + + for (int i = 0; i < n; i++) { + for (int j = i + 1; j < n; j++) { + if (nums[i] == nums[j]) { + res.push_back(nums[i]); + break; + } + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number[]} + */ + findDuplicates(nums) { + const n = nums.length; + const res = []; + + for (let i = 0; i < n; i++) { + for (let j = i + 1; j < n; j++) { + if (nums[i] === nums[j]) { + res.push(nums[i]); + break; + } + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(1)$ extra space. + +--- + +## 2. Sorting + +::tabs-start + +```python +class Solution: + def findDuplicates(self, nums: List[int]) -> List[int]: + nums.sort() + res = [] + + for i in range(len(nums) - 1): + if nums[i] == nums[i + 1]: + res.append(nums[i]) + + return res +``` + +```java +public class Solution { + public List findDuplicates(int[] nums) { + Arrays.sort(nums); + List res = new ArrayList<>(); + + for (int i = 0; i < nums.length - 1; i++) { + if (nums[i] == nums[i + 1]) { + res.add(nums[i]); + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector findDuplicates(vector& nums) { + sort(nums.begin(), nums.end()); + vector res; + + for (int i = 0; i < nums.size() - 1; i++) { + if (nums[i] == nums[i + 1]) { + res.push_back(nums[i]); + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number[]} + */ + findDuplicates(nums) { + nums.sort((a, b) => a - b); + const res = []; + + for (let i = 0; i < nums.length - 1; i++) { + if (nums[i] === nums[i + 1]) { + res.push(nums[i]); + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(1)$ or $O(n)$ depending on the sorting algorithm. + +--- + +## 3. Hash Set + +::tabs-start + +```python +class Solution: + def findDuplicates(self, nums: List[int]) -> List[int]: + seen = set() + res = [] + for num in nums: + if num in seen: + res.append(num) + else: + seen.add(num) + return res +``` + +```java +public class Solution { + public List findDuplicates(int[] nums) { + Set seen = new HashSet<>(); + List res = new ArrayList<>(); + + for (int num : nums) { + if (seen.contains(num)) { + res.add(num); + } else { + seen.add(num); + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector findDuplicates(vector& nums) { + unordered_set seen; + vector res; + + for (int num : nums) { + if (seen.count(num)) { + res.push_back(num); + } else { + seen.insert(num); + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number[]} + */ + findDuplicates(nums) { + const seen = new Set(); + const res = []; + + for (const num of nums) { + if (seen.has(num)) { + res.push(num); + } else { + seen.add(num); + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 4. Hash Map + +::tabs-start + +```python +class Solution: + def findDuplicates(self, nums: List[int]) -> List[int]: + count = Counter(nums) + res = [] + + for num in count: + if count[num] == 2: + res.append(num) + + return res +``` + +```java +public class Solution { + public List findDuplicates(int[] nums) { + Map count = new HashMap<>(); + List res = new ArrayList<>(); + + for (int num : nums) { + count.put(num, count.getOrDefault(num, 0) + 1); + } + + for (int num : count.keySet()) { + if (count.get(num) == 2) { + res.add(num); + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector findDuplicates(vector& nums) { + unordered_map count; + vector res; + + for (int num : nums) { + count[num]++; + } + + for (auto& [num, freq] : count) { + if (freq == 2) { + res.push_back(num); + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number[]} + */ + findDuplicates(nums) { + const count = new Map(); + const res = []; + + for (const num of nums) { + count.set(num, (count.get(num) || 0) + 1); + } + + for (const [num, freq] of count.entries()) { + if (freq === 2) { + res.push(num); + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 5. Negative Marking + +::tabs-start + +```python +class Solution: + def findDuplicates(self, nums: List[int]) -> List[int]: + res = [] + + for num in nums: + idx = abs(num) - 1 + if nums[idx] < 0: + res.append(abs(num)) + nums[idx] = -nums[idx] + + return res +``` + +```java +public class Solution { + public List findDuplicates(int[] nums) { + List res = new ArrayList<>(); + + for (int num : nums) { + int idx = Math.abs(num) - 1; + if (nums[idx] < 0) { + res.add(Math.abs(num)); + } + nums[idx] = -nums[idx]; + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector findDuplicates(vector& nums) { + vector res; + + for (int num : nums) { + int idx = abs(num) - 1; + if (nums[idx] < 0) { + res.push_back(abs(num)); + } + nums[idx] = -nums[idx]; + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number[]} + */ + findDuplicates(nums) { + const res = []; + + for (let num of nums) { + const idx = Math.abs(num) - 1; + if (nums[idx] < 0) { + res.push(Math.abs(num)); + } + nums[idx] = -nums[idx]; + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. \ No newline at end of file diff --git a/articles/find-polygon-with-the-largest-perimeter.md b/articles/find-polygon-with-the-largest-perimeter.md new file mode 100644 index 000000000..f736cb463 --- /dev/null +++ b/articles/find-polygon-with-the-largest-perimeter.md @@ -0,0 +1,295 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def largestPerimeter(self, nums: List[int]) -> int: + n = len(nums) + res = -1 + + for i, large in enumerate(nums): + cur = 0 + for j, side in enumerate(nums): + if i != j and side <= large: + cur += side + if cur > large: + res = max(res, cur + large) + + return res +``` + +```java +public class Solution { + public long largestPerimeter(int[] nums) { + int n = nums.length; + long res = -1; + + for (int i = 0; i < n; i++) { + int large = nums[i]; + long cur = 0; + + for (int j = 0; j < n; j++) { + if (i != j && nums[j] <= large) { + cur += nums[j]; + } + } + + if (cur > large) { + res = Math.max(res, cur + large); + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + long long largestPerimeter(vector& nums) { + int n = nums.size(); + long long res = -1; + + for (int i = 0; i < n; i++) { + long long large = nums[i]; + long long cur = 0; + + for (int j = 0; j < n; j++) { + if (i != j && nums[j] <= large) { + cur += nums[j]; + } + } + + if (cur > large) { + res = max(res, cur + large); + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + largestPerimeter(nums) { + const n = nums.length; + let res = -1; + + for (let i = 0; i < n; i++) { + const large = nums[i]; + let cur = 0; + + for (let j = 0; j < n; j++) { + if (i !== j && nums[j] <= large) { + cur += nums[j]; + } + } + + if (cur > large) { + res = Math.max(res, cur + large); + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(1)$ extra space. + +--- + +## 2. Sorting + +::tabs-start + +```python +class Solution: + def largestPerimeter(self, nums: List[int]) -> int: + nums.sort() + res = -1 + total = 0 + for num in nums: + if total > num: + res = total + num + total += num + return res +``` + +```java +public class Solution { + public long largestPerimeter(int[] nums) { + Arrays.sort(nums); + long res = -1; + long total = 0; + + for (int num : nums) { + if (total > num) { + res = total + num; + } + total += num; + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + long long largestPerimeter(vector& nums) { + sort(nums.begin(), nums.end()); + long long res = -1; + long long total = 0; + + for (int& num : nums) { + if (total > num) { + res = total + num; + } + total += num; + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + largestPerimeter(nums) { + nums.sort((a, b) => a - b); + let res = -1; + let total = 0; + + for (let num of nums) { + if (total > num) { + res = total + num; + } + total += num; + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(1)$ or $O(n)$ depending on the sorting algorithm. + +--- + +## 3. Max Heap + +::tabs-start + +```python +class Solution: + def largestPerimeter(self, nums: List[int]) -> int: + nums = [-num for num in nums] + heapq.heapify(nums) + total = -sum(nums) + + while len(nums) > 2: + largest = -heapq.heappop(nums) + total -= largest + if largest < total: + return total + largest + return -1 +``` + +```java +public class Solution { + public long largestPerimeter(int[] nums) { + PriorityQueue maxHeap = new PriorityQueue<>((a, b) -> b - a); + long total = 0; + for (int num : nums) { + maxHeap.add(num); + total += num; + } + + while (maxHeap.size() > 2) { + int largest = maxHeap.poll(); + total -= largest; + if (largest < total) { + return total + largest; + } + } + return -1; + } +} +``` + +```cpp +class Solution { +public: + long long largestPerimeter(vector& nums) { + priority_queue maxHeap(nums.begin(), nums.end()); + long long total = accumulate(nums.begin(), nums.end(), 0LL); + + while (maxHeap.size() > 2) { + int largest = maxHeap.top(); + maxHeap.pop(); + total -= largest; + if (largest < total) { + return total + largest; + } + } + return -1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + largestPerimeter(nums) { + const maxHeap = new MaxPriorityQueue(); + let total = 0; + + nums.forEach(num => { + total += num; + maxHeap.enqueue(num); + }); + + while (maxHeap.size() > 2) { + const largest = maxHeap.dequeue().element; + total -= largest; + if (largest < total) return total + largest; + } + return -1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: + * $O(n + (30\log n))$ in Python, C++, JS. + * $O(n \log n)$ in Java. +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/get-equal-substrings-within-budget.md b/articles/get-equal-substrings-within-budget.md new file mode 100644 index 000000000..078230575 --- /dev/null +++ b/articles/get-equal-substrings-within-budget.md @@ -0,0 +1,274 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def equalSubstring(self, s: str, t: str, maxCost: int) -> int: + n = len(s) + res = 0 + + for i in range(n): + cur_cost = 0 + for j in range(i, n): + cur_cost += abs(ord(t[j]) - ord(s[j])) + if cur_cost > maxCost: + break + res = max(res, j - i + 1) + + return res +``` + +```java +public class Solution { + public int equalSubstring(String s, String t, int maxCost) { + int n = s.length(); + int res = 0; + + for (int i = 0; i < n; i++) { + int curCost = 0; + for (int j = i; j < n; j++) { + curCost += Math.abs(t.charAt(j) - s.charAt(j)); + if (curCost > maxCost) { + break; + } + res = Math.max(res, j - i + 1); + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int equalSubstring(string s, string t, int maxCost) { + int n = s.size(); + int res = 0; + + for (int i = 0; i < n; i++) { + int curCost = 0; + for (int j = i; j < n; j++) { + curCost += abs(t[j] - s[j]); + if (curCost > maxCost) { + break; + } + res = max(res, j - i + 1); + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @param {string} t + * @param {number} maxCost + * @return {number} + */ + equalSubstring(s, t, maxCost) { + const n = s.length; + let res = 0; + + for (let i = 0; i < n; i++) { + let curCost = 0; + for (let j = i; j < n; j++) { + curCost += Math.abs(t.charCodeAt(j) - s.charCodeAt(j)); + if (curCost > maxCost) { + break; + } + res = Math.max(res, j - i + 1); + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(1)$ + +--- + +## 2. Sliding Window + +::tabs-start + +```python +class Solution: + def equalSubstring(self, s: str, t: str, maxCost: int) -> int: + curCost = 0 + l = 0 + res = 0 + + for r in range(len(s)): + curCost += abs(ord(s[r]) - ord(t[r])) + while curCost > maxCost: + curCost -= abs(ord(s[l]) - ord(t[l])) + l += 1 + res = max(res, r - l + 1) + + return res +``` + +```java +public class Solution { + public int equalSubstring(String s, String t, int maxCost) { + int curCost = 0, l = 0, res = 0; + + for (int r = 0; r < s.length(); r++) { + curCost += Math.abs(s.charAt(r) - t.charAt(r)); + while (curCost > maxCost) { + curCost -= Math.abs(s.charAt(l) - t.charAt(l)); + l++; + } + res = Math.max(res, r - l + 1); + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int equalSubstring(string s, string t, int maxCost) { + int curCost = 0, l = 0, res = 0; + + for (int r = 0; r < s.length(); r++) { + curCost += abs(s[r] - t[r]); + while (curCost > maxCost) { + curCost -= abs(s[l] - t[l]); + l++; + } + res = max(res, r - l + 1); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @param {string} t + * @param {number} maxCost + * @return {number} + */ + equalSubstring(s, t, maxCost) { + let curCost = 0, l = 0, res = 0; + + for (let r = 0; r < s.length; r++) { + curCost += Math.abs(s.charCodeAt(r) - t.charCodeAt(r)); + while (curCost > maxCost) { + curCost -= Math.abs(s.charCodeAt(l) - t.charCodeAt(l)); + l++; + } + res = Math.max(res, r - l + 1); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ + +--- + +## 3. Sliding Window (Optimal) + +::tabs-start + +```python +class Solution: + def equalSubstring(self, s: str, t: str, maxCost: int) -> int: + l = 0 + for r in range(len(s)): + maxCost -= abs(ord(s[r]) - ord(t[r])) + if maxCost < 0: + maxCost += abs(ord(s[l]) - ord(t[l])) + l += 1 + return len(s) - l +``` + +```java +public class Solution { + public int equalSubstring(String s, String t, int maxCost) { + int l = 0; + for (int r = 0; r < s.length(); r++) { + maxCost -= Math.abs(s.charAt(r) - t.charAt(r)); + if (maxCost < 0) { + maxCost += Math.abs(s.charAt(l) - t.charAt(l)); + l++; + } + } + return s.length() - l; + } +} +``` + +```cpp +class Solution { +public: + int equalSubstring(string s, string t, int maxCost) { + int l = 0; + for (int r = 0; r < s.length(); r++) { + maxCost -= abs(s[r] - t[r]); + if (maxCost < 0) { + maxCost += abs(s[l] - t[l]); + l++; + } + } + return s.length() - l; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @param {string} t + * @param {number} maxCost + * @return {number} + */ + equalSubstring(s, t, maxCost) { + let l = 0; + for (let r = 0; r < s.length; r++) { + maxCost -= Math.abs(s.charCodeAt(r) - t.charCodeAt(r)); + if (maxCost < 0) { + maxCost += Math.abs(s.charCodeAt(l) - t.charCodeAt(l)); + l++; + } + } + return s.length - l; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/k-th-symbol-in-grammar.md b/articles/k-th-symbol-in-grammar.md new file mode 100644 index 000000000..d3f782b4c --- /dev/null +++ b/articles/k-th-symbol-in-grammar.md @@ -0,0 +1,413 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def kthGrammar(self, n: int, k: int) -> int: + prev = ['0'] + for i in range(2, n + 1): + cur = [] + for c in prev: + if c == '0': + cur.append('0') + cur.append('1') + else: + cur.append('1') + cur.append('0') + prev = cur + return int(prev[k - 1]) +``` + +```java +public class Solution { + public int kthGrammar(int n, int k) { + List prev = new ArrayList<>(); + prev.add('0'); + for (int i = 2; i <= n; i++) { + List cur = new ArrayList<>(); + for (char c : prev) { + if (c == '0') { + cur.add('0'); + cur.add('1'); + } else { + cur.add('1'); + cur.add('0'); + } + } + prev = cur; + } + return prev.get(k - 1) - '0'; + } +} +``` + +```cpp +class Solution { +public: + int kthGrammar(int n, int k) { + vector prev = {'0'}; + for (int i = 2; i <= n; i++) { + vector cur; + for (char c : prev) { + if (c == '0') { + cur.push_back('0'); + cur.push_back('1'); + } else { + cur.push_back('1'); + cur.push_back('0'); + } + } + prev = cur; + } + return prev[k - 1] - '0'; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number} k + * @return {number} + */ + kthGrammar(n, k) { + let prev = ['0']; + for (let i = 2; i <= n; i++) { + let cur = []; + for (let c of prev) { + if (c === '0') { + cur.push('0'); + cur.push('1'); + } else { + cur.push('1'); + cur.push('0'); + } + } + prev = cur; + } + return parseInt(prev[k - 1]); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(2 ^ n)$ +* Space complexity: $O(2 ^ n)$ + +--- + +## 2. Binary Tree Traversal (Recursion) + +::tabs-start + +```python +class Solution: + def kthGrammar(self, n: int, k: int) -> int: + def dfs(n, k, root): + if n == 1: + return root + + total = 1 << (n - 1) + if k > (total // 2): + return dfs(n - 1, k - (total // 2), root ^ 1) + else: + return dfs(n - 1, k, root) + + return dfs(n, k, 0) +``` + +```java +public class Solution { + public int kthGrammar(int n, int k) { + return dfs(n, k, 0); + } + + private int dfs(int n, int k, int root) { + if (n == 1) { + return root; + } + + int total = 1 << (n - 1); + if (k > total / 2) { + return dfs(n - 1, k - total / 2, root ^ 1); + } else { + return dfs(n - 1, k, root); + } + } +} +``` + +```cpp +class Solution { +public: + int kthGrammar(int n, int k) { + return dfs(n, k, 0); + } + + int dfs(int n, int k, int root){ + if (n == 1) return root; + + int total = 1 << (n - 1); + if (k > total / 2) { + return dfs(n - 1, k - total / 2, root ^ 1); + } else { + return dfs(n - 1, k, root); + } + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number} k + * @return {number} + */ + kthGrammar(n, k) { + const dfs = (n, k, root) => { + if (n === 1) return root; + + const total = 1 << (n - 1); + if (k > total / 2) { + return dfs(n - 1, k - total / 2, root ^ 1); + } else { + return dfs(n - 1, k, root); + } + }; + + return dfs(n, k, 0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ for recursion stack. + +--- + +## 3. Binary Tree Traversal (Iteration) + +::tabs-start + +```python +class Solution: + def kthGrammar(self, n: int, k: int) -> int: + cur = 0 + left, right = 1, 2 ** (n - 1) + + for _ in range(n - 1): + mid = (left + right) // 2 + if k <= mid: + right = mid + else: + left = mid + 1 + cur = 0 if cur else 1 + + return cur +``` + +```java +public class Solution { + public int kthGrammar(int n, int k) { + int cur = 0; + int left = 1, right = 1 << (n - 1); + + for (int i = 0; i < n - 1; i++) { + int mid = (left + right) / 2; + if (k <= mid) { + right = mid; + } else { + left = mid + 1; + cur = (cur == 0) ? 1 : 0; + } + } + + return cur; + } +} +``` + +```cpp +class Solution { +public: + int kthGrammar(int n, int k) { + int cur = 0; + int left = 1, right = 1 << (n - 1); + + for (int i = 0; i < n - 1; i++) { + int mid = (left + right) / 2; + if (k <= mid) { + right = mid; + } else { + left = mid + 1; + cur = (cur == 0) ? 1 : 0; + } + } + + return cur; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number} k + * @return {number} + */ + kthGrammar(n, k) { + let cur = 0; + let left = 1, right = 1 << (n - 1); + + for (let i = 0; i < n - 1; i++) { + let mid = Math.floor((left + right) / 2); + if (k <= mid) { + right = mid; + } else { + left = mid + 1; + cur = cur === 0 ? 1 : 0; + } + } + + return cur; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ + +--- + +## 4. Recursion (Traverse Towards Root) + +::tabs-start + +```python +class Solution: + def kthGrammar(self, n: int, k: int) -> int: + if n == 1: + return 0 + if k & 1: + return self.kthGrammar(n - 1, (k + 1) // 2) + return self.kthGrammar(n - 1, k // 2) ^ 1 +``` + +```java +public class Solution { + public int kthGrammar(int n, int k) { + if (n == 1) { + return 0; + } + if ((k & 1) == 1) { + return kthGrammar(n - 1, (k + 1) / 2); + } + return kthGrammar(n - 1, k / 2) ^ 1; + } +} +``` + +```cpp +class Solution { +public: + int kthGrammar(int n, int k) { + if (n == 1) { + return 0; + } + if (k & 1) { + return kthGrammar(n - 1, (k + 1) / 2); + } + return kthGrammar(n - 1, k / 2) ^ 1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number} k + * @return {number} + */ + kthGrammar(n, k) { + if (n === 1) { + return 0; + } + if (k % 2 === 1) { + return this.kthGrammar(n - 1, Math.floor((k + 1) / 2)); + } + return this.kthGrammar(n - 1, Math.floor(k / 2)) ^ 1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ for recursion stack. + +--- + +## 5. Math + +::tabs-start + +```python +class Solution: + def kthGrammar(self, n: int, k: int) -> int: + return bin(k - 1).count('1') & 1 +``` + +```java +public class Solution { + public int kthGrammar(int n, int k) { + return Integer.bitCount(k - 1) & 1; + } +} +``` + +```cpp +class Solution { +public: + int kthGrammar(int n, int k) { + return __builtin_popcount(k - 1) & 1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number} k + * @return {number} + */ + kthGrammar(n, k) { + return (k - 1).toString(2).split('1').length - 1 & 1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(\log n)$ +* Space complexity: $O(1)$ or $O(\log n)$ depending on the language. \ No newline at end of file diff --git a/articles/length-of-longest-subarray-with-at-most-k-frequency.md b/articles/length-of-longest-subarray-with-at-most-k-frequency.md new file mode 100644 index 000000000..5726525c2 --- /dev/null +++ b/articles/length-of-longest-subarray-with-at-most-k-frequency.md @@ -0,0 +1,278 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def maxSubarrayLength(self, nums: List[int], k: int) -> int: + n, res = len(nums), 0 + + for i in range(n): + count = defaultdict(int) + for j in range(i, n): + count[nums[j]] += 1 + if count[nums[j]] > k: + break + res = max(res, j - i + 1) + + return res +``` + +```java +public class Solution { + public int maxSubarrayLength(int[] nums, int k) { + int n = nums.length, res = 0; + + for (int i = 0; i < n; i++) { + Map count = new HashMap<>(); + for (int j = i; j < n; j++) { + count.put(nums[j], count.getOrDefault(nums[j], 0) + 1); + if (count.get(nums[j]) > k) { + break; + } + res = Math.max(res, j - i + 1); + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int maxSubarrayLength(vector& nums, int k) { + int n = nums.size(), res = 0; + + for (int i = 0; i < n; i++) { + unordered_map count; + for (int j = i; j < n; j++) { + count[nums[j]]++; + if (count[nums[j]] > k) { + break; + } + res = max(res, j - i + 1); + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {number} + */ + maxSubarrayLength(nums, k) { + let n = nums.length, res = 0; + + for (let i = 0; i < n; i++) { + let count = new Map(); + for (let j = i; j < n; j++) { + count.set(nums[j], (count.get(nums[j]) || 0) + 1); + if (count.get(nums[j]) > k) { + break; + } + res = Math.max(res, j - i + 1); + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n)$ + +--- + +## 2. Sliding Window + +::tabs-start + +```python +class Solution: + def maxSubarrayLength(self, nums: List[int], k: int) -> int: + res = 0 + count = defaultdict(int) + l = 0 + for r in range(len(nums)): + count[nums[r]] += 1 + while count[nums[r]] > k: + count[nums[l]] -= 1 + l += 1 + res = max(res, r - l + 1) + return res +``` + +```java +public class Solution { + public int maxSubarrayLength(int[] nums, int k) { + int res = 0; + Map count = new HashMap<>(); + int l = 0; + + for (int r = 0; r < nums.length; r++) { + count.put(nums[r], count.getOrDefault(nums[r], 0) + 1); + while (count.get(nums[r]) > k) { + count.put(nums[l], count.get(nums[l]) - 1); + l++; + } + res = Math.max(res, r - l + 1); + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int maxSubarrayLength(vector& nums, int k) { + int res = 0; + unordered_map count; + int l = 0; + + for (int r = 0; r < nums.size(); r++) { + count[nums[r]]++; + while (count[nums[r]] > k) { + count[nums[l]]--; + l++; + } + res = max(res, r - l + 1); + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {number} + */ + maxSubarrayLength(nums, k) { + let res = 0; + let count = new Map(); + let l = 0; + + for (let r = 0; r < nums.length; r++) { + count.set(nums[r], (count.get(nums[r]) || 0) + 1); + while (count.get(nums[r]) > k) { + count.set(nums[l], count.get(nums[l]) - 1); + l++; + } + res = Math.max(res, r - l + 1); + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Sliding Window (Optimal) + +::tabs-start + +```python +class Solution: + def maxSubarrayLength(self, nums: List[int], k: int) -> int: + count = defaultdict(int) + l = res = 0 + cnt = 0 # count of numbers with freq > k + for r in range(len(nums)): + count[nums[r]] += 1 + cnt += count[nums[r]] > k + if cnt > 0: + cnt -= count[nums[l]] > k + count[nums[l]] -= 1 + l += 1 + return len(nums) - l +``` + +```java +public class Solution { + public int maxSubarrayLength(int[] nums, int k) { + HashMap count = new HashMap<>(); + int l = 0, cnt = 0; // count of numbers with freq > k + for (int r = 0; r < nums.length; r++) { + count.put(nums[r], count.getOrDefault(nums[r], 0) + 1); + if (count.get(nums[r]) > k) cnt++; + if (cnt > 0) { + if (count.get(nums[l]) > k) cnt--; + count.put(nums[l], count.get(nums[l]) - 1); + l++; + } + } + return nums.length - l; + } +} +``` + +```cpp +class Solution { +public: + int maxSubarrayLength(vector& nums, int k) { + unordered_map count; + int l = 0, cnt = 0; // count of numbers with freq > k + for (int r = 0; r < nums.size(); r++) { + count[nums[r]]++; + cnt += count[nums[r]] > k; + if (cnt > 0) { + cnt -= count[nums[l]] > k; + count[nums[l]]--; + l++; + } + } + return nums.size() - l; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {number} + */ + maxSubarrayLength(nums, k) { + let count = new Map(); + let l = 0, cnt = 0; // count of numbers with freq > k + for (let r = 0; r < nums.length; r++) { + count.set(nums[r], (count.get(nums[r]) || 0) + 1); + if (count.get(nums[r]) > k) cnt++; + if (cnt > 0) { + if (count.get(nums[l]) > k) cnt--; + count.set(nums[l], count.get(nums[l]) - 1); + l++; + } + } + return nums.length - l; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/make-the-string-great.md b/articles/make-the-string-great.md new file mode 100644 index 000000000..10b297d5c --- /dev/null +++ b/articles/make-the-string-great.md @@ -0,0 +1,334 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def makeGood(self, s: str) -> str: + n = len(s) + i = 0 + while i < n: + if i and s[i] != s[i - 1] and s[i].lower() == s[i - 1].lower(): + s = s[:i - 1] + s[i + 1:] + n -= 2 + i -= 2 + i += 1 + return s +``` + +```java +public class Solution { + public String makeGood(String s) { + int n = s.length(); + int i = 0; + while (i < n) { + if (i > 0 && s.charAt(i) != s.charAt(i - 1) && + Character.toLowerCase(s.charAt(i)) == Character.toLowerCase(s.charAt(i - 1))) { + s = s.substring(0, i - 1) + s.substring(i + 1); + n -= 2; + i -= 2; + } + i++; + } + return s; + } +} +``` + +```cpp +class Solution { +public: + string makeGood(string s) { + int n = s.length(); + int i = 0; + while (i < n) { + if (i > 0 && s[i] != s[i - 1] && tolower(s[i]) == tolower(s[i - 1])) { + s.erase(i - 1, 2); + n -= 2; + i -= 2; + } + i++; + } + return s; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {string} + */ + makeGood(s) { + let n = s.length; + let i = 0; + while (i < n) { + if (i > 0 && s[i] !== s[i - 1] && s[i].toLowerCase() === s[i - 1].toLowerCase()) { + s = s.slice(0, i - 1) + s.slice(i + 1); + n -= 2; + i -= 2; + } + i++; + } + return s; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n)$ + +--- + +## 2. Stack - I + +::tabs-start + +```python +class Solution: + def makeGood(self, s: str) -> str: + def lower(c): + if ord(c) < ord('a'): + return chr(ord('a') + ord(c) - ord('A')) + return c + + stack = [] + i = 0 + while i < len(s): + if stack and stack[-1] != s[i] and lower(stack[-1]) == lower(s[i]): + stack.pop() + else: + stack.append(s[i]) + i += 1 + return "".join(stack) +``` + +```java +public class Solution { + public String makeGood(String s) { + StringBuilder stack = new StringBuilder(); + for (char c : s.toCharArray()) { + if (stack.length() > 0 && stack.charAt(stack.length() - 1) != c && + Character.toLowerCase(stack.charAt(stack.length() - 1)) == Character.toLowerCase(c)) { + stack.deleteCharAt(stack.length() - 1); + } else { + stack.append(c); + } + } + return stack.toString(); + } +} +``` + +```cpp +class Solution { +public: + string makeGood(string s) { + string stack; + for (char c : s) { + if (!stack.empty() && stack.back() != c && + tolower(stack.back()) == tolower(c)) { + stack.pop_back(); + } else { + stack.push_back(c); + } + } + return stack; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {string} + */ + makeGood(s) { + const stack = []; + for (const c of s) { + if (stack.length > 0 && stack[stack.length - 1] !== c && + stack[stack.length - 1].toLowerCase() === c.toLowerCase()) { + stack.pop(); + } else { + stack.push(c); + } + } + return stack.join(""); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Stack - II + +::tabs-start + +```python +class Solution: + def makeGood(self, s: str) -> str: + stack = [] + for i in range(len(s)): + if stack and abs(ord(s[i]) - ord(stack[-1])) == 32: + stack.pop() + else: + stack.append(s[i]) + return "".join(stack) +``` + +```java +public class Solution { + public String makeGood(String s) { + StringBuilder stack = new StringBuilder(); + for (int i = 0; i < s.length(); i++) { + if (stack.length() > 0 && + Math.abs(stack.charAt(stack.length() - 1) - s.charAt(i)) == 32) { + stack.deleteCharAt(stack.length() - 1); + } else { + stack.append(s.charAt(i)); + } + } + return stack.toString(); + } +} +``` + +```cpp +class Solution { +public: + string makeGood(string s) { + string stack; + for (char& c : s) { + if (!stack.empty() && abs(stack.back() - c) == 32) { + stack.pop_back(); + } else { + stack.push_back(c); + } + } + return stack; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {string} + */ + makeGood(s) { + const stack = []; + for (const c of s) { + if (stack.length > 0 && + Math.abs(stack[stack.length - 1].charCodeAt(0) - c.charCodeAt(0)) === 32) { + stack.pop(); + } else { + stack.push(c); + } + } + return stack.join(""); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 4. Two Pointers + +::tabs-start + +```python +class Solution: + def makeGood(self, s: str) -> str: + l = 0 + s = list(s) + for r in range(len(s)): + if l > 0 and abs(ord(s[r]) - ord(s[l - 1])) == 32: + l -= 1 + else: + s[l] = s[r] + l += 1 + return ''.join(s[:l]) +``` + +```java +public class Solution { + public String makeGood(String s) { + int l = 0; + char[] arr = s.toCharArray(); + for (int r = 0; r < arr.length; r++) { + if (l > 0 && Math.abs(arr[r] - arr[l - 1]) == 32) { + l--; + } else { + arr[l++] = arr[r]; + } + } + return new String(arr, 0, l); + } +} +``` + +```cpp +class Solution { +public: + string makeGood(string s) { + int l = 0; + for (int r = 0; r < s.length(); r++) { + if (l > 0 && abs(s[r] - s[l - 1]) == 32) { + l--; + } else { + s[l++] = s[r]; + } + } + return s.substr(0, l); + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {string} + */ + makeGood(s) { + let l = 0; + let arr = s.split(''); + for (let r = 0; r < arr.length; r++) { + if (l > 0 && Math.abs(arr[r].charCodeAt(0) - arr[l - 1].charCodeAt(0)) === 32) { + l--; + } else { + arr[l++] = arr[r]; + } + } + return arr.slice(0, l).join(''); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ or $O(n)$ depending on the language. \ No newline at end of file diff --git a/articles/minimum-length-of-string-after-deleting-similar-ends.md b/articles/minimum-length-of-string-after-deleting-similar-ends.md new file mode 100644 index 000000000..81155cffe --- /dev/null +++ b/articles/minimum-length-of-string-after-deleting-similar-ends.md @@ -0,0 +1,86 @@ +## 1. Greedy + Two Pointers + +::tabs-start + +```python +class Solution: + def minimumLength(self, s: str) -> int: + l, r = 0, len(s) - 1 + + while l < r and s[l] == s[r]: + tmp = s[l] + while l <= r and s[l] == tmp: + l += 1 + while l <= r and s[r] == tmp: + r -= 1 + return r - l + 1 +``` + +```java +public class Solution { + public int minimumLength(String s) { + int l = 0, r = s.length() - 1; + + while (l < r && s.charAt(l) == s.charAt(r)) { + char tmp = s.charAt(l); + while (l <= r && s.charAt(l) == tmp) { + l++; + } + while (l <= r && s.charAt(r) == tmp) { + r--; + } + } + return r - l + 1; + } +} +``` + +```cpp +class Solution { +public: + int minimumLength(string s) { + int l = 0, r = s.length() - 1; + + while (l < r && s[l] == s[r]) { + char tmp = s[l]; + while (l <= r && s[l] == tmp) { + l++; + } + while (l <= r && s[r] == tmp) { + r--; + } + } + return r - l + 1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {number} + */ + minimumLength(s) { + let l = 0, r = s.length - 1; + + while (l < r && s[l] === s[r]) { + const tmp = s[l]; + while (l <= r && s[l] === tmp) { + l++; + } + while (l <= r && s[r] === tmp) { + r--; + } + } + return r - l + 1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. \ No newline at end of file diff --git a/articles/minimum-number-of-operations-to-make-array-continuous.md b/articles/minimum-number-of-operations-to-make-array-continuous.md new file mode 100644 index 000000000..fda4f013d --- /dev/null +++ b/articles/minimum-number-of-operations-to-make-array-continuous.md @@ -0,0 +1,449 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def minOperations(self, nums: List[int]) -> int: + N = len(nums) + res = float("inf") + nums = sorted(set(nums)) + n = len(nums) + + for i in range(n): + noChange = 1 + for j in range(i + 1, n): + if nums[i] < nums[j] < nums[i] + N: + noChange += 1 + res = min(res, N - noChange) + + return res +``` + +```java +public class Solution { + public int minOperations(int[] nums) { + int N = nums.length; + int res = Integer.MAX_VALUE; + TreeSet set = new TreeSet<>(); + for (int num : nums) { + set.add(num); + } + Integer[] sortedNums = set.toArray(new Integer[0]); + int n = sortedNums.length; + + for (int i = 0; i < n; i++) { + int noChange = 1; + for (int j = i + 1; j < n; j++) { + if (sortedNums[j] < sortedNums[i] + N) { + noChange++; + } + } + res = Math.min(res, N - noChange); + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int minOperations(vector& nums) { + int N = nums.size(); + int res = INT_MAX; + set uniqueNums(nums.begin(), nums.end()); + vector sortedNums(uniqueNums.begin(), uniqueNums.end()); + int n = sortedNums.size(); + + for (int i = 0; i < n; i++) { + int noChange = 1; + for (int j = i + 1; j < n; j++) { + if (sortedNums[j] < sortedNums[i] + N) { + noChange++; + } + } + res = min(res, N - noChange); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + minOperations(nums) { + const N = nums.length; + let res = Infinity; + const uniqueNums = Array.from(new Set(nums)).sort((a, b) => a - b); + const n = uniqueNums.length; + + for (let i = 0; i < n; i++) { + let noChange = 1; + for (let j = i + 1; j < n; j++) { + if (uniqueNums[j] < uniqueNums[i] + N) { + noChange++; + } + } + res = Math.min(res, N - noChange); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n)$ + +--- + +## 2. Binary Search + +::tabs-start + +```python +class Solution: + def minOperations(self, nums: List[int]) -> int: + N = len(nums) + res = float("inf") + nums = sorted(set(nums)) + n = len(nums) + + for i in range(n): + l, r = i, n + while l < r: + mid = (l + r) // 2 + if nums[mid] < nums[i] + N: + l = mid + 1 + else: + r = mid + noChange = l - i + res = min(res, N - noChange) + + return res +``` + +```java +public class Solution { + public int minOperations(int[] nums) { + int N = nums.length; + int res = Integer.MAX_VALUE; + TreeSet set = new TreeSet<>(); + for (int num : nums) { + set.add(num); + } + Integer[] sortedNums = set.toArray(new Integer[0]); + int n = sortedNums.length; + + for (int i = 0; i < n; i++) { + int l = i, r = n; + while (l < r) { + int mid = l + (r - l) / 2; + if (sortedNums[mid] < sortedNums[i] + N) { + l = mid + 1; + } else { + r = mid; + } + } + int noChange = l - i; + res = Math.min(res, N - noChange); + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int minOperations(vector& nums) { + int N = nums.size(); + int res = INT_MAX; + set uniqueNums(nums.begin(), nums.end()); + vector sortedNums(uniqueNums.begin(), uniqueNums.end()); + int n = sortedNums.size(); + + for (int i = 0; i < n; i++) { + int l = i, r = n; + while (l < r) { + int mid = l + (r - l) / 2; + if (sortedNums[mid] < sortedNums[i] + N) { + l = mid + 1; + } else { + r = mid; + } + } + int noChange = l - i; + res = min(res, N - noChange); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + minOperations(nums) { + const N = nums.length; + let res = Infinity; + const uniqueNums = Array.from(new Set(nums)).sort((a, b) => a - b); + const n = uniqueNums.length; + + for (let i = 0; i < n; i++) { + let l = i, r = n; + while (l < r) { + const mid = Math.floor((l + r) / 2); + if (uniqueNums[mid] < uniqueNums[i] + N) { + l = mid + 1; + } else { + r = mid; + } + } + const noChange = l - i; + res = Math.min(res, N - noChange); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Sliding Window + +::tabs-start + +```python +class Solution: + def minOperations(self, nums: List[int]) -> int: + length = len(nums) + nums = sorted(set(nums)) + res = length + r = 0 + + for l in range(len(nums)): + while r < len(nums) and nums[r] < nums[l] + length: + r += 1 + window = r - l + res = min(res, length - window) + + return res +``` + +```java +public class Solution { + public int minOperations(int[] nums) { + int length = nums.length; + TreeSet set = new TreeSet<>(); + for (int num : nums) { + set.add(num); + } + List sortedNums = new ArrayList<>(set); + int res = length, r = 0; + + for (int l = 0; l < sortedNums.size(); l++) { + while (r < sortedNums.size() && sortedNums.get(r) < sortedNums.get(l) + length) { + r++; + } + int window = r - l; + res = Math.min(res, length - window); + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int minOperations(vector& nums) { + int length = nums.size(); + set uniqueNums(nums.begin(), nums.end()); + vector sortedNums(uniqueNums.begin(), uniqueNums.end()); + int res = length, r = 0; + + for (int l = 0; l < sortedNums.size(); l++) { + while (r < sortedNums.size() && sortedNums[r] < sortedNums[l] + length) { + r++; + } + int window = r - l; + res = min(res, length - window); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + minOperations(nums) { + const length = nums.length; + const uniqueNums = Array.from(new Set(nums)).sort((a, b) => a - b); + let res = length, r = 0; + + for (let l = 0; l < uniqueNums.length; l++) { + while (r < uniqueNums.length && uniqueNums[r] < uniqueNums[l] + length) { + r++; + } + const window = r - l; + res = Math.min(res, length - window); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ + +--- + +## 4. Sliding Window (Optimal) + +::tabs-start + +```python +class Solution: + def minOperations(self, nums: List[int]) -> int: + length = len(nums) + nums.sort() + res = length + n = 1 + + for i in range(1, length): + if nums[i] != nums[i - 1]: + nums[n] = nums[i] + n += 1 + + l = 0 + for r in range(n): + l += (nums[r] - nums[l] > length - 1) + + return length - (n - l) +``` + +```java +public class Solution { + public int minOperations(int[] nums) { + int length = nums.length; + Arrays.sort(nums); + int n = 1; + + for (int i = 1; i < length; i++) { + if (nums[i] != nums[i - 1]) { + nums[n] = nums[i]; + n++; + } + } + + int l = 0; + for (int r = 0; r < n; r++) { + if (nums[r] - nums[l] > length - 1) { + l++; + } + } + + return length - (n - l); + } +} +``` + +```cpp +class Solution { +public: + int minOperations(vector& nums) { + int length = nums.size(); + sort(nums.begin(), nums.end()); + int n = 1; + + for (int i = 1; i < length; i++) { + if (nums[i] != nums[i - 1]) { + nums[n] = nums[i]; + n++; + } + } + + int l = 0; + for (int r = 0; r < n; r++) { + if (nums[r] - nums[l] > length - 1) { + l++; + } + } + + return length - (n - l); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + minOperations(nums) { + const length = nums.length; + nums.sort((a, b) => a - b); + let n = 1; + + for (let i = 1; i < length; i++) { + if (nums[i] !== nums[i - 1]) { + nums[n] = nums[i]; + n++; + } + } + + let l = 0; + for (let r = 0; r < n; r++) { + if (nums[r] - nums[l] > length - 1) { + l++; + } + } + + return length - (n - l); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(1)$ or $O(n)$ depending on the sorting algorithm. \ No newline at end of file diff --git a/articles/minimum-operations-to-reduce-x-to-zero.md b/articles/minimum-operations-to-reduce-x-to-zero.md new file mode 100644 index 000000000..e3b3ca2eb --- /dev/null +++ b/articles/minimum-operations-to-reduce-x-to-zero.md @@ -0,0 +1,577 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def minOperations(self, nums: List[int], x: int) -> int: + n = len(nums) + res = n + 1 + suffixSum = prefixSum = 0 + + for i in range(n - 1, -1, -1): + suffixSum += nums[i] + if suffixSum == x: + res = min(res, n - i) + + for i in range(n): + prefixSum += nums[i] + suffixSum = 0 + if prefixSum == x: + res = min(res, i + 1) + + for j in range(n - 1, i, -1): + suffixSum += nums[j] + if prefixSum + suffixSum == x: + res = min(res, i + 1 + n - j) + + return -1 if res == n + 1 else res +``` + +```java +public class Solution { + public int minOperations(int[] nums, int x) { + int n = nums.length; + int res = n + 1; + int suffixSum = 0, prefixSum = 0; + + for (int i = n - 1; i >= 0; i--) { + suffixSum += nums[i]; + if (suffixSum == x) { + res = Math.min(res, n - i); + } + } + + for (int i = 0; i < n; i++) { + prefixSum += nums[i]; + suffixSum = 0; + if (prefixSum == x) { + res = Math.min(res, i + 1); + } + + for (int j = n - 1; j > i; j--) { + suffixSum += nums[j]; + if (prefixSum + suffixSum == x) { + res = Math.min(res, i + 1 + n - j); + } + } + } + + return res == n + 1 ? -1 : res; + } +} +``` + +```cpp +class Solution { +public: + int minOperations(vector& nums, int x) { + int n = nums.size(); + int res = n + 1, suffixSum = 0, prefixSum = 0; + + for (int i = n - 1; i >= 0; i--) { + suffixSum += nums[i]; + if (suffixSum == x) { + res = min(res, n - i); + } + } + + for (int i = 0; i < n; i++) { + prefixSum += nums[i]; + suffixSum = 0; + if (prefixSum == x) { + res = min(res, i + 1); + } + + for (int j = n - 1; j > i; j--) { + suffixSum += nums[j]; + if (prefixSum + suffixSum == x) { + res = min(res, i + 1 + n - j); + } + } + } + + return res == n + 1 ? -1 : res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} x + * @return {number} + */ + minOperations(nums, x) { + const n = nums.length; + let res = n + 1, suffixSum = 0, prefixSum = 0; + + for (let i = n - 1; i >= 0; i--) { + suffixSum += nums[i]; + if (suffixSum === x) { + res = Math.min(res, n - i); + } + } + + for (let i = 0; i < n; i++) { + prefixSum += nums[i]; + suffixSum = 0; + if (prefixSum === x) { + res = Math.min(res, i + 1); + } + + for (let j = n - 1; j > i; j--) { + suffixSum += nums[j]; + if (prefixSum + suffixSum === x) { + res = Math.min(res, i + 1 + n - j); + } + } + } + + return res === n + 1 ? -1 : res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(1)$ extra space. + +--- + +## 2. Prefix Sum + Binary Search + +::tabs-start + +```python +class Solution: + def minOperations(self, nums: List[int], x: int) -> int: + n = len(nums) + prefixSum = [0] * (n + 1) + for i in range(n): + prefixSum[i + 1] = prefixSum[i] + nums[i] + + if x > prefixSum[n]: + return -1 + + def binarySearch(target, m): + l, r = 1, m + index = n + 1 + + while l <= r: + mid = (l + r) >> 1 + if prefixSum[mid] >= target: + if prefixSum[mid] == target: + index = mid + r = mid - 1 + else: + l = mid + 1 + + return index + + res = binarySearch(x, n) + suffixSum = 0 + for i in range(n - 1, 0, -1): + suffixSum += nums[i] + if suffixSum == x: + res = min(res, n - i) + break + if suffixSum > x: break + res = min(res, binarySearch(x - suffixSum, i) + n - i) + + return -1 if res == n + 1 else res +``` + +```java +public class Solution { + public int minOperations(int[] nums, int x) { + int n = nums.length; + int[] prefixSum = new int[n + 1]; + for (int i = 0; i < n; i++) { + prefixSum[i + 1] = prefixSum[i] + nums[i]; + } + + if (x > prefixSum[n]) { + return -1; + } + + int res = binarySearch(prefixSum, x, n); + int suffixSum = 0; + for (int i = n - 1; i > 0; i--) { + suffixSum += nums[i]; + if (suffixSum == x) { + res = Math.min(res, n - i); + break; + } + if (suffixSum > x) break; + res = Math.min(res, binarySearch(prefixSum, x - suffixSum, i) + n - i); + } + + return res == n + 1 ? -1 : res; + } + + private int binarySearch(int[] prefixSum, int target, int m) { + int l = 1, r = m; + int index = prefixSum.length; + + while (l <= r) { + int mid = (l + r) / 2; + if (prefixSum[mid] >= target) { + if (prefixSum[mid] == target) { + index = mid; + } + r = mid - 1; + } else { + l = mid + 1; + } + } + return index; + } +} +``` + +```cpp +class Solution { +public: + int minOperations(vector& nums, int x) { + int n = nums.size(); + vector prefixSum(n + 1, 0); + for (int i = 0; i < n; i++) { + prefixSum[i + 1] = prefixSum[i] + nums[i]; + } + + if (x > prefixSum[n]) { + return -1; + } + + auto binarySearch = [&](int target, int m) { + int l = 1, r = m, index = n + 1; + while (l <= r) { + int mid = (l + r) / 2; + if (prefixSum[mid] >= target) { + if (prefixSum[mid] == target) { + index = mid; + } + r = mid - 1; + } else { + l = mid + 1; + } + } + return index; + }; + + int res = binarySearch(x, n); + int suffixSum = 0; + for (int i = n - 1; i > 0; i--) { + suffixSum += nums[i]; + if (suffixSum == x) { + res = min(res, n - i); + break; + } + if (suffixSum > x) break; + res = min(res, binarySearch(x - suffixSum, i) + n - i); + } + + return res == n + 1 ? -1 : res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} x + * @return {number} + */ + minOperations(nums, x) { + const n = nums.length; + const prefixSum = new Array(n + 1).fill(0); + for (let i = 0; i < n; i++) { + prefixSum[i + 1] = prefixSum[i] + nums[i]; + } + + if (x > prefixSum[n]) return -1; + + const binarySearch = (target, m) => { + let l = 1, r = m; + let index = n + 1; + while (l <= r) { + let mid = Math.floor((l + r) / 2); + if (prefixSum[mid] >= target) { + if (prefixSum[mid] === target) { + index = mid; + } + r = mid - 1; + } else { + l = mid + 1; + } + } + return index; + }; + + let res = binarySearch(x, n); + let suffixSum = 0; + for (let i = n - 1; i > 0; i--) { + suffixSum += nums[i]; + if (suffixSum === x) { + res = Math.min(res, n - i); + break; + } + if (suffixSum > x) break; + res = Math.min(res, binarySearch(x - suffixSum, i) + n - i); + } + + return res === n + 1 ? -1 : res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Prefix Sum + Hash Map + +::tabs-start + +```python +class Solution: + def minOperations(self, nums: List[int], x: int) -> int: + total = sum(nums) + if total == x: + return len(nums) + + target = total - x + if target < 0: + return -1 + + res = -1 + prefixSum = 0 + prefixMap = {0: -1} # prefixSum -> index + + for i, num in enumerate(nums): + prefixSum += num + if prefixSum - target in prefixMap: + res = max(res, i - prefixMap[prefixSum - target]) + prefixMap[prefixSum] = i + + return len(nums) - res if res != -1 else -1 +``` + +```java +public class Solution { + public int minOperations(int[] nums, int x) { + int total = 0; + for (int num : nums) total += num; + if (total == x) return nums.length; + + int target = total - x; + if (target < 0) return -1; + + Map prefixMap = new HashMap<>(); + prefixMap.put(0, -1); + int prefixSum = 0, res = -1; + + for (int i = 0; i < nums.length; i++) { + prefixSum += nums[i]; + if (prefixMap.containsKey(prefixSum - target)) { + res = Math.max(res, i - prefixMap.get(prefixSum - target)); + } + prefixMap.put(prefixSum, i); + } + + return res == -1 ? -1 : nums.length - res; + } +} +``` + +```cpp +class Solution { +public: + int minOperations(vector& nums, int x) { + int total = 0; + for (int& num : nums) total += num; + if (total == x) return nums.size(); + + int target = total - x; + if (target < 0) return -1; + + unordered_map prefixMap; + prefixMap[0] = -1; + int prefixSum = 0, res = -1; + + for (int i = 0; i < nums.size(); i++) { + prefixSum += nums[i]; + if (prefixMap.count(prefixSum - target)) { + res = max(res, i - prefixMap[prefixSum - target]); + } + prefixMap[prefixSum] = i; + } + + return res == -1 ? -1 : nums.size() - res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} x + * @return {number} + */ + minOperations(nums, x) { + const total = nums.reduce((acc, num) => acc + num, 0); + if (total === x) return nums.length; + + const target = total - x; + if (target < 0) return -1; + + const prefixMap = new Map(); + prefixMap.set(0, -1); + let prefixSum = 0, res = -1; + + for (let i = 0; i < nums.length; i++) { + prefixSum += nums[i]; + if (prefixMap.has(prefixSum - target)) { + res = Math.max(res, i - prefixMap.get(prefixSum - target)); + } + prefixMap.set(prefixSum, i); + } + + return res === -1 ? -1 : nums.length - res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 4. Sliding Window + +::tabs-start + +```python +class Solution: + def minOperations(self, nums: List[int], x: int) -> int: + target = sum(nums) - x + cur_sum = 0 + max_window = -1 + l = 0 + + for r in range(len(nums)): + cur_sum += nums[r] + + while l <= r and cur_sum > target: + cur_sum -= nums[l] + l += 1 + + if cur_sum == target: + max_window = max(max_window, r - l + 1) + + return -1 if max_window == -1 else len(nums) - max_window +``` + +```java +public class Solution { + public int minOperations(int[] nums, int x) { + int target = 0; + for (int num : nums) target += num; + target -= x; + + int curSum = 0, maxWindow = -1, l = 0; + + for (int r = 0; r < nums.length; r++) { + curSum += nums[r]; + + while (l <= r && curSum > target) { + curSum -= nums[l]; + l++; + } + + if (curSum == target) { + maxWindow = Math.max(maxWindow, r - l + 1); + } + } + + return maxWindow == -1 ? -1 : nums.length - maxWindow; + } +} +``` + +```cpp +class Solution { +public: + int minOperations(vector& nums, int x) { + int target = accumulate(nums.begin(), nums.end(), 0) - x; + int curSum = 0, maxWindow = -1, l = 0; + + for (int r = 0; r < nums.size(); r++) { + curSum += nums[r]; + + while (l <= r && curSum > target) { + curSum -= nums[l]; + l++; + } + + if (curSum == target) { + maxWindow = max(maxWindow, r - l + 1); + } + } + + return maxWindow == -1 ? -1 : nums.size() - maxWindow; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} x + * @return {number} + */ + minOperations(nums, x) { + const target = nums.reduce((acc, num) => acc + num, 0) - x; + let curSum = 0, maxWindow = -1, l = 0; + + for (let r = 0; r < nums.length; r++) { + curSum += nums[r]; + + while (l <= r && curSum > target) { + curSum -= nums[l]; + l++; + } + + if (curSum === target) { + maxWindow = Math.max(maxWindow, r - l + 1); + } + } + + return maxWindow === -1 ? -1 : nums.length - maxWindow; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. \ No newline at end of file diff --git a/articles/minimum-remove-to-make-valid-parentheses.md b/articles/minimum-remove-to-make-valid-parentheses.md new file mode 100644 index 000000000..23627cf72 --- /dev/null +++ b/articles/minimum-remove-to-make-valid-parentheses.md @@ -0,0 +1,522 @@ +## 1. Stack + +::tabs-start + +```python +class Solution: + def minRemoveToMakeValid(self, s: str) -> str: + res = [] + cnt = 0 # extra ( parentheses + for c in s: + if c == "(": + res.append(c) + cnt += 1 + elif c == ")" and cnt > 0: + res.append(c) + cnt -= 1 + elif c != ")": + res.append(c) + + filtered = [] + for c in reversed(res): + if c == "(" and cnt > 0: + cnt -= 1 + else: + filtered.append(c) + return "".join(reversed(filtered)) +``` + +```java +public class Solution { + public String minRemoveToMakeValid(String s) { + StringBuilder res = new StringBuilder(); + int cnt = 0; + + for (char c : s.toCharArray()) { + if (c == '(') { + res.append(c); + cnt++; + } else if (c == ')' && cnt > 0) { + res.append(c); + cnt--; + } else if (c != ')') { + res.append(c); + } + } + + StringBuilder filtered = new StringBuilder(); + for (int i = res.length() - 1; i >= 0; i--) { + char c = res.charAt(i); + if (c == '(' && cnt > 0) { + cnt--; + } else { + filtered.append(c); + } + } + return filtered.reverse().toString(); + } +} +``` + +```cpp +class Solution { +public: + string minRemoveToMakeValid(string s) { + string res; + int cnt = 0; + + for (char c : s) { + if (c == '(') { + res.push_back(c); + cnt++; + } else if (c == ')' && cnt > 0) { + res.push_back(c); + cnt--; + } else if (c != ')') { + res.push_back(c); + } + } + + string filtered; + for (int i = res.size() - 1; i >= 0; i--) { + char c = res[i]; + if (c == '(' && cnt > 0) { + cnt--; + } else { + filtered.push_back(c); + } + } + reverse(filtered.begin(), filtered.end()); + return filtered; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {string} + */ + minRemoveToMakeValid(s) { + let res = []; + let cnt = 0; + + for (let c of s) { + if (c === '(') { + res.push(c); + cnt++; + } else if (c === ')' && cnt > 0) { + res.push(c); + cnt--; + } else if (c !== ')') { + res.push(c); + } + } + + let filtered = []; + for (let i = res.length - 1; i >= 0; i--) { + let c = res[i]; + if (c === '(' && cnt > 0) { + cnt--; + } else { + filtered.push(c); + } + } + return filtered.reverse().join(''); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Without Stack + +::tabs-start + +```python +class Solution: + def minRemoveToMakeValid(self, s: str) -> str: + arr = list(s) + cnt = 0 # extra ( parentheses + for i, c in enumerate(s): + if c == "(": + cnt += 1 + elif c == ")" and cnt > 0: + cnt -= 1 + elif c == ")": + arr[i] = '' + + res = [] + for c in reversed(arr): + if c == '(' and cnt > 0: + cnt -= 1 + else: + res.append(c) + + return ''.join(reversed(res)) +``` + +```java +public class Solution { + public String minRemoveToMakeValid(String s) { + char[] arr = s.toCharArray(); + int cnt = 0; + + for (int i = 0; i < s.length(); i++) { + char c = s.charAt(i); + if (c == '(') { + cnt++; + } else if (c == ')' && cnt > 0) { + cnt--; + } else if (c == ')') { + arr[i] = '\0'; + } + } + + StringBuilder res = new StringBuilder(); + for (int i = arr.length - 1; i >= 0; i--) { + char c = arr[i]; + if (c == '(' && cnt > 0) { + cnt--; + } else if (c != '\0') { + res.append(c); + } + } + + return res.reverse().toString(); + } +} +``` + +```cpp +class Solution { +public: + string minRemoveToMakeValid(string s) { + vector arr(s.begin(), s.end()); + int cnt = 0; + + for (int i = 0; i < s.size(); i++) { + if (s[i] == '(') { + cnt++; + } else if (s[i] == ')' && cnt > 0) { + cnt--; + } else if (s[i] == ')') { + arr[i] = '\0'; + } + } + + string res; + for (int i = arr.size() - 1; i >= 0; i--) { + if (arr[i] == '(' && cnt > 0) { + cnt--; + } else if (arr[i] != '\0') { + res.push_back(arr[i]); + } + } + + reverse(res.begin(), res.end()); + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {string} + */ + minRemoveToMakeValid(s) { + let arr = [...s]; + let cnt = 0; + + for (let i = 0; i < s.length; i++) { + let c = s[i]; + if (c === '(') { + cnt++; + } else if (c === ')' && cnt > 0) { + cnt--; + } else if (c === ')') { + arr[i] = ''; + } + } + + let res = []; + for (let i = arr.length - 1; i >= 0; i--) { + let c = arr[i]; + if (c === '(' && cnt > 0) { + cnt--; + } else if (c !== '') { + res.push(c); + } + } + + return res.reverse().join(''); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Stack (Optimal) + +::tabs-start + +```python +class Solution: + def minRemoveToMakeValid(self, s: str) -> str: + s = list(s) + stack = [] + for i, c in enumerate(s): + if c == '(': + stack.append(i) + elif c == ')': + if stack: + stack.pop() + else: + s[i] = '' + + while stack: + s[stack.pop()] = '' + return ''.join(s) +``` + +```java +public class Solution { + public String minRemoveToMakeValid(String s) { + StringBuilder sb = new StringBuilder(s); + Stack stack = new Stack<>(); + + for (int i = 0; i < sb.length(); i++) { + if (sb.charAt(i) == '(') { + stack.push(i); + } else if (sb.charAt(i) == ')') { + if (!stack.isEmpty()) { + stack.pop(); + } else { + sb.setCharAt(i, '\0'); + } + } + } + + while (!stack.isEmpty()) { + sb.setCharAt(stack.pop(), '\0'); + } + + StringBuilder result = new StringBuilder(); + for (int i = 0; i < sb.length(); i++) { + if (sb.charAt(i) != '\0') { + result.append(sb.charAt(i)); + } + } + return result.toString(); + } +} +``` + +```cpp +class Solution { +public: + string minRemoveToMakeValid(string s) { + stack stack; + for (int i = 0; i < s.size(); i++) { + if (s[i] == '(') { + stack.push(i); + } else if (s[i] == ')') { + if (!stack.empty()) { + stack.pop(); + } else { + s[i] = '\0'; + } + } + } + + while (!stack.empty()) { + s[stack.top()] = '\0'; + stack.pop(); + } + + string result; + for (char& c : s) { + if (c != '\0') { + result += c; + } + } + return result; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {string} + */ + minRemoveToMakeValid(s) { + const arr = [...s]; + const stack = []; + + for (let i = 0; i < arr.length; i++) { + if (arr[i] === '(') { + stack.push(i); + } else if (arr[i] === ')') { + if (stack.length > 0) { + stack.pop(); + } else { + arr[i] = ''; + } + } + } + + while (stack.length > 0) { + arr[stack.pop()] = ''; + } + + return arr.join(''); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 4. Without Stack (Optimal) + +::tabs-start + +```python +class Solution: + def minRemoveToMakeValid(self, s: str) -> str: + openCnt = closeCnt = 0 + for c in s: + closeCnt += c == ')' + + res = [] + for c in s: + if c == '(': + if openCnt == closeCnt: + continue + openCnt += 1 + elif c == ')': + closeCnt -= 1 + if openCnt == 0: + continue + openCnt -= 1 + res.append(c) + + return ''.join(res) +``` + +```java +public class Solution { + public String minRemoveToMakeValid(String s) { + int openCnt = 0, closeCnt = 0; + for (char c : s.toCharArray()) { + if (c == ')') closeCnt++; + } + + StringBuilder res = new StringBuilder(); + for (char c : s.toCharArray()) { + if (c == '(') { + if (openCnt == closeCnt) continue; + openCnt++; + } else if (c == ')') { + closeCnt--; + if (openCnt == 0) continue; + openCnt--; + } + res.append(c); + } + + return res.toString(); + } +} +``` + +```cpp +class Solution { +public: + string minRemoveToMakeValid(string s) { + int openCnt = 0, closeCnt = 0; + for (char& c : s) { + if (c == ')') closeCnt++; + } + + string res; + for (char& c : s) { + if (c == '(') { + if (openCnt == closeCnt) continue; + openCnt++; + } else if (c == ')') { + closeCnt--; + if (openCnt == 0) continue; + openCnt--; + } + res.push_back(c); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {string} + */ + minRemoveToMakeValid(s) { + let openCnt = 0, closeCnt = 0; + for (const c of s) { + if (c === ')') closeCnt++; + } + + let res = []; + for (const c of s) { + if (c === '(') { + if (openCnt === closeCnt) continue; + openCnt++; + } else if (c === ')') { + closeCnt--; + if (openCnt === 0) continue; + openCnt--; + } + res.push(c); + } + + return res.join(''); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: + * $O(1)$ extra space. + * $O(n)$ space for the output string. \ No newline at end of file diff --git a/articles/minimum-time-to-make-rope-colorful.md b/articles/minimum-time-to-make-rope-colorful.md new file mode 100644 index 000000000..a9d436a9d --- /dev/null +++ b/articles/minimum-time-to-make-rope-colorful.md @@ -0,0 +1,270 @@ +## 1. Two Pointers - I + +::tabs-start + +```python +class Solution: + def minCost(self, colors: str, neededTime: List[int]) -> int: + n = len(neededTime) + res = i = 0 + while i < n: + j = i + maxi = curr = 0 + while j < n and colors[j] == colors[i]: + maxi = max(maxi, neededTime[j]) + curr += neededTime[j] + j += 1 + res += curr - maxi + i = j + return res +``` + +```java +public class Solution { + public int minCost(String colors, int[] neededTime) { + int n = neededTime.length; + int res = 0, i = 0; + while (i < n) { + int j = i, maxi = 0, curr = 0; + while (j < n && colors.charAt(j) == colors.charAt(i)) { + maxi = Math.max(maxi, neededTime[j]); + curr += neededTime[j]; + j++; + } + res += curr - maxi; + i = j; + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int minCost(string colors, vector& neededTime) { + int n = neededTime.size(); + int res = 0, i = 0; + while (i < n) { + int j = i, maxi = 0, curr = 0; + while (j < n && colors[j] == colors[i]) { + maxi = max(maxi, neededTime[j]); + curr += neededTime[j]; + j++; + } + res += curr - maxi; + i = j; + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} colors + * @param {number[]} neededTime + * @return {number} + */ + minCost(colors, neededTime) { + const n = neededTime.length; + let res = 0, i = 0; + while (i < n) { + let j = i, maxi = 0, curr = 0; + while (j < n && colors[j] === colors[i]) { + maxi = Math.max(maxi, neededTime[j]); + curr += neededTime[j]; + j++; + } + res += curr - maxi; + i = j; + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. + +--- + +## 2. Two Pointers - II + +::tabs-start + +```python +class Solution: + def minCost(self, colors: str, neededTime: List[int]) -> int: + l, res = 0, 0 + for r in range(1, len(colors)): + if colors[l] == colors[r]: + if neededTime[l] < neededTime[r]: + res += neededTime[l] + l = r + else: + res += neededTime[r] + else: + l = r + return res +``` + +```java +public class Solution { + public int minCost(String colors, int[] neededTime) { + int l = 0, res = 0; + for (int r = 1; r < colors.length(); r++) { + if (colors.charAt(l) == colors.charAt(r)) { + if (neededTime[l] < neededTime[r]) { + res += neededTime[l]; + l = r; + } else { + res += neededTime[r]; + } + } else { + l = r; + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int minCost(string colors, vector& neededTime) { + int l = 0, res = 0; + for (int r = 1; r < colors.size(); r++) { + if (colors[l] == colors[r]) { + if (neededTime[l] < neededTime[r]) { + res += neededTime[l]; + l = r; + } else { + res += neededTime[r]; + } + } else { + l = r; + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} colors + * @param {number[]} neededTime + * @return {number} + */ + minCost(colors, neededTime) { + let l = 0, res = 0; + for (let r = 1; r < colors.length; r++) { + if (colors[l] === colors[r]) { + if (neededTime[l] < neededTime[r]) { + res += neededTime[l]; + l = r; + } else { + res += neededTime[r]; + } + } else { + l = r; + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. + +--- + +## 3. Two Pointers - III + +::tabs-start + +```python +class Solution: + def minCost(self, colors: str, neededTime: List[int]) -> int: + res = maxi = 0 + for i in range(len(colors)): + if i and colors[i] != colors[i - 1]: + maxi = 0 + res += min(maxi, neededTime[i]) + maxi = max(maxi, neededTime[i]) + return res +``` + +```java +public class Solution { + public int minCost(String colors, int[] neededTime) { + int res = 0, maxi = 0; + for (int i = 0; i < colors.length(); i++) { + if (i > 0 && colors.charAt(i) != colors.charAt(i - 1)) { + maxi = 0; + } + res += Math.min(maxi, neededTime[i]); + maxi = Math.max(maxi, neededTime[i]); + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int minCost(string colors, vector& neededTime) { + int res = 0, maxi = 0; + for (int i = 0; i < colors.size(); i++) { + if (i > 0 && colors[i] != colors[i - 1]) { + maxi = 0; + } + res += min(maxi, neededTime[i]); + maxi = max(maxi, neededTime[i]); + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} colors + * @param {number[]} neededTime + * @return {number} + */ + minCost(colors, neededTime) { + let res = 0, maxi = 0; + for (let i = 0; i < colors.length; i++) { + if (i > 0 && colors[i] !== colors[i - 1]) { + maxi = 0; + } + res += Math.min(maxi, neededTime[i]); + maxi = Math.max(maxi, neededTime[i]); + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. \ No newline at end of file diff --git a/articles/number-of-subsequences-that-satisfy-the-given-sum-condition.md b/articles/number-of-subsequences-that-satisfy-the-given-sum-condition.md new file mode 100644 index 000000000..cc81b5ad0 --- /dev/null +++ b/articles/number-of-subsequences-that-satisfy-the-given-sum-condition.md @@ -0,0 +1,514 @@ +## 1. Brute Force (Recursion) + +::tabs-start + +```python +class Solution: + def numSubseq(self, nums: List[int], target: int) -> int: + MOD = 1000000007 + + def dfs(maxi, mini, i): + if i == len(nums): + if mini != float("inf") and (maxi + mini) <= target: + return 1 + return 0 + + skip = dfs(maxi, mini, i + 1) + include = dfs(max(maxi, nums[i]), min(mini, nums[i]), i + 1) + return (skip + include) % MOD + + return dfs(float("-inf"), float("inf"), 0) +``` + +```java +public class Solution { + private static final int MOD = 1000000007; + + public int numSubseq(int[] nums, int target) { + return dfs(nums, Integer.MIN_VALUE, Integer.MAX_VALUE, 0, target); + } + + private int dfs(int[] nums, int maxi, int mini, int i, int target) { + if (i == nums.length) { + if (mini != Integer.MAX_VALUE && (maxi + mini) <= target) { + return 1; + } + return 0; + } + + int skip = dfs(nums, maxi, mini, i + 1, target); + int include = dfs(nums, Math.max(maxi, nums[i]), Math.min(mini, nums[i]), i + 1, target); + return (skip + include) % MOD; + } +} +``` + +```cpp +class Solution { +public: + const int MOD = 1e9 + 7; + + int numSubseq(vector& nums, int target) { + return dfs(nums, INT_MIN, INT_MAX, 0, target); + } + +private: + int dfs(vector& nums, int maxi, int mini, int i, int target) { + if (i == nums.size()) { + if (mini != INT_MAX && (maxi + mini) <= target) { + return 1; + } + return 0; + } + + int skip = dfs(nums, maxi, mini, i + 1, target); + int include = dfs(nums, max(maxi, nums[i]), min(mini, nums[i]), i + 1, target); + return (skip + include) % MOD; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} target + * @return {number} + */ + numSubseq(nums, target) { + const MOD = 1000000007; + + const dfs = (maxi, mini, i) => { + if (i === nums.length) { + if (mini !== Infinity && (maxi + mini) <= target) { + return 1; + } + return 0; + } + + const skip = dfs(maxi, mini, i + 1); + const include = dfs(Math.max(maxi, nums[i]), Math.min(mini, nums[i]), i + 1); + return (skip + include) % MOD; + }; + + return dfs(-Infinity, Infinity, 0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(2 ^ n)$ +* Space complexity: $O(n)$ for recursion stack. + +--- + +## 2. Binary Search + +::tabs-start + +```python +class Solution: + def numSubseq(self, nums: List[int], target: int) -> int: + nums.sort() + MOD = 1000000007 + res = 0 + + for i in range(len(nums)): + if nums[i] * 2 > target: + break + + l, r = i, len(nums) - 1 + while l <= r: + mid = (l + r) // 2 + if nums[i] + nums[mid] <= target: + l = mid + 1 + else: + r = mid - 1 + + count = pow(2, r - i, MOD) + res = (res + count) % MOD + + return res +``` + +```java +public class Solution { + public int numSubseq(int[] nums, int target) { + Arrays.sort(nums); + int MOD = 1000000007; + int res = 0; + + for (int i = 0; i < nums.length; i++) { + if (nums[i] * 2 > target) break; + + int l = i, r = nums.length - 1; + while (l <= r) { + int mid = l + (r - l) / 2; + if (nums[i] + nums[mid] <= target) { + l = mid + 1; + } else { + r = mid - 1; + } + } + + long count = pow(2, r - i, MOD); + res = (int) ((res + count) % MOD); + } + return res; + } + + private long pow(int base, int exp, int mod) { + long result = 1; + long b = base; + while (exp > 0) { + if ((exp & 1) == 1) result = (result * b) % mod; + b = (b * b) % mod; + exp >>= 1; + } + return result; + } +} +``` + +```cpp +class Solution { +public: + int numSubseq(vector& nums, int target) { + sort(nums.begin(), nums.end()); + int MOD = 1000000007; + int res = 0; + + for (int i = 0; i < nums.size(); i++) { + if (nums[i] * 2 > target) break; + + int l = i, r = nums.size() - 1; + while (l <= r) { + int mid = l + (r - l) / 2; + if (nums[i] + nums[mid] <= target) { + l = mid + 1; + } else { + r = mid - 1; + } + } + + long long count = powMod(2, r - i, MOD); + res = (res + count) % MOD; + } + return res; + } + +private: + long long powMod(int base, int exp, int mod) { + long long result = 1, b = base; + while (exp > 0) { + if (exp & 1) result = (result * b) % mod; + b = (b * b) % mod; + exp >>= 1; + } + return result; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} target + * @return {number} + */ + numSubseq(nums, target) { + nums.sort((a, b) => a - b); + const MOD = BigInt(1000000007); + let res = 0n; + + const powerMod = (base, exp, mod) => { + let result = 1n, b = BigInt(base); + while (exp > 0) { + if (exp & 1) result = (result * b) % mod; + b = (b * b) % mod; + exp >>= 1; + } + return result; + }; + + for (let i = 0; i < nums.length; i++) { + if (nums[i] * 2 > target) break; + + let l = i, r = nums.length - 1; + while (l <= r) { + const mid = Math.floor((l + r) / 2); + if (nums[i] + nums[mid] <= target) { + l = mid + 1; + } else { + r = mid - 1; + } + } + + const count = powerMod(2, r - i, MOD); + res = (res + count) % MOD; + } + + return Number(res); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(1)$ or $O(n)$ depending on the sorting algorithm. + +--- + +## 3. Two Pointers + +::tabs-start + +```python +class Solution: + def numSubseq(self, nums: List[int], target: int) -> int: + nums.sort() + res = 0 + mod = 10**9 + 7 + + r = len(nums) - 1 + for i, left in enumerate(nums): + while i <= r and left + nums[r] > target: + r -= 1 + if i <= r: + res += pow(2, r - i, mod) + res %= mod + + return res +``` + +```java +public class Solution { + public int numSubseq(int[] nums, int target) { + Arrays.sort(nums); + int res = 0, mod = 1000000007; + int r = nums.length - 1; + + for (int i = 0; i < nums.length; i++) { + while (i <= r && nums[i] + nums[r] > target) { + r--; + } + if (i <= r) { + res = (res + power(2, r - i, mod)) % mod; + } + } + return res; + } + + private int power(int base, int exp, int mod) { + long result = 1, b = base; + while (exp > 0) { + if ((exp & 1) == 1) result = (result * b) % mod; + b = (b * b) % mod; + exp >>= 1; + } + return (int) result; + } +} +``` + +```cpp +class Solution { +public: + int numSubseq(vector& nums, int target) { + sort(nums.begin(), nums.end()); + int res = 0, mod = 1000000007; + int r = nums.size() - 1; + + for (int i = 0; i < nums.size(); i++) { + while (i <= r && nums[i] + nums[r] > target) { + r--; + } + if (i <= r) { + res = (res + power(2, r - i, mod)) % mod; + } + } + return res; + } + +private: + long long power(int base, int exp, int mod) { + long long result = 1, b = base; + while (exp > 0) { + if (exp & 1) result = (result * b) % mod; + b = (b * b) % mod; + exp >>= 1; + } + return result; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} target + * @return {number} + */ + numSubseq(nums, target) { + nums.sort((a, b) => a - b); + const mod = BigInt(1000000007); + let res = 0n; + + const power = (base, exp, mod) => { + let result = 1n, b = BigInt(base); + while (exp > 0) { + if (exp & 1) result = (result * b) % mod; + b = (b * b) % mod; + exp >>= 1; + } + return result; + }; + + for (let i = 0, r = nums.length - 1; i < nums.length; i++) { + while (nums[i] + nums[r] > target && i <= r) { + r--; + } + if (i <= r) { + res = (res + power(2, r - i, mod)) % mod; + } + } + + return Number(res); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(1)$ or $O(n)$ depending on the sorting algorithm. + +--- + +## 4. Two Pointers (Optimal) + +::tabs-start + +```python +class Solution: + def numSubseq(self, nums: List[int], target: int) -> int: + nums.sort() + MOD = 1000000007 + res = 0 + l, r = 0, len(nums) - 1 + power = [1] * len(nums) + + for i in range(1, len(nums)): + power[i] = (power[i - 1] * 2) % MOD + + while l <= r: + if nums[l] + nums[r] <= target: + res = (res + power[r - l]) % MOD + l += 1 + else: + r -= 1 + + return res +``` + +```java +public class Solution { + public int numSubseq(int[] nums, int target) { + Arrays.sort(nums); + int MOD = 1000000007; + int res = 0, l = 0, r = nums.length - 1; + int[] power = new int[nums.length]; + power[0] = 1; + + for (int i = 1; i < nums.length; i++) { + power[i] = (power[i - 1] * 2) % MOD; + } + + while (l <= r) { + if (nums[l] + nums[r] <= target) { + res = (res + power[r - l]) % MOD; + l++; + } else { + r--; + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int numSubseq(vector& nums, int target) { + sort(nums.begin(), nums.end()); + int MOD = 1000000007; + int res = 0, l = 0, r = nums.size() - 1; + vector power(nums.size(), 1); + + for (int i = 1; i < nums.size(); i++) { + power[i] = (power[i - 1] * 2) % MOD; + } + + while (l <= r) { + if (nums[l] + nums[r] <= target) { + res = (res + power[r - l]) % MOD; + l++; + } else { + r--; + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} target + * @return {number} + */ + numSubseq(nums, target) { + nums.sort((a, b) => a - b); + const MOD = 1000000007; + let res = 0, l = 0, r = nums.length - 1; + const power = Array(nums.length).fill(1); + + for (let i = 1; i < nums.length; i++) { + power[i] = (power[i - 1] * 2) % MOD; + } + + while (l <= r) { + if (nums[l] + nums[r] <= target) { + res = (res + power[r - l]) % MOD; + l++; + } else { + r--; + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/rearrange-array-elements-by-sign.md b/articles/rearrange-array-elements-by-sign.md new file mode 100644 index 000000000..a634ed75b --- /dev/null +++ b/articles/rearrange-array-elements-by-sign.md @@ -0,0 +1,310 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def rearrangeArray(self, nums: List[int]) -> List[int]: + n = len(nums) + for i in range(n): + if ((i % 2 == 0 and nums[i] > 0) or + (i % 2 == 1 and nums[i] < 0)): + continue + + j = i + 1 + while j < n and ((nums[j] > 0) == (nums[i] > 0)): + j += 1 + + tmp = nums[j] + while j > i: + nums[j] = nums[j - 1] + j -= 1 + nums[i] = tmp + return nums +``` + +```java +public class Solution { + public int[] rearrangeArray(int[] nums) { + int n = nums.length; + for (int i = 0; i < n; i++) { + if ((i % 2 == 0 && nums[i] > 0) || (i % 2 == 1 && nums[i] < 0)) { + continue; + } + + int j = i + 1; + while (j < n && ((nums[j] > 0) == (nums[i] > 0))) { + j++; + } + + int temp = nums[j]; + while (j > i) { + nums[j] = nums[j - 1]; + j--; + } + nums[i] = temp; + } + return nums; + } +} +``` + +```cpp +class Solution { +public: + vector rearrangeArray(vector& nums) { + int n = nums.size(); + for (int i = 0; i < n; i++) { + if ((i % 2 == 0 && nums[i] > 0) || (i % 2 == 1 && nums[i] < 0)) { + continue; + } + + int j = i + 1; + while (j < n && ((nums[j] > 0) == (nums[i] > 0))) { + j++; + } + + int temp = nums[j]; + while (j > i) { + nums[j] = nums[j - 1]; + j--; + } + nums[i] = temp; + } + return nums; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number[]} + */ + rearrangeArray(nums) { + let n = nums.length; + for (let i = 0; i < n; i++) { + if ((i % 2 === 0 && nums[i] > 0) || (i % 2 === 1 && nums[i] < 0)) { + continue; + } + + let j = i + 1; + while (j < n && ((nums[j] > 0) === (nums[i] > 0))) { + j++; + } + + let temp = nums[j]; + while (j > i) { + nums[j] = nums[j - 1]; + j--; + } + nums[i] = temp; + } + return nums; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(1)$ extra space. + +--- + +## 2. Group Numbers Into Two Arrays + +::tabs-start + +```python +class Solution: + def rearrangeArray(self, nums: List[int]) -> List[int]: + pos, neg = [], [] + for num in nums: + if num > 0: + pos.append(num) + else: + neg.append(num) + + i = 0 + while 2 * i < len(nums): + nums[2 * i] = pos[i] + nums[2 * i + 1] = neg[i] + i += 1 + return nums +``` + +```java +public class Solution { + public int[] rearrangeArray(int[] nums) { + List pos = new ArrayList<>(); + List neg = new ArrayList<>(); + for (int num : nums) { + if (num > 0) { + pos.add(num); + } else { + neg.add(num); + } + } + + int i = 0; + while (2 * i < nums.length) { + nums[2 * i] = pos.get(i); + nums[2 * i + 1] = neg.get(i); + i++; + } + return nums; + } +} +``` + +```cpp +class Solution { +public: + vector rearrangeArray(vector& nums) { + vector pos, neg; + for (int num : nums) { + if (num > 0) { + pos.push_back(num); + } else { + neg.push_back(num); + } + } + + int i = 0; + while (2 * i < nums.size()) { + nums[2 * i] = pos[i]; + nums[2 * i + 1] = neg[i]; + i++; + } + return nums; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number[]} + */ + rearrangeArray(nums) { + const pos = [], neg = []; + for (const num of nums) { + if (num > 0) { + pos.push(num); + } else { + neg.push(num); + } + } + + let i = 0; + while (2 * i < nums.length) { + nums[2 * i] = pos[i]; + nums[2 * i + 1] = neg[i]; + i++; + } + return nums; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Two Pointers + +::tabs-start + +```python +class Solution: + def rearrangeArray(self, nums: List[int]) -> List[int]: + i, j = 0, 1 + res = [0] * len(nums) + for k in range(len(nums)): + if nums[k] > 0: + res[i] = nums[k] + i += 2 + else: + res[j] = nums[k] + j += 2 + return res +``` + +```java +public class Solution { + public int[] rearrangeArray(int[] nums) { + int i = 0, j = 1; + int[] res = new int[nums.length]; + for (int k = 0; k < nums.length; k++) { + if (nums[k] > 0) { + res[i] = nums[k]; + i += 2; + } else { + res[j] = nums[k]; + j += 2; + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + vector rearrangeArray(vector& nums) { + int i = 0, j = 1; + vector res(nums.size()); + for (int k = 0; k < nums.size(); k++) { + if (nums[k] > 0) { + res[i] = nums[k]; + i += 2; + } else { + res[j] = nums[k]; + j += 2; + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number[]} + */ + rearrangeArray(nums) { + let i = 0, j = 1; + const res = new Array(nums.length); + for (let k = 0; k < nums.length; k++) { + if (nums[k] > 0) { + res[i] = nums[k]; + i += 2; + } else { + res[j] = nums[k]; + j += 2; + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ for the output array. \ No newline at end of file diff --git a/articles/remove-duplicates-from-sorted-array-ii.md b/articles/remove-duplicates-from-sorted-array-ii.md new file mode 100644 index 000000000..8fff8a2d4 --- /dev/null +++ b/articles/remove-duplicates-from-sorted-array-ii.md @@ -0,0 +1,421 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def removeDuplicates(self, nums: List[int]) -> int: + n = len(nums) + if n <= 2: + return n + i = 0 + while i < n - 1: + if nums[i] == nums[i + 1]: + j = i + 2 + cnt = 0 + while j < n and nums[i] == nums[j]: + j += 1 + cnt += 1 + for k in range(i + 2, n): + if j >= n: + break + nums[k] = nums[j] + j += 1 + n -= cnt + i += 2 + else: + i += 1 + return n +``` + +```java +public class Solution { + public int removeDuplicates(int[] nums) { + int n = nums.length; + if (n <= 2) return n; + int i = 0; + while (i < n - 1) { + if (nums[i] == nums[i + 1]) { + int j = i + 2, cnt = 0; + while (j < n && nums[i] == nums[j]) { + j++; + cnt++; + } + for (int k = i + 2; k < n; k++) { + if (j >= n) break; + nums[k] = nums[j++]; + } + n -= cnt; + i += 2; + } else { + i++; + } + } + return n; + } +} +``` + +```cpp +class Solution { +public: + int removeDuplicates(vector& nums) { + int n = nums.size(); + if (n <= 2) return n; + int i = 0; + while (i < n - 1) { + if (nums[i] == nums[i + 1]) { + int j = i + 2, cnt = 0; + while (j < n && nums[i] == nums[j]) { + j++; + cnt++; + } + for (int k = i + 2; k < n; k++) { + if (j >= n) break; + nums[k] = nums[j++]; + } + n -= cnt; + i += 2; + } else { + i++; + } + } + return n; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + removeDuplicates(nums) { + let n = nums.length; + if (n <= 2) return n; + let i = 0; + while (i < n - 1) { + if (nums[i] === nums[i + 1]) { + let j = i + 2, cnt = 0; + while (j < n && nums[i] === nums[j]) { + j++; + cnt++; + } + for (let k = i + 2; k < n; k++) { + if (j >= n) break; + nums[k] = nums[j++]; + } + n -= cnt; + i += 2; + } else { + i++; + } + } + return n; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(1)$ extra space. + +--- + +## 2. Hash Map + +::tabs-start + +```python +class Solution: + def removeDuplicates(self, nums: List[int]) -> int: + n = len(nums) + if n <= 2: + return n + + count = Counter(nums) + i = 0 + for num in count: + nums[i] = num + count[num] -= 1 + i += 1 + if count[num] >= 1: + nums[i] = num + count[num] -= 1 + i += 1 + return i +``` + +```java +public class Solution { + public int removeDuplicates(int[] nums) { + Map count = new HashMap<>(); + List arr = new ArrayList<>(); + for (int num : nums) { + count.put(num, count.getOrDefault(num, 0) + 1); + if (count.get(num) == 1) { + arr.add(num); + } + } + + int i = 0; + for (int num : arr) { + nums[i++] = num; + count.put(num, count.get(num) - 1); + if (count.get(num) >= 1) { + nums[i++] = num; + count.put(num, count.get(num) - 1); + } + } + return i; + } +} +``` + +```cpp +class Solution { +public: + int removeDuplicates(vector& nums) { + unordered_map count; + vector arr; + for (int& num : nums) { + count[num]++; + if (count[num] == 1) { + arr.push_back(num); + } + } + + int i = 0; + for (auto& num : arr) { + int& cnt = count[num]; + nums[i++] = num; + cnt--; + if (cnt >= 1) { + nums[i++] = num; + cnt--; + } + } + return i; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + removeDuplicates(nums) { + const count = new Map(); + for (const num of nums) { + count.set(num, (count.get(num) || 0) + 1); + } + + let i = 0; + for (const [num, cnt] of count) { + nums[i++] = num; + count.set(num, cnt - 1); + if (count.get(num) >= 1) { + nums[i++] = num; + count.set(num, cnt - 1); + } + } + return i; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Two Pointers + +::tabs-start + +```python +class Solution: + def removeDuplicates(self, nums: List[int]) -> int: + l, r = 0, 0 + + while r < len(nums): + count = 1 + while r + 1 < len(nums) and nums[r] == nums[r + 1]: + r += 1 + count += 1 + + for i in range(min(2, count)): + nums[l] = nums[r] + l += 1 + r += 1 + + return l +``` + +```java +public class Solution { + public int removeDuplicates(int[] nums) { + int l = 0, r = 0; + + while (r < nums.length) { + int count = 1; + while (r + 1 < nums.length && nums[r] == nums[r + 1]) { + r++; + count++; + } + + for (int i = 0; i < Math.min(2, count); i++) { + nums[l] = nums[r]; + l++; + } + r++; + } + + return l; + } +} +``` + +```cpp +class Solution { +public: + int removeDuplicates(vector& nums) { + int l = 0, r = 0; + + while (r < nums.size()) { + int count = 1; + while (r + 1 < nums.size() && nums[r] == nums[r + 1]) { + r++; + count++; + } + + for (int i = 0; i < min(2, count); i++) { + nums[l] = nums[r]; + l++; + } + r++; + } + + return l; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + removeDuplicates(nums) { + let l = 0, r = 0; + + while (r < nums.length) { + let count = 1; + while (r + 1 < nums.length && nums[r] === nums[r + 1]) { + r++; + count++; + } + + for (let i = 0; i < Math.min(2, count); i++) { + nums[l] = nums[r]; + l++; + } + r++; + } + + return l; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. + +--- + +## 4. Two Pointers (Optimal) + +::tabs-start + +```python +class Solution: + def removeDuplicates(self, nums: List[int]) -> int: + l = 0 + for num in nums: + if l < 2 or num != nums[l - 2]: + nums[l] = num + l += 1 + return l +``` + +```java +public class Solution { + public int removeDuplicates(int[] nums) { + int l = 0; + for (int num : nums) { + if (l < 2 || num != nums[l - 2]) { + nums[l] = num; + l++; + } + } + return l; + } +} +``` + +```cpp +class Solution { +public: + int removeDuplicates(vector& nums) { + int l = 0; + for (int num : nums) { + if (l < 2 || num != nums[l - 2]) { + nums[l] = num; + l++; + } + } + return l; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + removeDuplicates(nums) { + let l = 0; + for (let num of nums) { + if (l < 2 || num !== nums[l - 2]) { + nums[l] = num; + l++; + } + } + return l; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. \ No newline at end of file diff --git a/articles/sequential-digits.md b/articles/sequential-digits.md new file mode 100644 index 000000000..b1f6bb8b8 --- /dev/null +++ b/articles/sequential-digits.md @@ -0,0 +1,579 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def sequentialDigits(self, low: int, high: int) -> List[int]: + res = [] + + for num in range(low, high + 1): + s = str(num) + flag = True + for i in range(1, len(s)): + if ord(s[i]) - ord(s[i - 1]) != 1: + flag = False + break + if flag: + res.append(num) + + return res +``` + +```java +public class Solution { + public List sequentialDigits(int low, int high) { + List res = new ArrayList<>(); + for (int num = low; num <= high; num++) { + String s = String.valueOf(num); + boolean flag = true; + for (int i = 1; i < s.length(); i++) { + if (s.charAt(i) - s.charAt(i - 1) != 1) { + flag = false; + break; + } + } + if (flag) { + res.add(num); + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + vector sequentialDigits(int low, int high) { + vector res; + for (int num = low; num <= high; num++) { + string s = to_string(num); + bool flag = true; + for (int i = 1; i < s.size(); i++) { + if (s[i] - s[i - 1] != 1) { + flag = false; + break; + } + } + if (flag) { + res.push_back(num); + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} low + * @param {number} high + * @return {number[]} + */ + sequentialDigits(low, high) { + const res = []; + for (let num = low; num <= high; num++) { + const s = num.toString(); + let flag = true; + for (let i = 1; i < s.length; i++) { + if (s.charCodeAt(i) - s.charCodeAt(i - 1) !== 1) { + flag = false; + break; + } + } + if (flag) { + res.push(num); + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ + +--- + +## 2. Simulation + +::tabs-start + +```python +class Solution: + def sequentialDigits(self, low: int, high: int) -> List[int]: + res = [] + low_digit, high_digit = len(str(low)), len(str(high)) + + for digits in range(low_digit, high_digit + 1): + for start in range(1, 10): + if start + digits > 10: + break + num = start + prev = start + for i in range(digits - 1): + num = num * 10 + prev += 1 + num += prev + if low <= num <= high: + res.append(num) + return res +``` + +```java +public class Solution { + public List sequentialDigits(int low, int high) { + List res = new ArrayList<>(); + int lowDigit = String.valueOf(low).length(); + int highDigit = String.valueOf(high).length(); + + for (int digits = lowDigit; digits <= highDigit; digits++) { + for (int start = 1; start < 10; start++) { + if (start + digits > 10) { + break; + } + int num = start; + int prev = start; + for (int i = 1; i < digits; i++) { + num = num * 10 + (++prev); + } + if (num >= low && num <= high) { + res.add(num); + } + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + vector sequentialDigits(int low, int high) { + vector res; + int lowDigit = to_string(low).length(); + int highDigit = to_string(high).length(); + + for (int digits = lowDigit; digits <= highDigit; digits++) { + for (int start = 1; start < 10; start++) { + if (start + digits > 10) { + break; + } + int num = start; + int prev = start; + for (int i = 1; i < digits; i++) { + num = num * 10 + (++prev); + } + if (num >= low && num <= high) { + res.push_back(num); + } + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} low + * @param {number} high + * @return {number[]} + */ + sequentialDigits(low, high) { + const res = []; + const lowDigit = low.toString().length; + const highDigit = high.toString().length; + + for (let digits = lowDigit; digits <= highDigit; digits++) { + for (let start = 1; start < 10; start++) { + if (start + digits > 10) { + break; + } + let num = start; + let prev = start; + for (let i = 1; i < digits; i++) { + num = num * 10 + (++prev); + } + if (num >= low && num <= high) { + res.push(num); + } + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(1)$ +* Space complexity: $O(1)$ + +> Since, we have at most $36$ valid numbers as per the given constraints. + +--- + +## 3. Breadth First Search + +::tabs-start + +```python +class Solution: + def sequentialDigits(self, low: int, high: int) -> List[int]: + res = [] + queue = deque(range(1, 10)) + + while queue: + n = queue.popleft() + if n > high: + continue + if low <= n <= high: + res.append(n) + ones = n % 10 + if ones < 9: + queue.append(n * 10 + (ones + 1)) + + return res +``` + +```java +public class Solution { + public List sequentialDigits(int low, int high) { + List res = new ArrayList<>(); + Queue queue = new LinkedList<>(); + + for (int i = 1; i < 10; i++) { + queue.add(i); + } + + while (!queue.isEmpty()) { + int n = queue.poll(); + if (n > high) { + continue; + } + if (n >= low && n <= high) { + res.add(n); + } + int ones = n % 10; + if (ones < 9) { + queue.add(n * 10 + (ones + 1)); + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector sequentialDigits(int low, int high) { + vector res; + queue queue; + + for (int i = 1; i < 10; i++) { + queue.push(i); + } + + while (!queue.empty()) { + int n = queue.front(); + queue.pop(); + + if (n > high) { + continue; + } + if (n >= low && n <= high) { + res.push_back(n); + } + int ones = n % 10; + if (ones < 9) { + queue.push(n * 10 + (ones + 1)); + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} low + * @param {number} high + * @return {number[]} + */ + sequentialDigits(low, high) { + const res = []; + const queue = new Queue(); + for (let i = 1; i < 9; i++) { + queue.push(i); + } + + while (!queue.isEmpty()) { + const n = queue.pop(); + if (n > high) { + continue; + } + if (n >= low && n <= high) { + res.push(n); + } + const ones = n % 10; + if (ones < 9) { + queue.push(n * 10 + (ones + 1)); + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(1)$ +* Space complexity: $O(1)$ + +> Since, we have at most $36$ valid numbers as per the given constraints. + +--- + +## 4. Depth First Search + +::tabs-start + +```python +class Solution: + def sequentialDigits(self, low: int, high: int) -> List[int]: + res = [] + + def dfs(num): + if num > high: + return + if low <= num <= high: + res.append(num) + last_digit = num % 10 + if last_digit < 9: + dfs(num * 10 + (last_digit + 1)) + + for i in range(1, 10): + dfs(i) + + return sorted(res) +``` + +```java +public class Solution { + public List sequentialDigits(int low, int high) { + List res = new ArrayList<>(); + + for (int i = 1; i < 10; i++) { + dfs(i, low, high, res); + } + + Collections.sort(res); + return res; + } + + private void dfs(int num, int low, int high, List res) { + if (num > high) { + return; + } + if (num >= low) { + res.add(num); + } + int lastDigit = num % 10; + if (lastDigit < 9) { + dfs(num * 10 + (lastDigit + 1), low, high, res); + } + } +} +``` + +```cpp +class Solution { +public: + vector sequentialDigits(int low, int high) { + vector res; + for (int i = 1; i < 10; i++) { + dfs(i, low, high, res); + } + sort(res.begin(), res.end()); + return res; + } + +private: + void dfs(int num, int low, int high, vector& res) { + if (num > high) { + return; + } + if (num >= low) { + res.push_back(num); + } + int lastDigit = num % 10; + if (lastDigit < 9) { + dfs(num * 10 + (lastDigit + 1), low, high, res); + } + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} low + * @param {number} high + * @return {number[]} + */ + sequentialDigits(low, high) { + const res = []; + + const dfs = (num) => { + if (num > high) { + return; + } + if (num >= low) { + res.push(num); + } + const lastDigit = num % 10; + if (lastDigit < 9) { + dfs(num * 10 + (lastDigit + 1)); + } + }; + + for (let i = 1; i < 10; i++) { + dfs(i); + } + + return res.sort((a, b) => a - b); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(1)$ +* Space complexity: $O(1)$ + +> Since, we have at most $36$ valid numbers as per the given constraints. + +--- + +## 5. Sliding Window + +::tabs-start + +```python +class Solution: + def sequentialDigits(self, low: int, high: int) -> List[int]: + nums = "123456789" + res = [] + for d in range(2, 10): + for i in range(9 - d + 1): + num = int(nums[i: i + d]) + if num > high: + break + if low <= num <= high: + res.append(num) + return res +``` + +```java +public class Solution { + public List sequentialDigits(int low, int high) { + String nums = "123456789"; + List res = new ArrayList<>(); + + for (int d = 2; d <= 9; d++) { + for (int i = 0; i <= 9 - d; i++) { + int num = Integer.parseInt(nums.substring(i, i + d)); + if (num > high) { + break; + } + if (num >= low && num <= high) { + res.add(num); + } + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector sequentialDigits(int low, int high) { + string nums = "123456789"; + vector res; + + for (int d = 2; d <= 9; d++) { + for (int i = 0; i <= 9 - d; i++) { + int num = stoi(nums.substr(i, d)); + if (num > high) { + break; + } + if (num >= low && num <= high) { + res.push_back(num); + } + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} low + * @param {number} high + * @return {number[]} + */ + sequentialDigits(low, high) { + const nums = "123456789"; + const res = []; + + for (let d = 2; d <= 9; d++) { + for (let i = 0; i <= 9 - d; i++) { + const num = parseInt(nums.substring(i, i + d)); + if (num > high) { + break; + } + if (num >= low && num <= high) { + res.push(num); + } + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(1)$ +* Space complexity: $O(1)$ + +> Since, we have at most $36$ valid numbers as per the given constraints. \ No newline at end of file diff --git a/articles/subarray-product-less-than-k.md b/articles/subarray-product-less-than-k.md new file mode 100644 index 000000000..dd7807c14 --- /dev/null +++ b/articles/subarray-product-less-than-k.md @@ -0,0 +1,313 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def numSubarrayProductLessThanK(self, nums: List[int], k: int) -> int: + n, res = len(nums), 0 + + for i in range(n): + curProd = 1 + for j in range(i, n): + curProd *= nums[j] + if curProd >= k: + break + res += 1 + + return res +``` + +```java +public class Solution { + public int numSubarrayProductLessThanK(int[] nums, int k) { + int n = nums.length, res = 0; + + for (int i = 0; i < n; i++) { + int curProd = 1; + for (int j = i; j < n; j++) { + curProd *= nums[j]; + if (curProd >= k) break; + res++; + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int numSubarrayProductLessThanK(vector& nums, int k) { + int n = nums.size(), res = 0; + + for (int i = 0; i < n; i++) { + int curProd = 1; + for (int j = i; j < n; j++) { + curProd *= nums[j]; + if (curProd >= k) break; + res++; + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {number} + */ + numSubarrayProductLessThanK(nums, k) { + let n = nums.length, res = 0; + + for (let i = 0; i < n; i++) { + let curProd = 1; + for (let j = i; j < n; j++) { + curProd *= nums[j]; + if (curProd >= k) break; + res++; + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(1)$ + +--- + +## 2. Binary Search + +::tabs-start + +```python +class Solution: + def numSubarrayProductLessThanK(self, nums: List[int], k: int) -> int: + if k <= 1: + return 0 + + n = len(nums) + res = 0 + logs = [0] * (n + 1) + logK = log(k) + for i in range(n): + logs[i + 1] = logs[i] + log(nums[i]) + + for i in range(n): + l, r = i + 1, n + 1 + while l < r: + mid = (l + r) >> 1 + if logs[mid] < logs[i] + logK: + l = mid + 1 + else: + r = mid + + res += l - (i + 1) + + return res +``` + +```java +public class Solution { + public int numSubarrayProductLessThanK(int[] nums, int k) { + if (k <= 1) return 0; + + int n = nums.length, res = 0; + double[] logs = new double[n + 1]; + double logK = Math.log(k); + + for (int i = 0; i < n; i++) { + logs[i + 1] = logs[i] + Math.log(nums[i]); + } + + for (int i = 0; i < n; i++) { + int l = i + 1, r = n + 1; + while (l < r) { + int mid = (l + r) / 2; + if (logs[mid] < logs[i] + logK) { + l = mid + 1; + } else { + r = mid; + } + } + res += l - (i + 1); + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int numSubarrayProductLessThanK(vector& nums, int k) { + if (k <= 1) return 0; + + int n = nums.size(), res = 0; + vector logs(n + 1, 0); + double logK = log(k); + + for (int i = 0; i < n; i++) { + logs[i + 1] = logs[i] + log(nums[i]); + } + + for (int i = 0; i < n; i++) { + int l = i + 1, r = n + 1; + while (l < r) { + int mid = (l + r) / 2; + if (logs[mid] < logs[i] + logK) { + l = mid + 1; + } else { + r = mid; + } + } + res += l - (i + 1); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {number} + */ + numSubarrayProductLessThanK(nums, k) { + if (k <= 1) return 0; + + const n = nums.length; + let res = 0; + const logs = new Array(n + 1).fill(0); + const logK = Math.log(k); + + for (let i = 0; i < n; i++) { + logs[i + 1] = logs[i] + Math.log(nums[i]); + } + + for (let i = 0; i < n; i++) { + let l = i + 1, r = n + 1; + while (l < r) { + const mid = Math.floor((l + r) / 2); + if (logs[mid] < logs[i] + logK) { + l = mid + 1; + } else { + r = mid; + } + } + res += l - (i + 1); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Sliding Window + +::tabs-start + +```python +class Solution: + def numSubarrayProductLessThanK(self, nums: List[int], k: int) -> int: + res = 0 + l = 0 + product = 1 + for r in range(len(nums)): + product *= nums[r] + while l <= r and product >= k: + product //= nums[l] + l += 1 + res += (r - l + 1) + return res +``` + +```java +public class Solution { + public int numSubarrayProductLessThanK(int[] nums, int k) { + int res = 0, l = 0; + long product = 1; + for (int r = 0; r < nums.length; r++) { + product *= nums[r]; + while (l <= r && product >= k) { + product /= nums[l++]; + } + res += (r - l + 1); + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int numSubarrayProductLessThanK(vector& nums, int k) { + int res = 0, l = 0; + long long product = 1; + for (int r = 0; r < nums.size(); r++) { + product *= nums[r]; + while (l <= r && product >= k) { + product /= nums[l++]; + } + res += (r - l + 1); + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {number} + */ + numSubarrayProductLessThanK(nums, k) { + let res = 0, l = 0, product = 1; + for (let r = 0; r < nums.length; r++) { + product *= nums[r]; + while (l <= r && product >= k) { + product = Math.floor(product / nums[l++]); + } + res += (r - l + 1); + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/subarrays-with-k-different-integers.md b/articles/subarrays-with-k-different-integers.md new file mode 100644 index 000000000..26328205c --- /dev/null +++ b/articles/subarrays-with-k-different-integers.md @@ -0,0 +1,546 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def subarraysWithKDistinct(self, nums: List[int], k: int) -> int: + n, res = len(nums), 0 + + for i in range(n): + seen = set() + for j in range(i, n): + seen.add(nums[j]) + if len(seen) > k: + break + + if len(seen) == k: + res += 1 + + return res +``` + +```java +public class Solution { + public int subarraysWithKDistinct(int[] nums, int k) { + int n = nums.length, res = 0; + + for (int i = 0; i < n; i++) { + Set seen = new HashSet<>(); + for (int j = i; j < n; j++) { + seen.add(nums[j]); + if (seen.size() > k) { + break; + } + if (seen.size() == k) { + res++; + } + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int subarraysWithKDistinct(vector& nums, int k) { + int n = nums.size(), res = 0; + + for (int i = 0; i < n; i++) { + unordered_set seen; + for (int j = i; j < n; j++) { + seen.insert(nums[j]); + if (seen.size() > k) { + break; + } + if (seen.size() == k) { + res++; + } + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {number} + */ + subarraysWithKDistinct(nums, k) { + let n = nums.length, res = 0; + + for (let i = 0; i < n; i++) { + let seen = new Set(); + for (let j = i; j < n; j++) { + seen.add(nums[j]); + if (seen.size > k) { + break; + } + if (seen.size === k) { + res++; + } + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n)$ + +--- + +## 2. Sliding Window + +::tabs-start + +```python +class Solution: + def subarraysWithKDistinct(self, nums: List[int], k: int) -> int: + + def atMostK(k): + count = defaultdict(int) + res = l = 0 + + for r in range(len(nums)): + count[nums[r]] += 1 + if count[nums[r]] == 1: + k -= 1 + + while k < 0: + count[nums[l]] -= 1 + if count[nums[l]] == 0: + k += 1 + l += 1 + + res += (r - l + 1) + + return res + + return atMostK(k) - atMostK(k - 1) +``` + +```java +public class Solution { + public int subarraysWithKDistinct(int[] nums, int k) { + return atMostK(nums, k) - atMostK(nums, k - 1); + } + + private int atMostK(int[] nums, int k) { + HashMap count = new HashMap<>(); + int res = 0, l = 0; + + for (int r = 0; r < nums.length; r++) { + count.put(nums[r], count.getOrDefault(nums[r], 0) + 1); + if (count.get(nums[r]) == 1) { + k--; + } + + while (k < 0) { + count.put(nums[l], count.get(nums[l]) - 1); + if (count.get(nums[l]) == 0) { + k++; + } + l++; + } + + res += (r - l + 1); + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int subarraysWithKDistinct(vector& nums, int k) { + return atMostK(nums, k) - atMostK(nums, k - 1); + } + +private: + int atMostK(vector& nums, int k) { + unordered_map count; + int res = 0, l = 0; + + for (int r = 0; r < nums.size(); r++) { + count[nums[r]]++; + if (count[nums[r]] == 1) { + k--; + } + + while (k < 0) { + count[nums[l]]--; + if (count[nums[l]] == 0) { + k++; + } + l++; + } + + res += (r - l + 1); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {number} + */ + subarraysWithKDistinct(nums, k) { + const atMostK = (k) => { + const count = new Map(); + let res = 0, l = 0; + + for (let r = 0; r < nums.length; r++) { + count.set(nums[r], (count.get(nums[r]) || 0) + 1); + if (count.get(nums[r]) === 1) { + k--; + } + + while (k < 0) { + count.set(nums[l], count.get(nums[l]) - 1); + if (count.get(nums[l]) === 0) { + k++; + } + l++; + } + + res += (r - l + 1); + } + + return res; + }; + + return atMostK(k) - atMostK(k - 1); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Slidingt Window (One Pass) - I + +::tabs-start + +```python +class Solution: + def subarraysWithKDistinct(self, nums: List[int], k: int) -> int: + count = defaultdict(int) + res = 0 + l_far = 0 + l_near = 0 + + for r in range(len(nums)): + count[nums[r]] += 1 + + while len(count) > k: + count[nums[l_near]] -= 1 + if count[nums[l_near]] == 0: + count.pop(nums[l_near]) + l_near += 1 + l_far = l_near + + while count[nums[l_near]] > 1: + count[nums[l_near]] -= 1 + l_near += 1 + + if len(count) == k: + res += l_near - l_far + 1 + + return res +``` + +```java +public class Solution { + public int subarraysWithKDistinct(int[] nums, int k) { + HashMap count = new HashMap<>(); + int res = 0, l_far = 0, l_near = 0; + + for (int r = 0; r < nums.length; r++) { + count.put(nums[r], count.getOrDefault(nums[r], 0) + 1); + + while (count.size() > k) { + count.put(nums[l_near], count.get(nums[l_near]) - 1); + if (count.get(nums[l_near]) == 0) { + count.remove(nums[l_near]); + } + l_near++; + l_far = l_near; + } + + while (count.get(nums[l_near]) > 1) { + count.put(nums[l_near], count.get(nums[l_near]) - 1); + l_near++; + } + + if (count.size() == k) { + res += l_near - l_far + 1; + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int subarraysWithKDistinct(vector& nums, int k) { + unordered_map count; + int res = 0, l_far = 0, l_near = 0; + + for (int r = 0; r < nums.size(); r++) { + count[nums[r]]++; + + while (count.size() > k) { + count[nums[l_near]]--; + if (count[nums[l_near]] == 0) { + count.erase(nums[l_near]); + } + l_near++; + l_far = l_near; + } + + while (count[nums[l_near]] > 1) { + count[nums[l_near]]--; + l_near++; + } + + if (count.size() == k) { + res += l_near - l_far + 1; + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {number} + */ + subarraysWithKDistinct(nums, k) { + const count = new Map(); + let res = 0, l_far = 0, l_near = 0; + + for (let r = 0; r < nums.length; r++) { + count.set(nums[r], (count.get(nums[r]) || 0) + 1); + + while (count.size > k) { + count.set(nums[l_near], count.get(nums[l_near]) - 1); + if (count.get(nums[l_near]) === 0) { + count.delete(nums[l_near]); + } + l_near++; + } + + while (count.get(nums[l_near]) > 1) { + count.set(nums[l_near], count.get(nums[l_near]) - 1); + l_near++; + } + + if (count.size === k) { + res += l_near - l_far + 1; + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 4. Sliding Window (One Pass) - II + +::tabs-start + +```python +class Solution: + def subarraysWithKDistinct(self, nums: List[int], k: int) -> int: + n = len(nums) + count = [0] * (n + 1) + res = l = cnt = 0 + + for r in range(n): + count[nums[r]] += 1 + if count[nums[r]] == 1: + k -= 1 + + if k < 0: + count[nums[l]] -= 1 + l += 1 + k += 1 + cnt = 0 + + if k == 0: + while count[nums[l]] > 1: + count[nums[l]] -= 1 + l += 1 + cnt += 1 + + res += (cnt + 1) + + return res +``` + +```java +public class Solution { + public int subarraysWithKDistinct(int[] nums, int k) { + int n = nums.length; + int[] count = new int[n + 1]; + int res = 0, l = 0, cnt = 0; + + for (int r = 0; r < n; r++) { + count[nums[r]]++; + if (count[nums[r]] == 1) { + k--; + } + + if (k < 0) { + count[nums[l]]--; + l++; + k++; + cnt = 0; + } + + if (k == 0) { + while (count[nums[l]] > 1) { + count[nums[l]]--; + l++; + cnt++; + } + + res += (cnt + 1); + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int subarraysWithKDistinct(vector& nums, int k) { + int n = nums.size(); + vector count(n + 1, 0); + int res = 0, l = 0, cnt = 0; + + for (int r = 0; r < n; r++) { + count[nums[r]]++; + if (count[nums[r]] == 1) { + k--; + } + + if (k < 0) { + count[nums[l]]--; + l++; + k++; + cnt = 0; + } + + if (k == 0) { + while (count[nums[l]] > 1) { + count[nums[l]]--; + l++; + cnt++; + } + + res += (cnt + 1); + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {number} + */ + subarraysWithKDistinct(nums, k) { + const n = nums.length; + const count = new Array(n + 1).fill(0); + let res = 0, l = 0, cnt = 0; + + for (let r = 0; r < n; r++) { + count[nums[r]]++; + if (count[nums[r]] === 1) { + k--; + } + + if (k < 0) { + count[nums[l]]--; + l++; + k++; + cnt = 0; + } + + if (k === 0) { + while (count[nums[l]] > 1) { + count[nums[l]]--; + l++; + cnt++; + } + + res += (cnt + 1); + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ \ No newline at end of file From af30f2feea200efb1ffd03c535ff4c057aa2a0b2 Mon Sep 17 00:00:00 2001 From: Sri Hari <94112314+Srihari2222@users.noreply.github.com> Date: Fri, 31 Jan 2025 02:20:14 +0530 Subject: [PATCH 31/45] Sri Hari: Batch-5/Neetcode-ALL/Added-articles (#3831) * Batch-5/Neetcode-ALL/Added-articles * Batch-5/Neetcode-ALL/Added-articles * Batch-5/Neetcode-ALL/Added-articles * Batch-5/Neetcode-ALL/Added-articles * Batch-5/Neetcode-ALL/Added-articles * Batch-5/Neetcode-ALL/Added-articles * Batch-5/Neetcode-ALL/Added-articles --- articles/132-pattern.md | 420 +++++++ articles/check-if-move-is-legal.md | 298 +++++ .../find-closest-node-to-given-two-nodes.md | 669 +++++++++++ ...ast-position-of-element-in-sorted-array.md | 449 +++++++ articles/find-peak-element.md | 341 ++++++ articles/flatten-nested-list-iterator.md | 579 +++++++++ articles/make-the-string-great.md | 2 +- .../maximum-number-of-removable-characters.md | 451 +++++++ articles/maximum-twin-sum-of-a-linked-list.md | 450 +++++++ ...inimize-the-maximum-difference-of-pairs.md | 561 +++++++++ articles/partition-list.md | 324 +++++ ...lating-next-right-pointers-in-each-node.md | 700 +++++++++++ articles/redundant-connection.md | 2 +- ...ve-all-adjacent-duplicates-in-string-ii.md | 467 +++++++ articles/remove-k-digits.md | 344 ++++++ articles/removing-stars-from-a-string.md | 357 ++++++ articles/search-suggestions-system.md | 589 +++++++++ articles/shortest-bridge.md | 1068 +++++++++++++++++ articles/shortest-path-in-binary-matrix.md | 475 ++++++++ articles/single-element-in-a-sorted-array.md | 438 +++++++ articles/sort-list.md | 730 +++++++++++ .../successful-pairs-of-spells-and-potions.md | 461 +++++++ articles/sum-of-subarray-minimums.md | 596 +++++++++ articles/swap-nodes-in-pairs.md | 419 +++++++ articles/swapping-nodes-in-a-linked-list.md | 785 ++++++++++++ articles/valid-perfect-square.md | 447 +++++++ articles/validate-stack-sequences.md | 160 +++ 27 files changed, 12580 insertions(+), 2 deletions(-) create mode 100644 articles/132-pattern.md create mode 100644 articles/check-if-move-is-legal.md create mode 100644 articles/find-closest-node-to-given-two-nodes.md create mode 100644 articles/find-first-and-last-position-of-element-in-sorted-array.md create mode 100644 articles/find-peak-element.md create mode 100644 articles/flatten-nested-list-iterator.md create mode 100644 articles/maximum-number-of-removable-characters.md create mode 100644 articles/maximum-twin-sum-of-a-linked-list.md create mode 100644 articles/minimize-the-maximum-difference-of-pairs.md create mode 100644 articles/partition-list.md create mode 100644 articles/populating-next-right-pointers-in-each-node.md create mode 100644 articles/remove-all-adjacent-duplicates-in-string-ii.md create mode 100644 articles/remove-k-digits.md create mode 100644 articles/removing-stars-from-a-string.md create mode 100644 articles/search-suggestions-system.md create mode 100644 articles/shortest-bridge.md create mode 100644 articles/shortest-path-in-binary-matrix.md create mode 100644 articles/single-element-in-a-sorted-array.md create mode 100644 articles/sort-list.md create mode 100644 articles/successful-pairs-of-spells-and-potions.md create mode 100644 articles/sum-of-subarray-minimums.md create mode 100644 articles/swap-nodes-in-pairs.md create mode 100644 articles/swapping-nodes-in-a-linked-list.md create mode 100644 articles/valid-perfect-square.md create mode 100644 articles/validate-stack-sequences.md diff --git a/articles/132-pattern.md b/articles/132-pattern.md new file mode 100644 index 000000000..b0dc31957 --- /dev/null +++ b/articles/132-pattern.md @@ -0,0 +1,420 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def find132pattern(self, nums: List[int]) -> bool: + n = len(nums) + + for k in range(2, n): + for j in range(k - 1, 0, -1): + if nums[j] <= nums[k]: + continue + + for i in range(j - 1, -1, -1): + if nums[i] < nums[k]: + return True + + return False +``` + +```java +public class Solution { + public boolean find132pattern(int[] nums) { + int n = nums.length; + + for (int k = 2; k < n; k++) { + for (int j = k - 1; j > 0; j--) { + if (nums[j] <= nums[k]) { + continue; + } + + for (int i = j - 1; i >= 0; i--) { + if (nums[i] < nums[k]) { + return true; + } + } + } + } + + return false; + } +} +``` + +```cpp +class Solution { +public: + bool find132pattern(vector& nums) { + int n = nums.size(); + + for (int k = 2; k < n; k++) { + for (int j = k - 1; j > 0; j--) { + if (nums[j] <= nums[k]) { + continue; + } + + for (int i = j - 1; i >= 0; i--) { + if (nums[i] < nums[k]) { + return true; + } + } + } + } + + return false; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {boolean} + */ + find132pattern(nums) { + let n = nums.length; + + for (let k = 2; k < n; k++) { + for (let j = k - 1; j > 0; j--) { + if (nums[j] <= nums[k]) { + continue; + } + + for (let i = j - 1; i >= 0; i--) { + if (nums[i] < nums[k]) { + return true; + } + } + } + } + + return false; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(1)$ + +--- + +## 2. Stack + +::tabs-start + +```python +class Solution: + def find132pattern(self, nums: List[int]) -> bool: + stack = [] # pair [num, minLeft], mono decreasing + curMin = nums[0] + + for i in range(1, len(nums)): + while stack and nums[i] >= stack[-1][0]: + stack.pop() + if stack and nums[i] > stack[-1][1]: + return True + + stack.append([nums[i], curMin]) + curMin = min(curMin, nums[i]) + + return False +``` + +```java +public class Solution { + public boolean find132pattern(int[] nums) { + Stack stack = new Stack<>(); // pair [num, minLeft] + int curMin = nums[0]; + + for (int i = 1; i < nums.length; i++) { + while (!stack.isEmpty() && nums[i] >= stack.peek()[0]) { + stack.pop(); + } + if (!stack.isEmpty() && nums[i] > stack.peek()[1]) { + return true; + } + + stack.push(new int[]{nums[i], curMin}); + curMin = Math.min(curMin, nums[i]); + } + + return false; + } +} +``` + +```cpp +class Solution { +public: + bool find132pattern(vector& nums) { + stack> stack; // pair + int curMin = nums[0]; + + for (int i = 1; i < nums.size(); i++) { + while (!stack.empty() && nums[i] >= stack.top().first) { + stack.pop(); + } + if (!stack.empty() && nums[i] > stack.top().second) { + return true; + } + + stack.push({nums[i], curMin}); + curMin = min(curMin, nums[i]); + } + + return false; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {boolean} + */ + find132pattern(nums) { + const stack = []; // pair [num, minLeft] + let curMin = nums[0]; + + for (let i = 1; i < nums.length; i++) { + while (stack.length > 0 && nums[i] >= stack[stack.length - 1][0]) { + stack.pop(); + } + if (stack.length > 0 && nums[i] > stack[stack.length - 1][1]) { + return true; + } + + stack.push([nums[i], curMin]); + curMin = Math.min(curMin, nums[i]); + } + + return false; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Stack (Optimal) + +::tabs-start + +```python +class Solution: + def find132pattern(self, nums: List[int]) -> bool: + stack, k = [], float('-inf') + + for i in range(len(nums) - 1, -1, -1): + if nums[i] < k: + return True + + while stack and stack[-1] < nums[i]: + k = stack.pop() + stack.append(nums[i]) + + return False +``` + +```java +public class Solution { + public boolean find132pattern(int[] nums) { + Stack stack = new Stack<>(); + int k = Integer.MIN_VALUE; + + for (int i = nums.length - 1; i >= 0; i--) { + if (nums[i] < k) { + return true; + } + + while (!stack.isEmpty() && stack.peek() < nums[i]) { + k = stack.pop(); + } + stack.push(nums[i]); + } + + return false; + } +} +``` + +```cpp +class Solution { +public: + bool find132pattern(vector& nums) { + stack stack; + int k = INT_MIN; + + for (int i = nums.size() - 1; i >= 0; i--) { + if (nums[i] < k) { + return true; + } + + while (!stack.empty() && stack.top() < nums[i]) { + k = stack.top(); + stack.pop(); + } + stack.push(nums[i]); + } + + return false; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {boolean} + */ + find132pattern(nums) { + const stack = []; + let k = -Infinity; + + for (let i = nums.length - 1; i >= 0; i--) { + if (nums[i] < k) { + return true; + } + + while (stack.length > 0 && stack[stack.length - 1] < nums[i]) { + k = stack.pop(); + } + stack.push(nums[i]); + } + + return false; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 4. Two Pointers + +::tabs-start + +```python +class Solution: + def find132pattern(self, nums: List[int]) -> bool: + n = len(nums) + stkTop, k = n, float('-inf') + + for i in range(n - 1, -1, -1): + if nums[i] < k: + return True + + while stkTop < n and nums[i] > nums[stkTop]: + k = nums[stkTop] + stkTop += 1 + + stkTop -= 1 + nums[stkTop] = nums[i] + + return False +``` + +```java +public class Solution { + public boolean find132pattern(int[] nums) { + int n = nums.length; + int stkTop = n; + int k = Integer.MIN_VALUE; + + for (int i = n - 1; i >= 0; i--) { + if (nums[i] < k) { + return true; + } + + while (stkTop < n && nums[i] > nums[stkTop]) { + k = nums[stkTop++]; + } + + nums[--stkTop] = nums[i]; + } + + return false; + } +} +``` + +```cpp +class Solution { +public: + bool find132pattern(vector& nums) { + int n = nums.size(); + int stkTop = n; + int k = INT_MIN; + + for (int i = n - 1; i >= 0; i--) { + if (nums[i] < k) { + return true; + } + + while (stkTop < n && nums[i] > nums[stkTop]) { + k = nums[stkTop++]; + } + + nums[--stkTop] = nums[i]; + } + + return false; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {boolean} + */ + find132pattern(nums) { + const n = nums.length; + let stkTop = n; + let k = -Infinity; + + for (let i = n - 1; i >= 0; i--) { + if (nums[i] < k) { + return true; + } + + while (stkTop < n && nums[i] > nums[stkTop]) { + k = nums[stkTop++]; + } + + nums[--stkTop] = nums[i]; + } + + return false; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. \ No newline at end of file diff --git a/articles/check-if-move-is-legal.md b/articles/check-if-move-is-legal.md new file mode 100644 index 000000000..acc1d8b66 --- /dev/null +++ b/articles/check-if-move-is-legal.md @@ -0,0 +1,298 @@ +## 1. Iteration - I + +::tabs-start + +```python +class Solution: + def checkMove(self, board: List[List[str]], rMove: int, cMove: int, color: str) -> bool: + ROWS, COLS = len(board), len(board[0]) + direction = [[1, 0], [-1, 0], [0, 1], [0, -1], + [1, 1], [-1, -1], [1, -1], [-1, 1]] + + board[rMove][cMove] = color + + def legal(row, col, color, direc): + dr, dc = direc + row, col = row + dr, col + dc + length = 1 + + while 0 <= row < ROWS and 0 <= col < COLS: + length += 1 + if board[row][col] == ".": + return False + if board[row][col] == color: + return length >= 3 + row, col = row + dr, col + dc + return False + + for d in direction: + if legal(rMove, cMove, color, d): + return True + return False +``` + +```java +public class Solution { + public boolean checkMove(char[][] board, int rMove, int cMove, char color) { + int ROWS = board.length, COLS = board[0].length; + int[][] direction = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}, + {1, 1}, {-1, -1}, {1, -1}, {-1, 1}}; + + board[rMove][cMove] = color; + + for (int[] d : direction) { + if (legal(board, rMove, cMove, color, d)) { + return true; + } + } + return false; + } + + private boolean legal(char[][] board, int row, int col, char color, int[] direc) { + int ROWS = board.length, COLS = board[0].length; + int dr = direc[0], dc = direc[1]; + row += dr; + col += dc; + int length = 1; + + while (row >= 0 && row < ROWS && col >= 0 && col < COLS) { + length++; + if (board[row][col] == '.') { + return false; + } + if (board[row][col] == color) { + return length >= 3; + } + row += dr; + col += dc; + } + return false; + } +} +``` + +```cpp +class Solution { +public: + bool checkMove(vector>& board, int rMove, int cMove, char color) { + int ROWS = board.size(), COLS = board[0].size(); + vector> direction = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}, + {1, 1}, {-1, -1}, {1, -1}, {-1, 1}}; + + board[rMove][cMove] = color; + + for (auto& d : direction) { + if (legal(board, rMove, cMove, color, d)) { + return true; + } + } + return false; + } + +private: + bool legal(vector>& board, int row, int col, char color, vector& direc) { + int ROWS = board.size(), COLS = board[0].size(); + int dr = direc[0], dc = direc[1]; + row += dr; + col += dc; + int length = 1; + + while (row >= 0 && row < ROWS && col >= 0 && col < COLS) { + length++; + if (board[row][col] == '.') { + return false; + } + if (board[row][col] == color) { + return length >= 3; + } + row += dr; + col += dc; + } + return false; + } +}; +``` + +```javascript +class Solution { + /** + * @param {character[][]} board + * @param {number} rMove + * @param {number} cMove + * @param {character} color + * @return {boolean} + */ + checkMove(board, rMove, cMove, color) { + const ROWS = board.length, COLS = board[0].length; + const direction = [[1, 0], [-1, 0], [0, 1], [0, -1], + [1, 1], [-1, -1], [1, -1], [-1, 1]]; + + board[rMove][cMove] = color; + + const legal = (row, col, color, [dr, dc]) => { + row += dr; + col += dc; + let length = 1; + + while (row >= 0 && row < ROWS && col >= 0 && col < COLS) { + length++; + if (board[row][col] === ".") { + return false; + } + if (board[row][col] === color) { + return length >= 3; + } + row += dr; + col += dc; + } + return false; + }; + + for (let d of direction) { + if (legal(rMove, cMove, color, d)) { + return true; + } + } + return false; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(1)$ +* Space complexity: $O(1)$ + +--- + +## 2. Iteration - II + +::tabs-start + +```python +class Solution: + def checkMove(self, board: List[List[str]], rMove: int, cMove: int, color: str) -> bool: + ROWS, COLS = len(board), len(board[0]) + direction = [0, 1, 0, -1, 0, 1, 1, -1, -1, 1] + + board[rMove][cMove] = color + + for d in range(9): + length = 1 + row, col = rMove, cMove + while True: + row += direction[d] + col += direction[d + 1] + + if row < 0 or col < 0 or row >= ROWS or col >= COLS or board[row][col] == ".": + break + if board[row][col] == color: + if length > 1: + return True + break + length += 1 + + return False +``` + +```java +public class Solution { + public boolean checkMove(char[][] board, int rMove, int cMove, char color) { + int ROWS = board.length, COLS = board[0].length; + int[] direction = {0, 1, 0, -1, 0, 1, 1, -1, -1, 1}; + + board[rMove][cMove] = color; + + for (int d = 0; d < 9; d++) { + int row = rMove, col = cMove; + for (int length = 1; ; ++length) { + row += direction[d]; + col += direction[d + 1]; + + if (row < 0 || col < 0 || row >= ROWS || col >= COLS || board[row][col] == '.') + break; + if (board[row][col] == color) { + if (length > 1) + return true; + break; + } + } + } + return false; + } +} + +``` + +```cpp +class Solution { +public: + bool checkMove(vector>& board, int rMove, int cMove, char color) { + int ROWS = board.size(), COLS = board[0].size(); + int direction[10] = {0, 1, 0, -1, 0, 1, 1, -1, -1, 1}; + + board[rMove][cMove] = color; + + for (int d = 0; d < 9; ++d) { + int row = rMove, col = cMove; + for (int length = 1; ; ++length) { + row += direction[d]; + col += direction[d + 1]; + + if (row < 0 || col < 0 || row >= ROWS || col >= COLS || board[row][col] == '.') + break; + if (board[row][col] == color) { + if (length > 1) + return true; + break; + } + } + } + return false; + } +}; +``` + +```javascript +class Solution { + /** + * @param {character[][]} board + * @param {number} rMove + * @param {number} cMove + * @param {character} color + * @return {boolean} + */ + checkMove(board, rMove, cMove, color) { + const ROWS = board.length, COLS = board[0].length; + const direction = [0, 1, 0, -1, 0, 1, 1, -1, -1, 1]; + + board[rMove][cMove] = color; + + for (let d = 0; d < 9; d++) { + let row = rMove, col = cMove; + for (let length = 1; ; ++length) { + row += direction[d]; + col += direction[d + 1]; + + if (row < 0 || col < 0 || row >= ROWS || col >= COLS || board[row][col] === '.') + break; + if (board[row][col] === color) { + if (length > 1) + return true; + break; + } + } + } + return false; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(1)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/find-closest-node-to-given-two-nodes.md b/articles/find-closest-node-to-given-two-nodes.md new file mode 100644 index 000000000..52cb6d365 --- /dev/null +++ b/articles/find-closest-node-to-given-two-nodes.md @@ -0,0 +1,669 @@ +## 1. Breadth First Search + +::tabs-start + +```python +class Solution: + def closestMeetingNode(self, edges: list[int], node1: int, node2: int) -> int: + adj = defaultdict(list) + for i, nei in enumerate(edges): + adj[i].append(nei) + + def bfs(src, distMap): + q = deque([(src, 0)]) + distMap[src] = 0 + while q: + node, dist = q.popleft() + for nei in adj[node]: + if nei not in distMap: + q.append((nei, dist + 1)) + distMap[nei] = dist + 1 + + node1Dist, node2Dist = {}, {} + bfs(node1, node1Dist) + bfs(node2, node2Dist) + + res, resDist = -1, float("inf") + for i in range(len(edges)): + if i in node1Dist and i in node2Dist: + dist = max(node1Dist[i], node2Dist[i]) + if dist < resDist: + resDist, res = dist, i + + return res +``` + +```java +public class Solution { + public int closestMeetingNode(int[] edges, int node1, int node2) { + int n = edges.length; + List[] adj = new ArrayList[n]; + for (int i = 0; i < n; i++) adj[i] = new ArrayList<>(); + for (int i = 0; i < n; i++) { + if (edges[i] != -1) adj[i].add(edges[i]); + } + + int[] node1Dist = bfs(node1, n, adj); + int[] node2Dist = bfs(node2, n, adj); + + int res = -1, resDist = Integer.MAX_VALUE; + for (int i = 0; i < n; i++) { + if (node1Dist[i] != -1 && node2Dist[i] != -1) { + int dist = Math.max(node1Dist[i], node2Dist[i]); + if (dist < resDist) { + resDist = dist; + res = i; + } + } + } + return res; + } + + private int[] bfs(int src, int n, List[] adj) { + int[] distMap = new int[n]; + Arrays.fill(distMap, -1); + Queue q = new LinkedList<>(); + q.offer(new int[]{src, 0}); + distMap[src] = 0; + + while (!q.isEmpty()) { + int[] cur = q.poll(); + int node = cur[0], dist = cur[1]; + + for (int nei : adj[node]) { + if (distMap[nei] == -1) { + q.offer(new int[]{nei, dist + 1}); + distMap[nei] = dist + 1; + } + } + } + return distMap; + } +} +``` + +```cpp +class Solution { +public: + int closestMeetingNode(vector& edges, int node1, int node2) { + int n = edges.size(); + vector> adj(n); + for (int i = 0; i < n; i++) { + if (edges[i] != -1) adj[i].push_back(edges[i]); + } + + vector node1Dist = bfs(node1, n, adj); + vector node2Dist = bfs(node2, n, adj); + + int res = -1, resDist = INT_MAX; + for (int i = 0; i < n; i++) { + if (node1Dist[i] != -1 && node2Dist[i] != -1) { + int dist = max(node1Dist[i], node2Dist[i]); + if (dist < resDist) { + resDist = dist; + res = i; + } + } + } + return res; + } + +private: + vector bfs(int src, int n, vector>& adj) { + vector distMap(n, -1); + queue> q; + q.push({src, 0}); + distMap[src] = 0; + + while (!q.empty()) { + auto [node, dist] = q.front(); + q.pop(); + + for (int nei : adj[node]) { + if (distMap[nei] == -1) { + q.push({nei, dist + 1}); + distMap[nei] = dist + 1; + } + } + } + return distMap; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} edges + * @param {number} node1 + * @param {number} node2 + * @return {number} + */ + closestMeetingNode(edges, node1, node2) { + const n = edges.length; + const adj = Array.from({ length: n }, () => []); + for (let i = 0; i < n; i++) { + if (edges[i] !== -1) adj[i].push(edges[i]); + } + + const bfs = (src) => { + const distMap = Array(n).fill(-1); + const q = new Queue([[src, 0]]); + distMap[src] = 0; + + while (!q.isEmpty()) { + const [node, dist] = q.pop(); + for (const nei of adj[node]) { + if (distMap[nei] === -1) { + q.push([nei, dist + 1]); + distMap[nei] = dist + 1; + } + } + } + return distMap; + }; + + const node1Dist = bfs(node1); + const node2Dist = bfs(node2); + + let res = -1, resDist = Infinity; + for (let i = 0; i < n; i++) { + if (node1Dist[i] !== -1 && node2Dist[i] !== -1) { + let dist = Math.max(node1Dist[i], node2Dist[i]); + if (dist < resDist) { + resDist = dist; + res = i; + } + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Breadth First Search (Optimal) + +::tabs-start + +```python +class Solution: + def closestMeetingNode(self, edges: list[int], node1: int, node2: int) -> int: + n = len(edges) + + def bfs(src): + dist = [-1] * n + q = deque([src]) + dist[src] = 0 + + while q: + node = q.popleft() + nei = edges[node] + if nei == -1 or dist[nei] >= 0: + continue + q.append(nei) + dist[nei] = dist[node] + 1 + return dist + + node1Dist, node2Dist = bfs(node1), bfs(node2) + + res, resDist = -1, float("inf") + for i in range(n): + if node1Dist[i] != -1 and node2Dist[i] != -1: + dist = max(node1Dist[i], node2Dist[i]) + if dist < resDist: + resDist, res = dist, i + + return res +``` + +```java +public class Solution { + public int closestMeetingNode(int[] edges, int node1, int node2) { + int n = edges.length; + int[] node1Dist = bfs(node1, edges, n); + int[] node2Dist = bfs(node2, edges, n); + + int res = -1, resDist = Integer.MAX_VALUE; + for (int i = 0; i < n; i++) { + if (node1Dist[i] != -1 && node2Dist[i] != -1) { + int dist = Math.max(node1Dist[i], node2Dist[i]); + if (dist < resDist) { + resDist = dist; + res = i; + } + } + } + + return res; + } + + private int[] bfs(int src, int[] edges, int n) { + int[] dist = new int[n]; + Arrays.fill(dist, -1); + Queue q = new LinkedList<>(); + q.offer(src); + dist[src] = 0; + + while (!q.isEmpty()) { + int node = q.poll(); + int nei = edges[node]; + if (nei == -1 || dist[nei] != -1) { + continue; + } + + q.offer(nei); + dist[nei] = dist[node] + 1; + } + return dist; + } +} +``` + +```cpp +class Solution { +public: + int closestMeetingNode(vector& edges, int node1, int node2) { + int n = edges.size(); + vector node1Dist = bfs(node1, edges, n); + vector node2Dist = bfs(node2, edges, n); + + int res = -1, resDist = INT_MAX; + for (int i = 0; i < n; i++) { + if (node1Dist[i] != -1 && node2Dist[i] != -1) { + int dist = max(node1Dist[i], node2Dist[i]); + if (dist < resDist) { + resDist = dist; + res = i; + } + } + } + return res; + } + +private: + vector bfs(int src, vector& edges, int n) { + vector dist(n, -1); + queue q; + q.push(src); + dist[src] = 0; + + while (!q.empty()) { + int node = q.front(); + q.pop(); + int nei = edges[node]; + if (nei == -1 || dist[nei] != -1) { + continue; + } + + q.push(nei); + dist[nei] = dist[node] + 1; + } + return dist; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} edges + * @param {number} node1 + * @param {number} node2 + * @return {number} + */ + closestMeetingNode(edges, node1, node2) { + const n = edges.length; + + const bfs = (src) => { + const dist = Array(n).fill(-1); + const q = new Queue([src]); + dist[src] = 0; + + while (!q.isEmpty()) { + const node = q.pop(); + const nei = edges[node]; + if (nei === -1 || dist[nei] !== -1) { + continue; + } + + q.push(nei); + dist[nei] = dist[node] + 1; + } + return dist; + }; + + const node1Dist = bfs(node1); + const node2Dist = bfs(node2); + + let res = -1, resDist = Infinity; + for (let i = 0; i < n; i++) { + if (node1Dist[i] !== -1 && node2Dist[i] !== -1) { + let dist = Math.max(node1Dist[i], node2Dist[i]); + if (dist < resDist) { + resDist = dist; + res = i; + } + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Depth First Search + +::tabs-start + +```python +class Solution: + def closestMeetingNode(self, edges: List[int], node1: int, node2: int) -> int: + n = len(edges) + + def dfs(node, dist): + nei = edges[node] + if nei != -1 and dist[nei] == -1: + dist[nei] = dist[node] + 1 + dfs(nei, dist) + + node1Dist = [-1] * n + node2Dist = [-1] * n + node1Dist[node1] = node2Dist[node2] = 0 + + dfs(node1, node1Dist) + dfs(node2, node2Dist) + + res, resDist = -1, float("inf") + for i in range(n): + if min(node1Dist[i], node2Dist[i]) != -1: + dist = max(node1Dist[i], node2Dist[i]) + if dist < resDist: + resDist, res = dist, i + + return res +``` + +```java +public class Solution { + public int closestMeetingNode(int[] edges, int node1, int node2) { + int n = edges.length; + int[] node1Dist = new int[n]; + int[] node2Dist = new int[n]; + Arrays.fill(node1Dist, -1); + Arrays.fill(node2Dist, -1); + node1Dist[node1] = 0; + node2Dist[node2] = 0; + + dfs(node1, edges, node1Dist); + dfs(node2, edges, node2Dist); + + int res = -1, resDist = Integer.MAX_VALUE; + for (int i = 0; i < n; i++) { + if (Math.min(node1Dist[i], node2Dist[i]) != -1) { + int dist = Math.max(node1Dist[i], node2Dist[i]); + if (dist < resDist) { + resDist = dist; + res = i; + } + } + } + return res; + } + + private void dfs(int node, int[] edges, int[] dist) { + int nei = edges[node]; + if (nei != -1 && dist[nei] == -1) { + dist[nei] = dist[node] + 1; + dfs(nei, edges, dist); + } + } +} +``` + +```cpp +class Solution { +public: + int closestMeetingNode(vector& edges, int node1, int node2) { + int n = edges.size(); + vector node1Dist(n, -1), node2Dist(n, -1); + node1Dist[node1] = node2Dist[node2] = 0; + + dfs(node1, edges, node1Dist); + dfs(node2, edges, node2Dist); + + int res = -1, resDist = INT_MAX; + for (int i = 0; i < n; i++) { + if (min(node1Dist[i], node2Dist[i]) != -1) { + int dist = max(node1Dist[i], node2Dist[i]); + if (dist < resDist) { + resDist = dist; + res = i; + } + } + } + return res; + } + +private: + void dfs(int node, vector& edges, vector& dist) { + int nei = edges[node]; + if (nei != -1 && dist[nei] == -1) { + dist[nei] = dist[node] + 1; + dfs(nei, edges, dist); + } + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} edges + * @param {number} node1 + * @param {number} node2 + * @return {number} + */ + closestMeetingNode(edges, node1, node2) { + const n = edges.length; + + const dfs = (node, dist) => { + const nei = edges[node]; + if (nei !== -1 && dist[nei] === -1) { + dist[nei] = dist[node] + 1; + dfs(nei, dist); + } + }; + + const node1Dist = Array(n).fill(-1); + const node2Dist = Array(n).fill(-1); + node1Dist[node1] = 0; + node2Dist[node2] = 0; + + dfs(node1, node1Dist); + dfs(node2, node2Dist); + + let res = -1, resDist = Infinity; + for (let i = 0; i < n; i++) { + if (Math.min(node1Dist[i], node2Dist[i]) !== -1) { + const dist = Math.max(node1Dist[i], node2Dist[i]); + if (dist < resDist) { + resDist = dist; + res = i; + } + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 4. Iterative Depth First Search + +::tabs-start + +```python +class Solution: + def closestMeetingNode(self, edges: list[int], node1: int, node2: int) -> int: + n = len(edges) + + def dfs(node): + dist = [-1] * n + dist[node] = 0 + while edges[node] != -1 and dist[edges[node]] == -1: + nei = edges[node] + dist[nei] = dist[node] + 1 + node = nei + return dist + + + node1Dist, node2Dist = dfs(node1), dfs(node2) + res, resDist = -1, float("inf") + for i in range(n): + if min(node1Dist[i], node2Dist[i]) != -1: + dist = max(node1Dist[i], node2Dist[i]) + if dist < resDist: + resDist, res = dist, i + return res +``` + +```java +public class Solution { + public int closestMeetingNode(int[] edges, int node1, int node2) { + int n = edges.length; + int[] node1Dist = dfs(node1, edges, n); + int[] node2Dist = dfs(node2, edges, n); + + int res = -1, resDist = Integer.MAX_VALUE; + for (int i = 0; i < n; i++) { + if (Math.min(node1Dist[i], node2Dist[i]) != -1) { + int dist = Math.max(node1Dist[i], node2Dist[i]); + if (dist < resDist) { + resDist = dist; + res = i; + } + } + } + return res; + } + + private int[] dfs(int node, int[] edges, int n) { + int[] dist = new int[n]; + Arrays.fill(dist, -1); + dist[node] = 0; + while (edges[node] != -1 && dist[edges[node]] == -1) { + int nei = edges[node]; + dist[nei] = dist[node] + 1; + node = nei; + } + return dist; + } +} +``` + +```cpp +class Solution { +public: + int closestMeetingNode(vector& edges, int node1, int node2) { + int n = edges.size(); + vector node1Dist = dfs(node1, edges, n); + vector node2Dist = dfs(node2, edges, n); + + int res = -1, resDist = INT_MAX; + for (int i = 0; i < n; i++) { + if (min(node1Dist[i], node2Dist[i]) != -1) { + int dist = max(node1Dist[i], node2Dist[i]); + if (dist < resDist) { + resDist = dist; + res = i; + } + } + } + return res; + } + +private: + vector dfs(int node, vector& edges, int n) { + vector dist(n, -1); + dist[node] = 0; + while (edges[node] != -1 && dist[edges[node]] == -1) { + int nei = edges[node]; + dist[nei] = dist[node] + 1; + node = nei; + } + return dist; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} edges + * @param {number} node1 + * @param {number} node2 + * @return {number} + */ + closestMeetingNode(edges, node1, node2) { + const n = edges.length; + + const dfs = (node) => { + const dist = Array(n).fill(-1); + dist[node] = 0; + while (edges[node] !== -1 && dist[edges[node]] === -1) { + const nei = edges[node]; + dist[nei] = dist[node] + 1; + node = nei; + } + return dist; + }; + + const node1Dist = dfs(node1); + const node2Dist = dfs(node2); + + let res = -1, resDist = Infinity; + for (let i = 0; i < n; i++) { + if (Math.min(node1Dist[i], node2Dist[i]) !== -1) { + const dist = Math.max(node1Dist[i], node2Dist[i]); + if (dist < resDist) { + resDist = dist; + res = i; + } + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/find-first-and-last-position-of-element-in-sorted-array.md b/articles/find-first-and-last-position-of-element-in-sorted-array.md new file mode 100644 index 000000000..e2d66f092 --- /dev/null +++ b/articles/find-first-and-last-position-of-element-in-sorted-array.md @@ -0,0 +1,449 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def searchRange(self, nums: List[int], target: int) -> List[int]: + res = [-1, -1] + + for i, num in enumerate(nums): + if num == target: + if res[0] == -1: + res[0] = res[1] = i + else: + res[1] = i + + return res +``` + +```java +public class Solution { + public int[] searchRange(int[] nums, int target) { + int[] res = {-1, -1}; + + for (int i = 0; i < nums.length; i++) { + if (nums[i] == target) { + if (res[0] == -1) { + res[0] = res[1] = i; + } else { + res[1] = i; + } + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector searchRange(vector& nums, int target) { + vector res = {-1, -1}; + + for (int i = 0; i < nums.size(); i++) { + if (nums[i] == target) { + if (res[0] == -1) { + res[0] = res[1] = i; + } else { + res[1] = i; + } + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} target + * @return {number[]} + */ + searchRange(nums, target) { + let res = [-1, -1]; + + for (let i = 0; i < nums.length; i++) { + if (nums[i] === target) { + if (res[0] === -1) { + res[0] = res[1] = i; + } else { + res[1] = i; + } + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ + +--- + +## 2. Binary Search - I + +::tabs-start + +```python +class Solution: + def searchRange(self, nums: List[int], target: int) -> List[int]: + left = self.binarySearch(nums, target, True) + right = self.binarySearch(nums, target, False) + return [left, right] + + def binarySearch(self, nums, target, leftBias): + l, r = 0, len(nums) - 1 + i = -1 + while l <= r: + m = (l + r) // 2 + if target > nums[m]: + l = m + 1 + elif target < nums[m]: + r = m - 1 + else: + i = m + if leftBias: + r = m - 1 + else: + l = m + 1 + return i +``` + +```java +public class Solution { + public int[] searchRange(int[] nums, int target) { + int left = binarySearch(nums, target, true); + int right = binarySearch(nums, target, false); + return new int[]{left, right}; + } + + private int binarySearch(int[] nums, int target, boolean leftBias) { + int l = 0, r = nums.length - 1, i = -1; + while (l <= r) { + int m = (l + r) / 2; + if (target > nums[m]) { + l = m + 1; + } else if (target < nums[m]) { + r = m - 1; + } else { + i = m; + if (leftBias) { + r = m - 1; + } else { + l = m + 1; + } + } + } + return i; + } +} +``` + +```cpp +class Solution { +public: + vector searchRange(vector& nums, int target) { + int left = binarySearch(nums, target, true); + int right = binarySearch(nums, target, false); + return {left, right}; + } + +private: + int binarySearch(vector& nums, int target, bool leftBias) { + int l = 0, r = nums.size() - 1, i = -1; + while (l <= r) { + int m = (l + r) / 2; + if (target > nums[m]) { + l = m + 1; + } else if (target < nums[m]) { + r = m - 1; + } else { + i = m; + if (leftBias) { + r = m - 1; + } else { + l = m + 1; + } + } + } + return i; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} target + * @return {number[]} + */ + searchRange(nums, target) { + let left = this.binarySearch(nums, target, true); + let right = this.binarySearch(nums, target, false); + return [left, right]; + } + + /** + * @param {number[]} nums + * @param {number} target + * @param {boolean} leftBias + * @return {number} + */ + binarySearch(nums, target, leftBias) { + let l = 0, r = nums.length - 1, i = -1; + while (l <= r) { + let m = Math.floor((l + r) / 2); + if (target > nums[m]) { + l = m + 1; + } else if (target < nums[m]) { + r = m - 1; + } else { + i = m; + if (leftBias) { + r = m - 1; + } else { + l = m + 1; + } + } + } + return i; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(\log n)$ +* Space complexity: $O(1)$ + +--- + +## 3. Binary Search - II + +::tabs-start + +```python +class Solution: + def searchRange(self, nums: List[int], target: int) -> List[int]: + n = len(nums) + + def binarySearch(target): + l, r = 0, n + while l < r: + m = l + (r - l) // 2 + if nums[m] >= target: + r = m + else: + l = m + 1 + return l + + start = binarySearch(target) + if start == n or nums[start] != target: + return [-1, -1] + + return [start, binarySearch(target + 1) - 1] +``` + +```java +public class Solution { + public int[] searchRange(int[] nums, int target) { + int n = nums.length; + + int start = binarySearch(nums, target, n); + if (start == n || nums[start] != target) { + return new int[]{-1, -1}; + } + + return new int[]{start, binarySearch(nums, target + 1, n) - 1}; + } + + private int binarySearch(int[] nums, int target, int n) { + int l = 0, r = n; + while (l < r) { + int m = l + (r - l) / 2; + if (nums[m] >= target) { + r = m; + } else { + l = m + 1; + } + } + return l; + } +} +``` + +```cpp +class Solution { +public: + vector searchRange(vector& nums, int target) { + int n = nums.size(); + + int start = binarySearch(nums, target, n); + if (start == n || nums[start] != target) { + return {-1, -1}; + } + + return {start, binarySearch(nums, target + 1, n) - 1}; + } + +private: + int binarySearch(vector& nums, int target, int n) { + int l = 0, r = n; + while (l < r) { + int m = l + (r - l) / 2; + if (nums[m] >= target) { + r = m; + } else { + l = m + 1; + } + } + return l; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} target + * @return {number[]} + */ + searchRange(nums, target) { + const n = nums.length; + + const binarySearch = (target) => { + let l = 0, r = n; + while (l < r) { + let m = Math.floor(l + (r - l) / 2); + if (nums[m] >= target) { + r = m; + } else { + l = m + 1; + } + } + return l; + }; + + let start = binarySearch(target); + if (start === n || nums[start] !== target) { + return [-1, -1]; + } + + return [start, binarySearch(target + 1) - 1]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(\log n)$ +* Space complexity: $O(1)$ + +--- + +## 4. In-Built Function + +::tabs-start + +```python +class Solution: + def searchRange(self, nums: List[int], target: int) -> List[int]: + left = bisect.bisect_left(nums, target) + if left >= len(nums) or nums[left] != target: + return [-1, -1] + + right = bisect.bisect_right(nums, target) - 1 + return [left, right] +``` + +```java +public class Solution { + public int[] searchRange(int[] nums, int target) { + int left = findLeft(nums, target); + if (left == -1) { + return new int[]{-1, -1}; + } + + int right = findRight(nums, target); + return new int[]{left, right}; + } + + private int findLeft(int[] nums, int target) { + // O(n) time in worst case + int index = Arrays.binarySearch(nums, target); + if (index < 0) return -1; + + while (index > 0 && nums[index - 1] == target) { + index--; + } + return index; + } + + private int findRight(int[] nums, int target) { + // O(n) time in worst case + int index = Arrays.binarySearch(nums, target); + if (index < 0) return -1; + + while (index < nums.length - 1 && nums[index + 1] == target) { + index++; + } + return index; + } +} +``` + +```cpp +class Solution { +public: + vector searchRange(vector& nums, int target) { + int left = lower_bound(nums.begin(), nums.end(), target) - nums.begin(); + if (left == nums.size() || nums[left] != target) { + return {-1, -1}; + } + + int right = upper_bound(nums.begin(), nums.end(), target) - nums.begin() - 1; + return {left, right}; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} target + * @return {number[]} + */ + searchRange(nums, target) { + let left = nums.indexOf(target); + let right = nums.lastIndexOf(target); + + return left === -1 ? [-1, -1] : [left, right]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(\log n)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/find-peak-element.md b/articles/find-peak-element.md new file mode 100644 index 000000000..9d341f185 --- /dev/null +++ b/articles/find-peak-element.md @@ -0,0 +1,341 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def findPeakElement(self, nums: List[int]) -> int: + for i in range(len(nums) - 1): + if nums[i] > nums[i + 1]: + return i + + return len(nums) - 1 +``` + +```java +public class Solution { + public int findPeakElement(int[] nums) { + for (int i = 0; i < nums.length - 1; i++) { + if (nums[i] > nums[i + 1]) { + return i; + } + } + return nums.length - 1; + } +} +``` + +```cpp +class Solution { +public: + int findPeakElement(vector& nums) { + for (int i = 0; i < nums.size() - 1; i++) { + if (nums[i] > nums[i + 1]) { + return i; + } + } + return nums.size() - 1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + findPeakElement(nums) { + for (let i = 0; i < nums.length - 1; i++) { + if (nums[i] > nums[i + 1]) { + return i; + } + } + return nums.length - 1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ + +--- + +## 2. Binary Search + +::tabs-start + +```python +class Solution: + def findPeakElement(self, nums: List[int]) -> int: + l, r = 0, len(nums) - 1 + + while l <= r: + m = l + (r - l) // 2 + if m > 0 and nums[m] < nums[m - 1]: + r = m - 1 + elif m < len(nums) - 1 and nums[m] < nums[m + 1]: + l = m + 1 + else: + return m +``` + +```java +public class Solution { + public int findPeakElement(int[] nums) { + int l = 0, r = nums.length - 1; + + while (l <= r) { + int m = l + (r - l) / 2; + if (m > 0 && nums[m] < nums[m - 1]) { + r = m - 1; + } else if (m < nums.length - 1 && nums[m] < nums[m + 1]) { + l = m + 1; + } else { + return m; + } + } + + return -1; + } +} +``` + +```cpp +class Solution { +public: + int findPeakElement(vector& nums) { + int l = 0, r = nums.size() - 1; + + while (l <= r) { + int m = l + (r - l) / 2; + if (m > 0 && nums[m] < nums[m - 1]) { + r = m - 1; + } else if (m < nums.size() - 1 && nums[m] < nums[m + 1]) { + l = m + 1; + } else { + return m; + } + } + + return -1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + findPeakElement(nums) { + let l = 0, r = nums.length - 1; + + while (l <= r) { + let m = Math.floor(l + (r - l) / 2); + if (m > 0 && nums[m] < nums[m - 1]) { + r = m - 1; + } else if (m < nums.length - 1 && nums[m] < nums[m + 1]) { + l = m + 1; + } else { + return m; + } + } + + return -1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(\log n)$ +* Space complexity: $O(1)$ + +--- + +## 3. Recursive Binary Search + +::tabs-start + +```python +class Solution: + def findPeakElement(self, nums: List[int]) -> int: + def binary_search(l, r): + if l == r: + return l + m = l + (r - l) // 2 + if nums[m] > nums[m + 1]: + return binary_search(l, m) + return binary_search(m + 1, r) + + return binary_search(0, len(nums) - 1) +``` + +```java +public class Solution { + public int findPeakElement(int[] nums) { + return binarySearch(nums, 0, nums.length - 1); + } + + private int binarySearch(int[] nums, int l, int r) { + if (l == r) { + return l; + } + int m = l + (r - l) / 2; + if (nums[m] > nums[m + 1]) { + return binarySearch(nums, l, m); + } + return binarySearch(nums, m + 1, r); + } +} +``` + +```cpp +class Solution { +public: + int findPeakElement(vector& nums) { + return binarySearch(nums, 0, nums.size() - 1); + } + +private: + int binarySearch(vector& nums, int l, int r) { + if (l == r) { + return l; + } + int m = l + (r - l) / 2; + if (nums[m] > nums[m + 1]) { + return binarySearch(nums, l, m); + } + return binarySearch(nums, m + 1, r); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + findPeakElement(nums) { + const binarySearch = (l, r) => { + if (l === r) { + return l; + } + let m = Math.floor((l + r) / 2); + if (nums[m] > nums[m + 1]) { + return binarySearch(l, m); + } else { + return binarySearch(m + 1, r); + } + }; + + return binarySearch(0, nums.length - 1); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(\log n)$ +* Space complexity: $O(\log n)$ for recursion stack. + +--- + +## 4. Binary Search (Optimal) + +::tabs-start + +```python +class Solution: + def findPeakElement(self, nums: List[int]) -> int: + l, r = 0, len(nums) - 1 + + while l < r: + m = (l + r) >> 1 + if nums[m] > nums[m + 1]: + r = m + else: + l = m + 1 + + return l +``` + +```java +public class Solution { + public int findPeakElement(int[] nums) { + int l = 0, r = nums.length - 1; + + while (l < r) { + int m = (l + r) >> 1; + if (nums[m] > nums[m + 1]) { + r = m; + } else { + l = m + 1; + } + } + + return l; + } +} +``` + +```cpp +class Solution { +public: + int findPeakElement(vector& nums) { + int l = 0, r = nums.size() - 1; + + while (l < r) { + int m = (l + r) >> 1; + if (nums[m] > nums[m + 1]) { + r = m; + } else { + l = m + 1; + } + } + + return l; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + findPeakElement(nums) { + let l = 0, r = nums.length - 1; + + while (l < r) { + let m = (l + r) >> 1; + if (nums[m] > nums[m + 1]) { + r = m; + } else { + l = m + 1; + } + } + + return l; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(\log n)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/flatten-nested-list-iterator.md b/articles/flatten-nested-list-iterator.md new file mode 100644 index 000000000..6ac2342a2 --- /dev/null +++ b/articles/flatten-nested-list-iterator.md @@ -0,0 +1,579 @@ +## 1. Recursion (Flatten And Store Into Global List ) + +::tabs-start + +```python +class NestedIterator: + def __init__(self, nestedList): + self.arr = [] + self.ptr = 0 + self.dfs(nestedList) + + def next(self): + val = self.arr[self.ptr] + self.ptr += 1 + return val + + def hasNext(self): + return self.ptr < len(self.arr) + + def dfs(self, nestedArr): + for num in nestedArr: + if num.isInteger(): + self.arr.append(num.getInteger()) + else: + self.dfs(num.getList()) +``` + +```java +public class NestedIterator implements Iterator { + + private List arr; + private int ptr; + + public NestedIterator(List nestedList) { + arr = new ArrayList<>(); + ptr = 0; + dfs(nestedList); + } + + @Override + public Integer next() { + return arr.get(ptr++); + } + + @Override + public boolean hasNext() { + return ptr < arr.size(); + } + + private void dfs(List nestedArr) { + for (NestedInteger num : nestedArr) { + if (num.isInteger()) { + arr.add(num.getInteger()); + } else { + dfs(num.getList()); + } + } + } +} +``` + +```cpp +class NestedIterator { +private: + vector arr; + int ptr; + + void dfs(const vector &nestedArr) { + for (const auto &num : nestedArr) { + if (num.isInteger()) { + arr.push_back(num.getInteger()); + } else { + dfs(num.getList()); + } + } + } + +public: + NestedIterator(vector &nestedList) { + ptr = 0; + dfs(nestedList); + } + + int next() { + return arr[ptr++]; + } + + bool hasNext() { + return ptr < arr.size(); + } +}; +``` + +```javascript +class NestedIterator { + /** + * @constructor + * @param {NestedInteger[]} nestedList + */ + constructor(nestedList) { + this.arr = []; + this.ptr = 0; + this.dfs(nestedList); + } + + /** + * @param {NestedInteger[]} nestedArr + */ + dfs(nestedArr) { + for (let num of nestedArr) { + if (num.isInteger()) { + this.arr.push(num.getInteger()); + } else { + this.dfs(num.getList()); + } + } + } + + /** + * @this NestedIterator + * @returns {integer} + */ + next() { + return this.arr[this.ptr++]; + } + + /** + * @this NestedIterator + * @returns {boolean} + */ + hasNext() { + return this.ptr < this.arr.length; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n + d)$ +* Space complexity: $O(n + d)$ + +> Where $n$ is the number of integers and $d$ is the nesting depth. + +--- + +## 2. Recursion (Flatten And Return) + +::tabs-start + +```python +class NestedIterator: + def __init__(self, nestedList): + self.arr = self.dfs(nestedList) + self.ptr = 0 + + def dfs(self, nestedArr): + res = [] + for num in nestedArr: + if num.isInteger(): + res.append(num.getInteger()) + else: + res.extend(self.dfs(num.getList())) + return res + + def next(self): + val = self.arr[self.ptr] + self.ptr += 1 + return val + + def hasNext(self): + return self.ptr < len(self.arr) +``` + +```java +public class NestedIterator implements Iterator { + private List arr; + private int ptr; + + public NestedIterator(List nestedList) { + arr = dfs(nestedList); + ptr = 0; + } + + private List dfs(List nestedArr) { + List res = new ArrayList<>(); + for (NestedInteger num : nestedArr) { + if (num.isInteger()) { + res.add(num.getInteger()); + } else { + res.addAll(dfs(num.getList())); + } + } + return res; + } + + @Override + public Integer next() { + return arr.get(ptr++); + } + + @Override + public boolean hasNext() { + return ptr < arr.size(); + } +} +``` + +```cpp +class NestedIterator { +private: + vector arr; + int ptr; + + vector dfs(const vector &nestedArr) { + vector res; + for (const auto &num : nestedArr) { + if (num.isInteger()) { + res.push_back(num.getInteger()); + } else { + vector temp = dfs(num.getList()); + res.insert(res.end(), temp.begin(), temp.end()); + } + } + return res; + } + +public: + NestedIterator(vector &nestedList) { + arr = dfs(nestedList); + ptr = 0; + } + + int next() { + return arr[ptr++]; + } + + bool hasNext() { + return ptr < arr.size(); + } +}; +``` + +```javascript +class NestedIterator { + /** + * @constructor + * @param {NestedInteger[]} nestedList + */ + constructor(nestedList) { + this.arr = this.dfs(nestedList); + this.ptr = 0; + } + + /** + * @param {NestedInteger[]} nestedArr + */ + dfs(nestedArr) { + let res = []; + for (let num of nestedArr) { + if (num.isInteger()) { + res.push(num.getInteger()); + } else { + res.push(...this.dfs(num.getList())); + } + } + return res; + } + + /** + * @this NestedIterator + * @returns {integer} + */ + next() { + return this.arr[this.ptr++]; + } + + /** + * @this NestedIterator + * @returns {boolean} + */ + hasNext() { + return this.ptr < this.arr.length; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n + d)$ +* Space complexity: $O(n + d)$ + +> Where $n$ is the number of integers and $d$ is the nesting depth. + +--- + +## 3. Recursion + Stack + +::tabs-start + +```python +class NestedIterator: + def __init__(self, nestedList: [NestedInteger]): + self.stack = [] + self.dfs(nestedList) + self.stack.reverse() + + def next(self) -> int: + return self.stack.pop() + + def hasNext(self) -> bool: + return len(self.stack) > 0 + + def dfs(self, nested): + for num in nested: + if num.isInteger(): + self.stack.append(num.getInteger()) + else: + self.dfs(num.getList()) +``` + +```java +public class NestedIterator implements Iterator { + private Stack stack; + + public NestedIterator(List nestedList) { + stack = new Stack<>(); + dfs(nestedList); + Collections.reverse(stack); + } + + private void dfs(List nested) { + for (NestedInteger num : nested) { + if (num.isInteger()) { + stack.push(num.getInteger()); + } else { + dfs(num.getList()); + } + } + } + + @Override + public Integer next() { + return stack.pop(); + } + + @Override + public boolean hasNext() { + return !stack.isEmpty(); + } +} +``` + +```cpp +class NestedIterator { +private: + vector stack; + + void dfs(const vector &nested) { + for (const auto &num : nested) { + if (num.isInteger()) { + stack.push_back(num.getInteger()); + } else { + dfs(num.getList()); + } + } + } + +public: + NestedIterator(vector &nestedList) { + dfs(nestedList); + reverse(stack.begin(), stack.end()); + } + + int next() { + int val = stack.back(); + stack.pop_back(); + return val; + } + + bool hasNext() { + return !stack.empty(); + } +}; +``` + +```javascript +class NestedIterator { + /** + * @constructor + * @param {NestedInteger[]} nestedList + */ + constructor(nestedList) { + this.stack = []; + this.dfs(nestedList); + this.stack.reverse(); + } + + /** + * @param {NestedInteger[]} nested + */ + dfs(nested) { + for (let num of nested) { + if (num.isInteger()) { + this.stack.push(num.getInteger()); + } else { + this.dfs(num.getList()); + } + } + } + + /** + * @this NestedIterator + * @returns {number} + */ + next() { + return this.stack.pop(); + } + + /** + * @this NestedIterator + * @returns {boolean} + */ + hasNext() { + return this.stack.length > 0; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n + d)$ +* Space complexity: $O(n + d)$ + +> Where $n$ is the number of integers and $d$ is the nesting depth. + +--- + +## 4. Stack + +::tabs-start + +```python +class NestedIterator: + def __init__(self, nestedList: [NestedInteger]): + self.stack = nestedList + self.stack.reverse() + + def next(self) -> int: + return self.stack.pop().getInteger() + + def hasNext(self) -> bool: + while self.stack: + top = self.stack[-1] + if top.isInteger(): + return True + + self.stack.pop() + self.stack.extend(reversed(top.getList())) + return False +``` + +```java +public class NestedIterator implements Iterator { + private List stack; + + public NestedIterator(List nestedList) { + stack = new ArrayList<>(nestedList); + Collections.reverse(stack); + } + + @Override + public Integer next() { + return stack.remove(stack.size() - 1).getInteger(); + } + + @Override + public boolean hasNext() { + while (!stack.isEmpty()) { + NestedInteger top = stack.get(stack.size() - 1); + if (top.isInteger()) { + return true; + } + stack.remove(stack.size() - 1); + List nestedList = top.getList(); + Collections.reverse(nestedList); + stack.addAll(nestedList); + } + return false; + } +} +``` + +```cpp +class NestedIterator { +private: + vector stack; + +public: + NestedIterator(vector &nestedList) { + stack = nestedList; + reverse(stack.begin(), stack.end()); + } + + int next() { + int val = stack.back().getInteger(); + stack.pop_back(); + return val; + } + + bool hasNext() { + while (!stack.empty()) { + NestedInteger top = stack.back(); + if (top.isInteger()) { + return true; + } + stack.pop_back(); + vector nestedList = top.getList(); + for (auto it = nestedList.rbegin(); it != nestedList.rend(); ++it) { + stack.push_back(*it); + } + } + return false; + } +}; +``` + +```javascript +class NestedIterator { + /** + * @constructor + * @param {NestedInteger[]} nestedList + */ + constructor(nestedList) { + this.stack = nestedList; + this.stack.reverse(); + } + + /** + * @this NestedIterator + * @returns {number} + */ + next() { + return this.stack.pop().getInteger(); + } + + /** + * @this NestedIterator + * @returns {boolean} + */ + hasNext() { + while (this.stack.length > 0) { + const top = this.stack[this.stack.length - 1]; + if (top.isInteger()) { + return true; + } + this.stack.pop(); + const nestedList = top.getList(); + nestedList.reverse(); + this.stack.push(...nestedList); + } + return false; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n + d)$ +* Space complexity: $O(n)$ + +> Where $n$ is the number of integers and $d$ is the nesting depth. \ No newline at end of file diff --git a/articles/make-the-string-great.md b/articles/make-the-string-great.md index 10b297d5c..f8ee712f7 100644 --- a/articles/make-the-string-great.md +++ b/articles/make-the-string-great.md @@ -43,7 +43,7 @@ public: int i = 0; while (i < n) { if (i > 0 && s[i] != s[i - 1] && tolower(s[i]) == tolower(s[i - 1])) { - s.erase(i - 1, 2); + s = s.substr(0, i - 1) + s.substr(i + 1); n -= 2; i -= 2; } diff --git a/articles/maximum-number-of-removable-characters.md b/articles/maximum-number-of-removable-characters.md new file mode 100644 index 000000000..bc22c959f --- /dev/null +++ b/articles/maximum-number-of-removable-characters.md @@ -0,0 +1,451 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def maximumRemovals(self, s: str, p: str, removable: List[int]) -> int: + n, m = len(s), len(p) + marked = set() + res = 0 + + for removeIdx in removable: + marked.add(removeIdx) + + sIdx = pIdx = 0 + while pIdx < m and sIdx < n: + if sIdx not in marked and s[sIdx] == p[pIdx]: + pIdx += 1 + sIdx += 1 + if pIdx != m: + break + res += 1 + + return res +``` + +```java +public class Solution { + public int maximumRemovals(String s, String p, int[] removable) { + int n = s.length(), m = p.length(); + Set marked = new HashSet<>(); + int res = 0; + + for (int removeIdx : removable) { + marked.add(removeIdx); + + int sIdx = 0, pIdx = 0; + while (pIdx < m && sIdx < n) { + if (!marked.contains(sIdx) && s.charAt(sIdx) == p.charAt(pIdx)) { + pIdx++; + } + sIdx++; + } + + if (pIdx != m) break; + res++; + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int maximumRemovals(string s, string p, vector& removable) { + int n = s.size(), m = p.size(); + unordered_set marked; + int res = 0; + + for (int removeIdx : removable) { + marked.insert(removeIdx); + + int sIdx = 0, pIdx = 0; + while (pIdx < m && sIdx < n) { + if (marked.find(sIdx) == marked.end() && s[sIdx] == p[pIdx]) { + pIdx++; + } + sIdx++; + } + + if (pIdx != m) break; + res++; + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @param {string} p + * @param {number[]} removable + * @return {number} + */ + maximumRemovals(s, p, removable) { + let n = s.length, m = p.length; + let marked = new Set(); + let res = 0; + + for (let removeIdx of removable) { + marked.add(removeIdx); + + let sIdx = 0, pIdx = 0; + while (pIdx < m && sIdx < n) { + if (!marked.has(sIdx) && s[sIdx] === p[pIdx]) { + pIdx++; + } + sIdx++; + } + + if (pIdx !== m) break; + res++; + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(k * (n + m))$ +* Space complexity: $O(k)$ + +> Where $n$ and $m$ are the lengths of the given strings $s$ and $p$ respectively. $k$ is the size of the array $removable$. + +--- + +## 2. Binary Search + Hash Set + +::tabs-start + +```python +class Solution: + def maximumRemovals(self, s: str, p: str, removable: List[int]) -> int: + def isSubseq(s, subseq, removed): + i1 = i2 = 0 + while i1 < len(s) and i2 < len(subseq): + if i1 in removed or s[i1] != subseq[i2]: + i1 += 1 + continue + i1 += 1 + i2 += 1 + return i2 == len(subseq) + + res = 0 + l, r = 0, len(removable) - 1 + while l <= r: + m = (l + r) // 2 + removed = set(removable[:m + 1]) + if isSubseq(s, p, removed): + res = max(res, m + 1) + l = m + 1 + else: + r = m - 1 + + return res +``` + +```java +public class Solution { + public int maximumRemovals(String s, String p, int[] removable) { + int res = 0, l = 0, r = removable.length - 1; + + while (l <= r) { + int m = (l + r) / 2; + Set removed = new HashSet<>(); + for (int i = 0; i <= m; i++) { + removed.add(removable[i]); + } + + if (isSubseq(s, p, removed)) { + res = Math.max(res, m + 1); + l = m + 1; + } else { + r = m - 1; + } + } + + return res; + } + + private boolean isSubseq(String s, String subseq, Set removed) { + int i1 = 0, i2 = 0; + while (i1 < s.length() && i2 < subseq.length()) { + if (removed.contains(i1) || s.charAt(i1) != subseq.charAt(i2)) { + i1++; + continue; + } + i1++; + i2++; + } + return i2 == subseq.length(); + } +} +``` + +```cpp +class Solution { +public: + int maximumRemovals(string s, string p, vector& removable) { + int res = 0, l = 0, r = removable.size() - 1; + + while (l <= r) { + int m = (l + r) / 2; + unordered_set removed(removable.begin(), removable.begin() + m + 1); + + if (isSubseq(s, p, removed)) { + res = max(res, m + 1); + l = m + 1; + } else { + r = m - 1; + } + } + + return res; + } + +private: + bool isSubseq(string& s, string& subseq, unordered_set& removed) { + int i1 = 0, i2 = 0; + while (i1 < s.size() && i2 < subseq.size()) { + if (removed.count(i1) || s[i1] != subseq[i2]) { + i1++; + continue; + } + i1++; + i2++; + } + return i2 == subseq.size(); + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @param {string} p + * @param {number[]} removable + * @return {number} + */ + maximumRemovals(s, p, removable) { + let res = 0, l = 0, r = removable.length - 1; + + while (l <= r) { + let m = Math.floor((l + r) / 2); + let removed = new Set(removable.slice(0, m + 1)); + + if (this.isSubseq(s, p, removed)) { + res = Math.max(res, m + 1); + l = m + 1; + } else { + r = m - 1; + } + } + + return res; + } + + /** + * @param {string} s + * @param {string} subseq + * @param {Set} removed + * @return {boolean} + */ + isSubseq(s, subseq, removed) { + let i1 = 0, i2 = 0; + while (i1 < s.length && i2 < subseq.length) { + if (removed.has(i1) || s[i1] !== subseq[i2]) { + i1++; + continue; + } + i1++; + i2++; + } + return i2 === subseq.length; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O((n + m) * \log k)$ +* Space complexity: $O(k)$ + +> Where $n$ and $m$ are the lengths of the given strings $s$ and $p$ respectively. $k$ is the size of the array $removable$. + +--- + +## 3. Binary Search + +::tabs-start + +```python +class Solution: + def maximumRemovals(self, s: str, p: str, removable: List[int]) -> int: + l, r = 0, len(removable) + n, m = len(s), len(p) + + def isSubseq(tmpS): + i1 = i2 = 0 + while i1 < n and i2 < m: + if tmpS[i1] == p[i2]: + i2 += 1 + i1 += 1 + return i2 == m + + while l < r: + mid = l + (r - l) // 2 + tmpS = list(s) + + for i in range(mid + 1): + tmpS[removable[i]] = '#' + + if isSubseq(tmpS): + l = mid + 1 + else: + r = mid + + return l +``` + +```java +public class Solution { + public int maximumRemovals(String s, String p, int[] removable) { + int l = 0, r = removable.length; + int n = s.length(), m = p.length(); + + while (l < r) { + int mid = l + (r - l) / 2; + char[] tmpS = s.toCharArray(); + + for (int i = 0; i <= mid; i++) { + tmpS[removable[i]] = '#'; + } + + if (isSubseq(tmpS, p)) { + l = mid + 1; + } else { + r = mid; + } + } + + return l; + } + + private boolean isSubseq(char[] s, String p) { + int i1 = 0, i2 = 0, n = s.length, m = p.length(); + + while (i1 < n && i2 < m) { + if (s[i1] == p.charAt(i2)) { + i2++; + } + i1++; + } + return i2 == m; + } +} +``` + +```cpp +class Solution { +public: + int maximumRemovals(string s, string p, vector& removable) { + int l = 0, r = removable.size(); + int n = s.size(), m = p.size(); + + while (l < r) { + int mid = l + (r - l) / 2; + string tmpS = s; + + for (int i = 0; i <= mid; i++) { + tmpS[removable[i]] = '#'; + } + + if (isSubseq(tmpS, p)) { + l = mid + 1; + } else { + r = mid; + } + } + + return l; + } + +private: + bool isSubseq(const string& s, const string& p) { + int i1 = 0, i2 = 0, n = s.size(), m = p.size(); + + while (i1 < n && i2 < m) { + if (s[i1] == p[i2]) { + i2++; + } + i1++; + } + return i2 == m; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @param {string} p + * @param {number[]} removable + * @return {number} + */ + maximumRemovals(s, p, removable) { + let l = 0, r = removable.length; + let n = s.length, m = p.length; + + const isSubseq = (tmpS) => { + let i1 = 0, i2 = 0; + while (i1 < n && i2 < m) { + if (tmpS[i1] === p[i2]) { + i2++; + } + i1++; + } + return i2 === m; + }; + + while (l < r) { + let mid = Math.floor(l + (r - l) / 2); + let tmpS = s.split(""); + + for (let i = 0; i <= mid; i++) { + tmpS[removable[i]] = "#"; + } + + if (isSubseq(tmpS)) { + l = mid + 1; + } else { + r = mid; + } + } + + return l; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O((n + m) * \log k)$ +* Space complexity: $O(n)$ + +> Where $n$ and $m$ are the lengths of the given strings $s$ and $p$ respectively. $k$ is the size of the array $removable$. \ No newline at end of file diff --git a/articles/maximum-twin-sum-of-a-linked-list.md b/articles/maximum-twin-sum-of-a-linked-list.md new file mode 100644 index 000000000..b896b8cae --- /dev/null +++ b/articles/maximum-twin-sum-of-a-linked-list.md @@ -0,0 +1,450 @@ +## 1. Convert To Array + +::tabs-start + +```python +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, val=0, next=None): +# self.val = val +# self.next = next +class Solution: + def pairSum(self, head: Optional[ListNode]) -> int: + arr = [] + cur = head + while cur: + arr.append(cur.val) + cur = cur.next + + i, j = 0, len(arr) - 1 + res = 0 + while i < j: + res = max(res, arr[i] + arr[j]) + i, j = i + 1, j - 1 + + return res +``` + +```java +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode() {} + * ListNode(int val) { this.val = val; } + * ListNode(int val, ListNode next) { this.val = val; this.next = next; } + * } + */ +public class Solution { + public int pairSum(ListNode head) { + List arr = new ArrayList<>(); + ListNode cur = head; + + while (cur != null) { + arr.add(cur.val); + cur = cur.next; + } + + int i = 0, j = arr.size() - 1, res = 0; + while (i < j) { + res = Math.max(res, arr.get(i) + arr.get(j)); + i++; + j--; + } + + return res; + } +} +``` + +```cpp +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode() : val(0), next(nullptr) {} + * ListNode(int x) : val(x), next(nullptr) {} + * ListNode(int x, ListNode *next) : val(x), next(next) {} + * }; + */ +class Solution { +public: + int pairSum(ListNode* head) { + vector arr; + ListNode* cur = head; + + while (cur) { + arr.push_back(cur->val); + cur = cur->next; + } + + int i = 0, j = arr.size() - 1, res = 0; + while (i < j) { + res = max(res, arr[i] + arr[j]); + i++; + j--; + } + + return res; + } +}; +``` + +```javascript +/** + * Definition for singly-linked list. + * class ListNode { + * constructor(val = 0, next = null) { + * this.val = val; + * this.next = next; + * } + * } + */ +class Solution { + /** + * @param {ListNode} head + * @return {number} + */ + pairSum(head) { + let arr = []; + let cur = head; + + while (cur) { + arr.push(cur.val); + cur = cur.next; + } + + let i = 0, j = arr.length - 1, res = 0; + while (i < j) { + res = Math.max(res, arr[i] + arr[j]); + i++; + j--; + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Reverse the Second Half + +::tabs-start + +```python +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, val=0, next=None): +# self.val = val +# self.next = next +class Solution: + def pairSum(self, head: Optional[ListNode]) -> int: + slow, fast = head, head + while fast and fast.next: + slow = slow.next + fast = fast.next.next + + prev, cur = None, slow + while cur: + nxt = cur.next + cur.next = prev + prev = cur + cur = nxt + + res = 0 + first, second = head, prev + while second: + res = max(res, first.val + second.val) + first, second = first.next, second.next + + return res +``` + +```java +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode() {} + * ListNode(int val) { this.val = val; } + * ListNode(int val, ListNode next) { this.val = val; this.next = next; } + * } + */ +public class Solution { + public int pairSum(ListNode head) { + ListNode slow = head, fast = head; + while (fast != null && fast.next != null) { + slow = slow.next; + fast = fast.next.next; + } + + ListNode prev = null, cur = slow; + while (cur != null) { + ListNode nxt = cur.next; + cur.next = prev; + prev = cur; + cur = nxt; + } + + int res = 0; + ListNode first = head, second = prev; + while (second != null) { + res = Math.max(res, first.val + second.val); + first = first.next; + second = second.next; + } + + return res; + } +} +``` + +```cpp +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode() : val(0), next(nullptr) {} + * ListNode(int x) : val(x), next(nullptr) {} + * ListNode(int x, ListNode *next) : val(x), next(next) {} + * }; + */ +class Solution { +public: + int pairSum(ListNode* head) { + ListNode* slow = head, *fast = head; + while (fast && fast->next) { + slow = slow->next; + fast = fast->next->next; + } + + ListNode* prev = nullptr, *cur = slow; + while (cur) { + ListNode* nxt = cur->next; + cur->next = prev; + prev = cur; + cur = nxt; + } + + int res = 0; + ListNode* first = head, *second = prev; + while (second) { + res = max(res, first->val + second->val); + first = first->next; + second = second->next; + } + + return res; + } +}; +``` + +```javascript +/** + * Definition for singly-linked list. + * class ListNode { + * constructor(val = 0, next = null) { + * this.val = val; + * this.next = next; + * } + * } + */ +class Solution { + /** + * @param {ListNode} head + * @return {number} + */ + pairSum(head) { + let slow = head, fast = head; + while (fast && fast.next) { + slow = slow.next; + fast = fast.next.next; + } + + let prev = null, cur = slow; + while (cur) { + let nxt = cur.next; + cur.next = prev; + prev = cur; + cur = nxt; + } + + let res = 0, first = head, second = prev; + while (second) { + res = Math.max(res, first.val + second.val); + first = first.next; + second = second.next; + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. + +--- + +## 3. Reverse the First Half + +::tabs-start + +```python +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, val=0, next=None): +# self.val = val +# self.next = next +class Solution: + def pairSum(self, head: Optional[ListNode]) -> int: + slow, fast = head, head + prev = None + + while fast and fast.next: + fast = fast.next.next + tmp = slow.next + slow.next = prev + prev = slow + slow = tmp + + res = 0 + while slow: + res = max(res, prev.val + slow.val) + prev = prev.next + slow = slow.next + + return res +``` + +```java +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode() {} + * ListNode(int val) { this.val = val; } + * ListNode(int val, ListNode next) { this.val = val; this.next = next; } + * } + */ +public class Solution { + public int pairSum(ListNode head) { + ListNode slow = head, fast = head, prev = null; + + while (fast != null && fast.next != null) { + fast = fast.next.next; + ListNode tmp = slow.next; + slow.next = prev; + prev = slow; + slow = tmp; + } + + int res = 0; + while (slow != null) { + res = Math.max(res, prev.val + slow.val); + prev = prev.next; + slow = slow.next; + } + + return res; + } +} +``` + +```cpp +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode() : val(0), next(nullptr) {} + * ListNode(int x) : val(x), next(nullptr) {} + * ListNode(int x, ListNode *next) : val(x), next(next) {} + * }; + */ +class Solution { +public: + int pairSum(ListNode* head) { + ListNode* slow = head, *fast = head, *prev = nullptr; + + while (fast && fast->next) { + fast = fast->next->next; + ListNode* tmp = slow->next; + slow->next = prev; + prev = slow; + slow = tmp; + } + + int res = 0; + while (slow) { + res = max(res, prev->val + slow->val); + prev = prev->next; + slow = slow->next; + } + + return res; + } +}; +``` + +```javascript +/** + * Definition for singly-linked list. + * class ListNode { + * constructor(val = 0, next = null) { + * this.val = val; + * this.next = next; + * } + * } + */ +class Solution { + /** + * @param {ListNode} head + * @return {number} + */ + pairSum(head) { + let slow = head, fast = head, prev = null; + + while (fast && fast.next) { + fast = fast.next.next; + let tmp = slow.next; + slow.next = prev; + prev = slow; + slow = tmp; + } + + let res = 0; + while (slow) { + res = Math.max(res, prev.val + slow.val); + prev = prev.next; + slow = slow.next; + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. \ No newline at end of file diff --git a/articles/minimize-the-maximum-difference-of-pairs.md b/articles/minimize-the-maximum-difference-of-pairs.md new file mode 100644 index 000000000..5ad0f31b1 --- /dev/null +++ b/articles/minimize-the-maximum-difference-of-pairs.md @@ -0,0 +1,561 @@ +## 1. Greedy + Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def minimizeMax(self, nums: List[int], p: int) -> int: + n = len(nums) + nums.sort() + dp = {} + + def dfs(i, pairs): + if pairs == p: + return 0 + if i >= n - 1: + return float('inf') + if (i, pairs) in dp: + return dp[(i, pairs)] + + take = max(nums[i + 1] - nums[i], dfs(i + 2, pairs + 1)) + skip = dfs(i + 1, pairs) + dp[(i, pairs)] = min(take, skip) + return dp[(i, pairs)] + + return dfs(0, 0) +``` + +```java +public class Solution { + private Map dp; + + public int minimizeMax(int[] nums, int p) { + Arrays.sort(nums); + dp = new HashMap<>(); + return dfs(0, 0, nums, p); + } + + private int dfs(int i, int pairs, int[] nums, int p) { + if (pairs == p) return 0; + if (i >= nums.length - 1) return Integer.MAX_VALUE; + + String key = i + "," + pairs; + if (dp.containsKey(key)) return dp.get(key); + + int take = Math.max(nums[i + 1] - nums[i], dfs(i + 2, pairs + 1, nums, p)); + int skip = dfs(i + 1, pairs, nums, p); + + int res = Math.min(take, skip); + dp.put(key, res); + return res; + } +} +``` + +```cpp +class Solution { + unordered_map dp; + +public: + int minimizeMax(vector& nums, int p) { + int n = nums.size(); + sort(nums.begin(), nums.end()); + return dfs(0, 0, nums, p); + } + +private: + int dfs(int i, int pairs, vector& nums, int p) { + if (pairs == p) return 0; + if (i >= nums.size() - 1) return INT_MAX; + long long key = i; + key = (key << 31) | pairs; + if (dp.count(key)) return dp[key]; + + int take = max(nums[i + 1] - nums[i], dfs(i + 2, pairs + 1, nums, p)); + int skip = dfs(i + 1, pairs, nums, p); + + return dp[key] = min(take, skip); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} p + * @return {number} + */ + minimizeMax(nums, p) { + nums.sort((a, b) => a - b); + const dp = new Map(); + + const dfs = (i, pairs) => { + if (pairs === p) return 0; + if (i >= nums.length - 1) return Infinity; + + let key = `${i},${pairs}`; + if (dp.has(key)) return dp.get(key); + + let take = Math.max(nums[i + 1] - nums[i], dfs(i + 2, pairs + 1)); + let skip = dfs(i + 1, pairs); + + let result = Math.min(take, skip); + dp.set(key, result); + return result; + }; + + return dfs(0, 0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * p)$ +* Space complexity: $O(n * p)$ + +> Where $n$ is the size of the input array and $p$ is the number of pairs to select. + +--- + +## 2. Greesy + Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def minimizeMax(self, nums: List[int], p: int) -> int: + n = len(nums) + nums.sort() + + dp = [[float('inf')] * (p + 1) for _ in range(n + 1)] + for i in range(n + 1): + dp[i][0] = 0 + + for i in range(n - 2, -1, -1): + for pairs in range(1, p + 1): + take = float('inf') + if i + 1 < n: + take = max(nums[i + 1] - nums[i], dp[i + 2][pairs - 1]) + + skip = dp[i + 1][pairs] + dp[i][pairs] = min(take, skip) + + return dp[0][p] +``` + +```java +public class Solution { + public int minimizeMax(int[] nums, int p) { + int n = nums.length; + Arrays.sort(nums); + + int[][] dp = new int[n + 1][p + 1]; + for (int i = 0; i <= n; i++) { + Arrays.fill(dp[i], Integer.MAX_VALUE); + dp[i][0] = 0; + } + + for (int i = n - 2; i >= 0; i--) { + for (int pairs = 1; pairs <= p; pairs++) { + int take = Integer.MAX_VALUE; + if (i + 1 < n) { + take = Math.max(nums[i + 1] - nums[i], dp[i + 2][pairs - 1]); + } + int skip = dp[i + 1][pairs]; + dp[i][pairs] = Math.min(take, skip); + } + } + + return dp[0][p]; + } +} +``` + +```cpp +class Solution { +public: + int minimizeMax(vector& nums, int p) { + int n = nums.size(); + sort(nums.begin(), nums.end()); + + vector> dp(n + 1, vector(p + 1, INT_MAX)); + for (int i = 0; i <= n; i++) { + dp[i][0] = 0; + } + + for (int i = n - 2; i >= 0; i--) { + for (int pairs = 1; pairs <= p; pairs++) { + int take = INT_MAX; + if (i + 1 < n) { + take = max(nums[i + 1] - nums[i], dp[i + 2][pairs - 1]); + } + int skip = dp[i + 1][pairs]; + dp[i][pairs] = min(take, skip); + } + } + + return dp[0][p]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} p + * @return {number} + */ + minimizeMax(nums, p) { + const n = nums.length; + nums.sort((a, b) => a - b); + + const dp = Array.from({ length: n + 1 }, () => + new Array(p + 1).fill(Infinity) + ); + for (let i = 0; i <= n; i++) { + dp[i][0] = 0; + } + + for (let i = n - 2; i >= 0; i--) { + for (let pairs = 1; pairs <= p; pairs++) { + let take = Infinity; + if (i + 1 < n) { + take = Math.max(nums[i + 1] - nums[i], dp[i + 2][pairs - 1]); + } + const skip = dp[i + 1][pairs]; + dp[i][pairs] = Math.min(take, skip); + } + } + + return dp[0][p]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * p)$ +* Space complexity: $O(n * p)$ + +> Where $n$ is the size of the input array and $p$ is the number of pairs to select. + +--- + +## 3. Greesy + Dynamic Programming (Space Optimized) + +::tabs-start + +```python +class Solution: + def minimizeMax(self, nums: List[int], p: int) -> int: + n = len(nums) + nums.sort() + + dp = [float('inf')] * (p + 1) + dp1 = [float('inf')] * (p + 1) + dp2 = [float('inf')] * (p + 1) + + dp[0] = dp1[0] = dp2[0] = 0 + for i in range(n - 1, -1, -1): + for pairs in range(1, p + 1): + take = float('inf') + if i + 1 < n: + take = max(nums[i + 1] - nums[i], dp2[pairs - 1]) + skip = dp1[pairs] + dp[pairs] = min(take, skip) + + dp2 = dp1[:] + dp1 = dp[:] + dp = [float('inf')] * (p + 1) + dp[0] = 0 + + return dp1[p] +``` + +```java +public class Solution { + public int minimizeMax(int[] nums, int p) { + int n = nums.length; + Arrays.sort(nums); + + int[] dp = new int[p + 1]; + int[] dp1 = new int[p + 1]; + int[] dp2 = new int[p + 1]; + Arrays.fill(dp, Integer.MAX_VALUE); + Arrays.fill(dp1, Integer.MAX_VALUE); + Arrays.fill(dp2, Integer.MAX_VALUE); + dp[0] = dp1[0] = dp2[0] = 0; + + for (int i = n - 1; i >= 0; i--) { + for (int pairs = 1; pairs <= p; pairs++) { + int take = Integer.MAX_VALUE; + if (i + 1 < n) { + take = Math.max(nums[i + 1] - nums[i], dp2[pairs - 1]); + } + int skip = dp1[pairs]; + dp[pairs] = Math.min(take, skip); + } + dp2 = dp1.clone(); + dp1 = dp.clone(); + Arrays.fill(dp, Integer.MAX_VALUE); + dp[0] = 0; + } + + return dp1[p]; + } +} +``` + +```cpp +class Solution { +public: + int minimizeMax(vector& nums, int p) { + int n = nums.size(); + sort(nums.begin(), nums.end()); + + vector dp(p + 1, INT_MAX); + vector dp1(p + 1, INT_MAX); + vector dp2(p + 1, INT_MAX); + + dp[0] = dp1[0] = dp2[0] = 0; + + for (int i = n - 1; i >= 0; i--) { + for (int pairs = 1; pairs <= p; pairs++) { + int take = INT_MAX; + if (i + 1 < n) { + take = max(nums[i + 1] - nums[i], dp2[pairs - 1]); + } + int skip = dp1[pairs]; + dp[pairs] = min(take, skip); + } + dp2 = dp1; + dp1 = dp; + fill(dp.begin(), dp.end(), INT_MAX); + dp[0] = 0; + } + + return dp1[p]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} p + * @return {number} + */ + minimizeMax(nums, p) { + const n = nums.length; + nums.sort((a, b) => a - b); + + let dp = new Array(p + 1).fill(Infinity); + let dp1 = new Array(p + 1).fill(Infinity); + let dp2 = new Array(p + 1).fill(Infinity); + + dp[0] = dp1[0] = dp2[0] = 0; + + for (let i = n - 1; i >= 0; i--) { + for (let pairs = 1; pairs <= p; pairs++) { + let take = Infinity; + if (i + 1 < n) { + take = Math.max(nums[i + 1] - nums[i], dp2[pairs - 1]); + } + let skip = dp1[pairs]; + dp[pairs] = Math.min(take, skip); + } + dp2 = dp1.slice(); + dp1 = dp.slice(); + dp.fill(Infinity); + dp[0] = 0; + } + + return dp1[p]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * p)$ +* Space complexity: $O(p)$ + +> Where $n$ is the size of the input array and $p$ is the number of pairs to select. + +--- + +## 4. Greedy + Binary Search + +::tabs-start + +```python +class Solution: + def minimizeMax(self, nums: List[int], p: int) -> int: + if p == 0: + return 0 + + def isValid(threshold): + i, cnt = 0, 0 + while i < len(nums) - 1: + if abs(nums[i] - nums[i + 1]) <= threshold: + cnt += 1 + i += 2 + else: + i += 1 + if cnt == p: + return True + return False + + nums.sort() + l, r = 0, nums[-1] - nums[0] + res = nums[-1] - nums[0] + + while l <= r: + m = l + (r - l) // 2 + if isValid(m): + res = m + r = m - 1 + else: + l = m + 1 + + return res +``` + +```java +public class Solution { + public int minimizeMax(int[] nums, int p) { + if (p == 0) return 0; + + Arrays.sort(nums); + int left = 0, right = nums[nums.length - 1] - nums[0]; + int result = right; + + while (left <= right) { + int mid = left + (right - left) / 2; + if (isValid(nums, mid, p)) { + result = mid; + right = mid - 1; + } else { + left = mid + 1; + } + } + + return result; + } + + private boolean isValid(int[] nums, int threshold, int p) { + int i = 0, count = 0; + while (i < nums.length - 1) { + if (Math.abs(nums[i] - nums[i + 1]) <= threshold) { + count++; + i += 2; + } else { + i++; + } + if (count == p) return true; + } + return false; + } +} +``` + +```cpp +class Solution { +public: + int minimizeMax(vector& nums, int p) { + if (p == 0) return 0; + + sort(nums.begin(), nums.end()); + int left = 0, right = nums.back() - nums[0]; + int result = right; + + while (left <= right) { + int mid = left + (right - left) / 2; + if (isValid(nums, mid, p)) { + result = mid; + right = mid - 1; + } else { + left = mid + 1; + } + } + + return result; + } + +private: + bool isValid(vector& nums, int threshold, int p) { + int i = 0, count = 0; + while (i < nums.size() - 1) { + if (abs(nums[i] - nums[i + 1]) <= threshold) { + count++; + i += 2; + } else { + i++; + } + if (count == p) return true; + } + return false; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} p + * @return {number} + */ + minimizeMax(nums, p) { + if (p === 0) return 0; + + nums.sort((a, b) => a - b); + let l = 0, r = nums[nums.length - 1] - nums[0], res = r; + + const isValid = (threshold) => { + let i = 0, cnt = 0; + while (i < nums.length - 1) { + if (Math.abs(nums[i] - nums[i + 1]) <= threshold) { + cnt++; + i += 2; + } else { + i++; + } + if (cnt === p) return true; + } + return false; + }; + + while (l <= r) { + let m = Math.floor(l + (r - l) / 2); + if (isValid(m)) { + res = m; + r = m - 1; + } else { + l = m + 1; + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n\log n + n\log m)$ +* Space complexity: $O(1)$ or $O(n)$ depending on the sorting algorithm. + +> Where $n$ is the size of the input array and $m$ is the maximum value in the array. \ No newline at end of file diff --git a/articles/partition-list.md b/articles/partition-list.md new file mode 100644 index 000000000..d3e69dd52 --- /dev/null +++ b/articles/partition-list.md @@ -0,0 +1,324 @@ +## 1. Brute Force + +::tabs-start + +```python +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, val=0, next=None): +# self.val = val +# self.next = next +class Solution: + def partition(self, head: Optional[ListNode], x: int) -> Optional[ListNode]: + if not head: + return None + + less, greater = [], [] + cur = head + while cur: + if cur.val < x: + less.append(cur.val) + else: + greater.append(cur.val) + cur = cur.next + + cur = head + for val in less: + cur.val = val + cur = cur.next + + for val in greater: + cur.val = val + cur = cur.next + + return head +``` + +```java +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode() {} + * ListNode(int val) { this.val = val; } + * ListNode(int val, ListNode next) { this.val = val; this.next = next; } + * } + */ +public class Solution { + public ListNode partition(ListNode head, int x) { + if (head == null) { + return null; + } + + List less = new ArrayList<>(); + List greater = new ArrayList<>(); + ListNode cur = head; + + while (cur != null) { + if (cur.val < x) { + less.add(cur.val); + } else { + greater.add(cur.val); + } + cur = cur.next; + } + + cur = head; + for (int val : less) { + cur.val = val; + cur = cur.next; + } + + for (int val : greater) { + cur.val = val; + cur = cur.next; + } + + return head; + } +} +``` + +```cpp +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode() : val(0), next(nullptr) {} + * ListNode(int x) : val(x), next(nullptr) {} + * ListNode(int x, ListNode *next) : val(x), next(next) {} + * }; + */ +class Solution { +public: + ListNode* partition(ListNode* head, int x) { + if (!head) return nullptr; + + vector less, greater; + ListNode* cur = head; + + while (cur) { + if (cur->val < x) { + less.push_back(cur->val); + } else { + greater.push_back(cur->val); + } + cur = cur->next; + } + + cur = head; + for (int val : less) { + cur->val = val; + cur = cur->next; + } + + for (int val : greater) { + cur->val = val; + cur = cur->next; + } + + return head; + } +}; +``` + +```javascript +/** + * Definition for singly-linked list. + * class ListNode { + * constructor(val = 0, next = null) { + * this.val = val; + * this.next = next; + * } + * } + */ +class Solution { + /** + * @param {ListNode} head + * @param {number} x + * @return {ListNode} + */ + partition(head, x) { + if (!head) return null; + + let less = [], greater = []; + let cur = head; + + while (cur) { + if (cur.val < x) { + less.push(cur.val); + } else { + greater.push(cur.val); + } + cur = cur.next; + } + + cur = head; + for (let val of less) { + cur.val = val; + cur = cur.next; + } + + for (let val of greater) { + cur.val = val; + cur = cur.next; + } + + return head; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Two Pointers + +::tabs-start + +```python +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, val=0, next=None): +# self.val = val +# self.next = next +class Solution: + def partition(self, head: Optional[ListNode], x: int) -> Optional[ListNode]: + left, right = ListNode(), ListNode() + ltail, rtail = left, right + + while head: + if head.val < x: + ltail.next = head + ltail = ltail.next + else: + rtail.next = head + rtail = rtail.next + head = head.next + + ltail.next = right.next + rtail.next = None + return left.next +``` + +```java +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode() {} + * ListNode(int val) { this.val = val; } + * ListNode(int val, ListNode next) { this.val = val; this.next = next; } + * } + */ +public class Solution { + public ListNode partition(ListNode head, int x) { + ListNode left = new ListNode(0), right = new ListNode(0); + ListNode ltail = left, rtail = right; + + while (head != null) { + if (head.val < x) { + ltail.next = head; + ltail = ltail.next; + } else { + rtail.next = head; + rtail = rtail.next; + } + head = head.next; + } + + ltail.next = right.next; + rtail.next = null; + return left.next; + } +} +``` + +```cpp +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode() : val(0), next(nullptr) {} + * ListNode(int x) : val(x), next(nullptr) {} + * ListNode(int x, ListNode *next) : val(x), next(next) {} + * }; + */ +class Solution { +public: + ListNode* partition(ListNode* head, int x) { + ListNode leftDummy(0), rightDummy(0); + ListNode *ltail = &leftDummy, *rtail = &rightDummy; + + while (head) { + if (head->val < x) { + ltail->next = head; + ltail = ltail->next; + } else { + rtail->next = head; + rtail = rtail->next; + } + head = head->next; + } + + ltail->next = rightDummy.next; + rtail->next = nullptr; + return leftDummy.next; + } +}; +``` + +```javascript +/** + * Definition for singly-linked list. + * class ListNode { + * constructor(val = 0, next = null) { + * this.val = val; + * this.next = next; + * } + * } + */ +class Solution { + /** + * @param {ListNode} head + * @param {number} x + * @return {ListNode} + */ + partition(head, x) { + let left = new ListNode(0), right = new ListNode(0); + let ltail = left, rtail = right; + + while (head) { + if (head.val < x) { + ltail.next = head; + ltail = ltail.next; + } else { + rtail.next = head; + rtail = rtail.next; + } + head = head.next; + } + + ltail.next = right.next; + rtail.next = null; + return left.next; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/populating-next-right-pointers-in-each-node.md b/articles/populating-next-right-pointers-in-each-node.md new file mode 100644 index 000000000..1f7339287 --- /dev/null +++ b/articles/populating-next-right-pointers-in-each-node.md @@ -0,0 +1,700 @@ +## 1. Breadth First Search + +::tabs-start + +```python +""" +# Definition for a Node. +class Node: + def __init__(self, val: int = 0, left: 'Node' = None, right: 'Node' = None, next: 'Node' = None): + self.val = val + self.left = left + self.right = right + self.next = next +""" + +class Solution: + def connect(self, root: 'Optional[Node]') -> 'Optional[Node]': + if not root: + return None + + q = deque([root]) + while q: + levelSize = len(q) + while levelSize: + node = q.popleft() + if levelSize > 1: + node.next = q[0] + if node.left: + q.append(node.left) + if node.right: + q.append(node.right) + levelSize -= 1 + + return root +``` + +```java +/* +// Definition for a Node. +class Node { + public int val; + public Node left; + public Node right; + public Node next; + + public Node() {} + + public Node(int _val) { + val = _val; + } + + public Node(int _val, Node _left, Node _right, Node _next) { + val = _val; + left = _left; + right = _right; + next = _next; + } +}; +*/ + +public class Solution { + public Node connect(Node root) { + if (root == null) return null; + + Queue q = new LinkedList<>(); + q.add(root); + + while (!q.isEmpty()) { + int levelSize = q.size(); + while (levelSize > 0) { + Node node = q.poll(); + if (levelSize > 1) { + node.next = q.peek(); + } + if (node.left != null) { + q.add(node.left); + } + if (node.right != null) { + q.add(node.right); + } + levelSize--; + } + } + + return root; + } +} +``` + +```cpp +/* +// Definition for a Node. +class Node { +public: + int val; + Node* left; + Node* right; + Node* next; + + Node() : val(0), left(NULL), right(NULL), next(NULL) {} + + Node(int _val) : val(_val), left(NULL), right(NULL), next(NULL) {} + + Node(int _val, Node* _left, Node* _right, Node* _next) + : val(_val), left(_left), right(_right), next(_next) {} +}; +*/ + +class Solution { +public: + Node* connect(Node* root) { + if (!root) return nullptr; + + queue q; + q.push(root); + + while (!q.empty()) { + int levelSize = q.size(); + while (levelSize > 0) { + Node* node = q.front(); + q.pop(); + if (levelSize > 1) { + node->next = q.front(); + } + if (node->left) { + q.push(node->left); + } + if (node->right) { + q.push(node->right); + } + levelSize--; + } + } + + return root; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null, next = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * this.next = next; + * } + * } + */ + +class Solution { + /** + * @param {Node} root + * @return {Node} + */ + connect(root) { + if (!root) return null; + + const q = new Queue(); + q.push(root); + + while (!q.isEmpty()) { + let levelSize = q.size(); + while (levelSize > 0) { + let node = q.pop(); + if (levelSize > 1) { + node.next = q.front(); + } + if (node.left) { + q.push(node.left); + } + if (node.right) { + q.push(node.right); + } + levelSize--; + } + } + + return root; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(\log n)$ + +--- + +## 2. Depth First Search + +::tabs-start + +```python +""" +# Definition for a Node. +class Node: + def __init__(self, val: int = 0, left: 'Node' = None, right: 'Node' = None, next: 'Node' = None): + self.val = val + self.left = left + self.right = right + self.next = next +""" + +class Solution: + def connect(self, root: 'Optional[Node]') -> 'Optional[Node]': + mp = {} + + def dfs(node, depth): + if not node: + return + + if depth not in mp: + mp[depth] = node + else: + mp[depth].next = node + mp[depth] = node + + dfs(node.left, depth + 1) + dfs(node.right, depth + 1) + + dfs(root, 0) + return root +``` + +```java +/* +// Definition for a Node. +class Node { + public int val; + public Node left; + public Node right; + public Node next; + + public Node() {} + + public Node(int _val) { + val = _val; + } + + public Node(int _val, Node _left, Node _right, Node _next) { + val = _val; + left = _left; + right = _right; + next = _next; + } +}; +*/ + +public class Solution { + public Node connect(Node root) { + Map mp = new HashMap<>(); + dfs(root, 0, mp); + return root; + } + + private void dfs(Node node, int depth, Map mp) { + if (node == null) return; + + if (!mp.containsKey(depth)) { + mp.put(depth, node); + } else { + mp.get(depth).next = node; + mp.put(depth, node); + } + + dfs(node.left, depth + 1, mp); + dfs(node.right, depth + 1, mp); + } +} +``` + +```cpp +/* +// Definition for a Node. +class Node { +public: + int val; + Node* left; + Node* right; + Node* next; + + Node() : val(0), left(NULL), right(NULL), next(NULL) {} + + Node(int _val) : val(_val), left(NULL), right(NULL), next(NULL) {} + + Node(int _val, Node* _left, Node* _right, Node* _next) + : val(_val), left(_left), right(_right), next(_next) {} +}; +*/ + +class Solution { +public: + Node* connect(Node* root) { + unordered_map mp; + dfs(root, 0, mp); + return root; + } + +private: + void dfs(Node* node, int depth, unordered_map& mp) { + if (!node) return; + + if (mp.find(depth) == mp.end()) { + mp[depth] = node; + } else { + mp[depth]->next = node; + mp[depth] = node; + } + + dfs(node->left, depth + 1, mp); + dfs(node->right, depth + 1, mp); + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null, next = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * this.next = next; + * } + * } + */ + +class Solution { + /** + * @param {Node} root + * @return {Node} + */ + connect(root) { + let mp = new Map(); + + const dfs = (node, depth) => { + if (!node) return; + + if (!mp.has(depth)) { + mp.set(depth, node); + } else { + mp.get(depth).next = node; + mp.set(depth, node); + } + + dfs(node.left, depth + 1); + dfs(node.right, depth + 1); + }; + + dfs(root, 0); + return root; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(\log n)$ + +--- + +## 3. Depth First Search (Optimal) + +::tabs-start + +```python +""" +# Definition for a Node. +class Node: + def __init__(self, val: int = 0, left: 'Node' = None, right: 'Node' = None, next: 'Node' = None): + self.val = val + self.left = left + self.right = right + self.next = next +""" + +class Solution: + def connect(self, root: 'Optional[Node]') -> 'Optional[Node]': + if not root: + return root + + if root.left: + root.left.next = root.right + if root.next: + root.right.next = root.next.left + + self.connect(root.left) + self.connect(root.right) + + return root +``` + +```java +/* +// Definition for a Node. +class Node { + public int val; + public Node left; + public Node right; + public Node next; + + public Node() {} + + public Node(int _val) { + val = _val; + } + + public Node(int _val, Node _left, Node _right, Node _next) { + val = _val; + left = _left; + right = _right; + next = _next; + } +}; +*/ + +public class Solution { + public Node connect(Node root) { + if (root == null) return root; + + if (root.left != null) { + root.left.next = root.right; + if (root.next != null) { + root.right.next = root.next.left; + } + + connect(root.left); + connect(root.right); + } + + return root; + } +} +``` + +```cpp +/* +// Definition for a Node. +class Node { +public: + int val; + Node* left; + Node* right; + Node* next; + + Node() : val(0), left(NULL), right(NULL), next(NULL) {} + + Node(int _val) : val(_val), left(NULL), right(NULL), next(NULL) {} + + Node(int _val, Node* _left, Node* _right, Node* _next) + : val(_val), left(_left), right(_right), next(_next) {} +}; +*/ + +class Solution { +public: + Node* connect(Node* root) { + if (!root) return root; + + if (root->left) { + root->left->next = root->right; + if (root->next) { + root->right->next = root->next->left; + } + + connect(root->left); + connect(root->right); + } + + return root; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null, next = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * this.next = next; + * } + * } + */ + +class Solution { + /** + * @param {Node} root + * @return {Node} + */ + connect(root) { + if (!root) return root; + + if (root.left) { + root.left.next = root.right; + if (root.next) { + root.right.next = root.next.left; + } + + this.connect(root.left); + this.connect(root.right); + } + + return root; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(\log n)$ for the recursion stack. + +--- + +## 4. Breadth First Search (Optimal) + +::tabs-start + +```python +""" +# Definition for a Node. +class Node: + def __init__(self, val: int = 0, left: 'Node' = None, right: 'Node' = None, next: 'Node' = None): + self.val = val + self.left = left + self.right = right + self.next = next +""" + +class Solution: + def connect(self, root: 'Optional[Node]') -> 'Optional[Node]': + cur, nxt = root, root.left if root else None + + while cur and nxt: + cur.left.next = cur.right + if cur.next: + cur.right.next = cur.next.left + + cur = cur.next + if not cur: + cur = nxt + nxt = cur.left + + return root +``` + +```java +/* +// Definition for a Node. +class Node { + public int val; + public Node left; + public Node right; + public Node next; + + public Node() {} + + public Node(int _val) { + val = _val; + } + + public Node(int _val, Node _left, Node _right, Node _next) { + val = _val; + left = _left; + right = _right; + next = _next; + } +}; +*/ + +public class Solution { + public Node connect(Node root) { + if (root == null) return null; + + Node cur = root, nxt = root.left; + + while (cur != null && nxt != null) { + cur.left.next = cur.right; + if (cur.next != null) { + cur.right.next = cur.next.left; + } + + cur = cur.next; + if (cur == null) { + cur = nxt; + nxt = cur.left; + } + } + + return root; + } +} +``` + +```cpp +/* +// Definition for a Node. +class Node { +public: + int val; + Node* left; + Node* right; + Node* next; + + Node() : val(0), left(NULL), right(NULL), next(NULL) {} + + Node(int _val) : val(_val), left(NULL), right(NULL), next(NULL) {} + + Node(int _val, Node* _left, Node* _right, Node* _next) + : val(_val), left(_left), right(_right), next(_next) {} +}; +*/ + +class Solution { +public: + Node* connect(Node* root) { + if (!root) return nullptr; + + Node* cur = root, *nxt = root->left; + + while (cur && nxt) { + cur->left->next = cur->right; + if (cur->next) { + cur->right->next = cur->next->left; + } + + cur = cur->next; + if (!cur) { + cur = nxt; + nxt = cur->left; + } + } + + return root; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null, next = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * this.next = next; + * } + * } + */ + +class Solution { + /** + * @param {Node} root + * @return {Node} + */ + connect(root) { + if (!root) return null; + + let cur = root, nxt = root.left; + + while (cur && nxt) { + cur.left.next = cur.right; + if (cur.next) { + cur.right.next = cur.next.left; + } + + cur = cur.next; + if (!cur) { + cur = nxt; + nxt = cur.left; + } + } + + return root; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/redundant-connection.md b/articles/redundant-connection.md index 4a51a9aa3..81a67ff84 100644 --- a/articles/redundant-connection.md +++ b/articles/redundant-connection.md @@ -646,7 +646,7 @@ class Solution: if indegree[nei] == 1: q.append(nei) - for u, v in edges[::-1]: + for u, v in reversed(edges): if indegree[u] == 2 and indegree[v]: return [u, v] return [] diff --git a/articles/remove-all-adjacent-duplicates-in-string-ii.md b/articles/remove-all-adjacent-duplicates-in-string-ii.md new file mode 100644 index 000000000..7c4ae8a82 --- /dev/null +++ b/articles/remove-all-adjacent-duplicates-in-string-ii.md @@ -0,0 +1,467 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def removeDuplicates(self, s: str, k: int) -> str: + while True: + flag = False + cur = s[0] + cnt = 1 + for i in range(1, len(s)): + if cur != s[i]: + cnt = 0 + cur = s[i] + cnt += 1 + if cnt == k: + s = s[:i - cnt + 1] + s[i + 1:] + flag = True + break + + if not flag: + break + + return s +``` + +```java +public class Solution { + public String removeDuplicates(String s, int k) { + while (true) { + boolean flag = false; + char cur = s.charAt(0); + int cnt = 1; + + for (int i = 1; i < s.length(); i++) { + if (cur != s.charAt(i)) { + cnt = 0; + cur = s.charAt(i); + } + cnt++; + if (cnt == k) { + s = s.substring(0, i - cnt + 1) + s.substring(i + 1); + flag = true; + break; + } + } + + if (!flag) { + break; + } + } + + return s; + } +} +``` + +```cpp +class Solution { +public: + string removeDuplicates(string s, int k) { + while (true) { + bool flag = false; + char cur = s[0]; + int cnt = 1; + + for (int i = 1; i < s.size(); i++) { + if (cur != s[i]) { + cnt = 0; + cur = s[i]; + } + cnt++; + if (cnt == k) { + s = s.substr(0, i - cnt + 1) + s.substr(i + 1); + flag = true; + break; + } + } + + if (!flag) { + break; + } + } + + return s; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @param {number} k + * @return {string} + */ + removeDuplicates(s, k) { + while (true) { + let flag = false; + let cur = s[0]; + let cnt = 1; + + for (let i = 1; i < s.length; i++) { + if (cur !== s[i]) { + cnt = 0; + cur = s[i]; + } + cnt++; + if (cnt === k) { + s = s.slice(0, i - cnt + 1) + s.slice(i + 1); + flag = true; + break; + } + } + + if (!flag) { + break; + } + } + + return s; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(\frac {n ^ 2}{k})$ +* Space complexity: $O(n)$ + +--- + +## 2. Stack + +::tabs-start + +```python +class Solution: + def removeDuplicates(self, s: str, k: int) -> str: + stack = [] + n = len(s) + i = 0 + s = list(s) + + while i < n: + if i == 0 or s[i] != s[i - 1]: + stack.append(1) + else: + stack[-1] += 1 + if stack[-1] == k: + stack.pop() + del s[i - k + 1:i + 1] + i -= k + n -= k + + i += 1 + + return ''.join(s) +``` + +```java +public class Solution { + public String removeDuplicates(String s, int k) { + StringBuilder sb = new StringBuilder(s); + Stack stack = new Stack<>(); + int i = 0; + + while (i < sb.length()) { + if (i == 0 || sb.charAt(i) != sb.charAt(i - 1)) { + stack.push(1); + } else { + stack.push(stack.pop() + 1); + if (stack.peek() == k) { + stack.pop(); + sb.delete(i - k + 1, i + 1); + i -= k; + } + } + i++; + } + + return sb.toString(); + } +} +``` + +```cpp +class Solution { +public: + string removeDuplicates(string s, int k) { + vector stack; + int n = s.length(), i = 0; + + while (i < n) { + if (i == 0 || s[i] != s[i - 1]) { + stack.push_back(1); + } else { + stack.back()++; + if (stack.back() == k) { + stack.pop_back(); + s.erase(i - k + 1, k); + i -= k; + n -= k; + } + } + i++; + } + + return s; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @param {number} k + * @return {string} + */ + removeDuplicates(s, k) { + let stack = []; + let arr = s.split(''); + let i = 0; + + while (i < arr.length) { + if (i === 0 || arr[i] !== arr[i - 1]) { + stack.push(1); + } else { + stack[stack.length - 1]++; + if (stack[stack.length - 1] === k) { + stack.pop(); + arr.splice(i - k + 1, k); + i -= k; + } + } + i++; + } + + return arr.join(''); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Stack (Optimal) + +::tabs-start + +```python +class Solution: + def removeDuplicates(self, s: str, k: int) -> str: + stack = [] # [char, count] + + for c in s: + if stack and stack[-1][0] == c: + stack[-1][1] += 1 + else: + stack.append([c, 1]) + if stack[-1][1] == k: + stack.pop() + + return ''.join(char * count for char, count in stack) +``` + +```java +public class Solution { + public String removeDuplicates(String s, int k) { + Stack stack = new Stack<>(); // [char, count] + + for (char c : s.toCharArray()) { + if (!stack.isEmpty() && stack.peek()[0] == c) { + stack.peek()[1]++; + } else { + stack.push(new int[] {c, 1}); + } + if (stack.peek()[1] == k) { + stack.pop(); + } + } + + StringBuilder res = new StringBuilder(); + while (!stack.isEmpty()) { + int[] top = stack.pop(); + res.append(String.valueOf((char) top[0]).repeat(top[1])); + } + + return res.reverse().toString(); + } +} +``` + +```cpp +class Solution { +public: + string removeDuplicates(string s, int k) { + vector> stack; + + for (char c : s) { + if (!stack.empty() && stack.back().first == c) { + stack.back().second++; + } else { + stack.push_back({c, 1}); + } + if (stack.back().second == k) { + stack.pop_back(); + } + } + + string res; + for (auto& p : stack) { + res.append(p.second, p.first); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @param {number} k + * @return {string} + */ + removeDuplicates(s, k) { + const stack = []; // [char, count] + + for (const c of s) { + if (stack.length && stack[stack.length - 1][0] === c) { + stack[stack.length - 1][1]++; + } else { + stack.push([c, 1]); + } + if (stack[stack.length - 1][1] === k) { + stack.pop(); + } + } + + let res = ""; + for (const [char, count] of stack) { + res += char.repeat(count); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 4. Two Pointers + +::tabs-start + +```python +class Solution: + def removeDuplicates(self, s: str, k: int) -> str: + i = 0 + n = len(s) + count = [0] * n + s = list(s) + for j in range(n): + s[i] = s[j] + count[i] = 1 + if i > 0 and s[i - 1] == s[j]: + count[i] += count[i - 1] + if count[i] == k: + i -= k + i += 1 + return ''.join(s[:i]) +``` + +```java +public class Solution { + public String removeDuplicates(String s, int k) { + int i = 0, n = s.length(); + char[] str = s.toCharArray(); + int[] count = new int[n]; + for (int j = 0; j < n; j++) { + str[i] = str[j]; + count[i] = 1; + if (i > 0 && str[i - 1] == str[j]) { + count[i] += count[i - 1]; + } + if (count[i] == k) { + i -= k; + } + i++; + } + return new String(str, 0, i); + } +} +``` + +```cpp +class Solution { +public: + string removeDuplicates(string s, int k) { + int i = 0, n = s.length(); + vector count(n); + for (int j = 0; j < n; i++, j++) { + s[i] = s[j]; + count[i] = 1; + if (i > 0 && s[i - 1] == s[j]) { + count[i] += count[i - 1]; + } + if (count[i] == k) i -= k; + } + return s.substr(0, i); + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @param {number} k + * @return {string} + */ + removeDuplicates(s, k) { + let i = 0; + const n = s.length; + const count = new Array(n).fill(0); + s = s.split(''); + for (let j = 0; j < n; j++) { + s[i] = s[j]; + count[i] = 1; + if (i > 0 && s[i - 1] === s[j]) { + count[i] += count[i - 1]; + } + if (count[i] === k) { + i -= k; + } + i++; + } + return s.slice(0, i).join(''); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/remove-k-digits.md b/articles/remove-k-digits.md new file mode 100644 index 000000000..19ccf59dc --- /dev/null +++ b/articles/remove-k-digits.md @@ -0,0 +1,344 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def removeKdigits(self, num: str, k: int) -> str: + num = list(num) + while k: + i = 1 + while i < len(num) and num[i] >= num[i - 1]: + i += 1 + num.pop(i - 1) + k -= 1 + + i = 0 + while i < len(num) and num[i] == '0': + i += 1 + + num = num[i:] + return ''.join(num) if num else '0' +``` + +```java +public class Solution { + public String removeKdigits(String num, int k) { + StringBuilder sb = new StringBuilder(num); + while (k > 0) { + int i = 1; + while (i < sb.length() && sb.charAt(i) >= sb.charAt(i - 1)) { + i++; + } + sb.deleteCharAt(i - 1); + k--; + } + + int i = 0; + while (i < sb.length() && sb.charAt(i) == '0') { + i++; + } + + sb = new StringBuilder(sb.substring(i)); + return sb.length() == 0 ? "0" : sb.toString(); + } +} +``` + +```cpp +class Solution { +public: + string removeKdigits(string num, int k) { + while (k > 0) { + int i = 1; + while (i < num.size() && num[i] >= num[i - 1]) { + i++; + } + num.erase(i - 1, 1); + k--; + } + + int i = 0; + while (i < num.size() && num[i] == '0') { + i++; + } + + num = num.substr(i); + return num.empty() ? "0" : num; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} num + * @param {number} k + * @return {string} + */ + removeKdigits(num, k) { + num = num.split(''); + while (k > 0) { + let i = 1; + while (i < num.length && num[i] >= num[i - 1]) { + i++; + } + num.splice(i - 1, 1); + k--; + } + + let i = 0; + while (i < num.length && num[i] === '0') { + i++; + } + + num = num.slice(i); + return num.length === 0 ? "0" : num.join(''); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * k)$ +* Space complexity: $O(1)$ or $O(n)$ depending on the language. + +--- + +## 2. Greedy + Stack + +::tabs-start + +```python +class Solution: + def removeKdigits(self, num: str, k: int) -> str: + stack = [] + for c in num: + while k > 0 and stack and stack[-1] > c: + k -= 1 + stack.pop() + stack.append(c) + + while stack and k: + stack.pop() + k -= 1 + + i = 0 + while i < len(stack) and stack[i] == '0': + i += 1 + + res = stack[i:] + return ''.join(res) if res else "0" +``` + +```java +public class Solution { + public String removeKdigits(String num, int k) { + StringBuilder stack = new StringBuilder(); + for (char c : num.toCharArray()) { + while (k > 0 && stack.length() > 0 && stack.charAt(stack.length() - 1) > c) { + stack.deleteCharAt(stack.length() - 1); + k--; + } + stack.append(c); + } + + while (k > 0 && stack.length() > 0) { + stack.deleteCharAt(stack.length() - 1); + k--; + } + + int i = 0; + while (i < stack.length() && stack.charAt(i) == '0') { + i++; + } + + String res = stack.substring(i); + return res.isEmpty() ? "0" : res; + } +} +``` + +```cpp +class Solution { +public: + string removeKdigits(string num, int k) { + string stack; + for (char c : num) { + while (k > 0 && !stack.empty() && stack.back() > c) { + stack.pop_back(); + k--; + } + stack.push_back(c); + } + + while (k > 0 && !stack.empty()) { + stack.pop_back(); + k--; + } + + int i = 0; + while (i < stack.size() && stack[i] == '0') { + i++; + } + + string res = stack.substr(i); + return res.empty() ? "0" : res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} num + * @param {number} k + * @return {string} + */ + removeKdigits(num, k) { + let stack = []; + for (let c of num) { + while (k > 0 && stack.length > 0 && stack[stack.length - 1] > c) { + stack.pop(); + k--; + } + stack.push(c); + } + + while (k > 0 && stack.length > 0) { + stack.pop(); + k--; + } + + let i = 0; + while (i < stack.length && stack[i] === '0') { + i++; + } + + let res = stack.slice(i).join(''); + return res === '' ? "0" : res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Two Pointers + +::tabs-start + +```python +class Solution: + def removeKdigits(self, num: str, k: int) -> str: + l = 0 + num = list(num) + + for r in range(len(num)): + while l > 0 and k > 0 and num[l - 1] > num[r]: + l -= 1 + k -= 1 + num[l] = num[r] + l += 1 + + l -= k + i = 0 + while i < l and num[i] == '0': + i += 1 + res = ''.join(num[i:l]) + return res if res else '0' +``` + +```java +public class Solution { + public String removeKdigits(String num, int k) { + char[] numArray = num.toCharArray(); + int l = 0; + + for (int r = 0; r < numArray.length; r++) { + while (l > 0 && k > 0 && numArray[l - 1] > numArray[r]) { + l--; + k--; + } + numArray[l++] = numArray[r]; + } + + l -= k; + int i = 0; + while (i < l && numArray[i] == '0') { + i++; + } + String res = new String(numArray, i, l - i); + return res.isEmpty() ? "0" : res; + } +} +``` + +```cpp +class Solution { +public: + string removeKdigits(string num, int k) { + int l = 0; + for (int r = 0; r < num.size(); r++) { + while (l > 0 && k > 0 && num[l - 1] > num[r]) { + l--; + k--; + } + num[l++] = num[r]; + } + + l -= k; + int i = 0; + while (i < l && num[i] == '0') { + i++; + } + if (i == l) return "0"; + return num.substr(i, l - i); + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} num + * @param {number} k + * @return {string} + */ + removeKdigits(num, k) { + let numArray = num.split(''); + let l = 0; + + for (let r = 0; r < numArray.length; r++) { + while (l > 0 && k > 0 && numArray[l - 1] > numArray[r]) { + l--; + k--; + } + numArray[l++] = numArray[r]; + } + + l -= k; + let i = 0; + while (i < l && numArray[i] === '0') { + i++; + } + let res = numArray.slice(i, l).join(''); + return res.length === 0 ? "0" : res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ or $O(n)$ depending on the language. \ No newline at end of file diff --git a/articles/removing-stars-from-a-string.md b/articles/removing-stars-from-a-string.md new file mode 100644 index 000000000..108454bae --- /dev/null +++ b/articles/removing-stars-from-a-string.md @@ -0,0 +1,357 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def removeStars(self, s: str) -> str: + while True: + flag = False + for i in range(1, len(s)): + if s[i] == '*' and s[i - 1] != '*': + s = s[:i - 1] + s[i + 1:] + flag = True + break + if not flag: + break + return s +``` + +```java +public class Solution { + public String removeStars(String s) { + while (true) { + boolean flag = false; + StringBuilder sb = new StringBuilder(s); + for (int i = 1; i < sb.length(); i++) { + if (sb.charAt(i) == '*' && sb.charAt(i - 1) != '*') { + sb.delete(i - 1, i + 1); + flag = true; + break; + } + } + s = sb.toString(); + if (!flag) { + break; + } + } + return s; + } +} +``` + +```cpp +class Solution { +public: + string removeStars(string s) { + while (true) { + bool flag = false; + for (int i = 1; i < s.size(); ++i) { + if (s[i] == '*' && s[i - 1] != '*') { + s = s.substr(0, i - 1) + s.substr(i + 1); + flag = true; + break; + } + } + if (!flag) { + break; + } + } + return s; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {string} + */ + removeStars(s) { + while (true) { + let flag = false; + for (let i = 1; i < s.length; i++) { + if (s[i] === '*' && s[i - 1] !== '*') { + s = s.slice(0, i - 1) + s.slice(i + 1); + flag = true; + break; + } + } + if (!flag) { + break; + } + } + return s; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n)$ + +--- + +## 2. Brute Force (Optimized) + +::tabs-start + +```python +class Solution: + def removeStars(self, s: str) -> str: + n = len(s) + i = 0 + while i < n: + if i and s[i] == '*' and s[i - 1] != '*': + s = s[:i - 1] + s[i + 1:] + n -= 2 + i -= 2 + i += 1 + return s +``` + +```java +public class Solution { + public String removeStars(String s) { + int n = s.length(); + int i = 0; + while (i < n) { + if (i > 0 && s.charAt(i) == '*' && s.charAt(i - 1) != '*') { + s = s.substring(0, i - 1) + s.substring(i + 1); + n -= 2; + i -= 2; + } + i++; + } + return s; + } +} +``` + +```cpp +class Solution { +public: + string removeStars(string s) { + int n = s.length(); + int i = 0; + while (i < n) { + if (i > 0 && s[i] == '*' && s[i - 1] != '*') { + s = s.substr(0, i - 1) + s.substr(i + 1); + n -= 2; + i -= 2; + } + i++; + } + return s; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {string} + */ + removeStars(s) { + let n = s.length; + let i = 0; + while (i < n) { + if (i > 0 && s[i] === '*' && s[i - 1] !== '*') { + s = s.slice(0, i - 1) + s.slice(i + 1); + n -= 2; + i -= 2; + } + i++; + } + return s; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n)$ + +--- + +## 3. Stack + +::tabs-start + +```python +class Solution: + def removeStars(self, s: str) -> str: + stack = [] + for c in s: + if c == "*": + stack and stack.pop() + else: + stack.append(c) + return "".join(stack) +``` + +```java +public class Solution { + public String removeStars(String s) { + Stack stack = new Stack<>(); + for (char c : s.toCharArray()) { + if (c == '*') { + if (!stack.isEmpty()) stack.pop(); + } else { + stack.push(c); + } + } + StringBuilder res = new StringBuilder(); + for (char c : stack) res.append(c); + return res.toString(); + } +} +``` + +```cpp +class Solution { +public: + string removeStars(string s) { + stack stack; + for (char c : s) { + if (c == '*') { + if (!stack.empty()) stack.pop(); + } else { + stack.push(c); + } + } + string res; + while (!stack.empty()) { + res += stack.top(); + stack.pop(); + } + reverse(res.begin(), res.end()); + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {string} + */ + removeStars(s) { + const stack = []; + for (const c of s) { + if (c === "*") { + if (stack.length > 0) stack.pop(); + } else { + stack.push(c); + } + } + return stack.join(""); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 4. Two Pointers + +::tabs-start + +```python +class Solution: + def removeStars(self, s: str) -> str: + l = 0 + s = list(s) + + for r in range(len(s)): + if s[r] == '*': + l -= 1 + else: + s[l] = s[r] + l += 1 + + return ''.join(s[:l]) +``` + +```java +public class Solution { + public String removeStars(String s) { + char[] arr = s.toCharArray(); + int l = 0; + + for (int r = 0; r < arr.length; r++) { + if (arr[r] == '*') { + l--; + } else { + arr[l] = arr[r]; + l++; + } + } + return new String(arr, 0, l); + } +} +``` + +```cpp +class Solution { +public: + string removeStars(string s) { + int l = 0; + + for (int r = 0; r < s.size(); r++) { + if (s[r] == '*') { + l--; + } else { + s[l] = s[r]; + l++; + } + } + return s.substr(0, l); + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {string} + */ + removeStars(s) { + const arr = s.split(''); + let l = 0; + + for (let r = 0; r < arr.length; r++) { + if (arr[r] === '*') { + l--; + } else { + arr[l] = arr[r]; + l++; + } + } + return arr.slice(0, l).join(''); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ or $O(n)$ depending on the language. \ No newline at end of file diff --git a/articles/search-suggestions-system.md b/articles/search-suggestions-system.md new file mode 100644 index 000000000..87325be56 --- /dev/null +++ b/articles/search-suggestions-system.md @@ -0,0 +1,589 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def suggestedProducts(self, products: List[str], searchWord: str) -> List[List[str]]: + res = [] + m = len(searchWord) + products.sort() + + for i in range(m): + cur = [] + for w in products: + if len(w) <= i: + continue + + flag = True + for j in range(i + 1): + if w[j] != searchWord[j]: + flag = False + break + + if flag: + cur.append(w) + if len(cur) == 3: + break + + if not cur: + for j in range(i, m): + res.append([]) + break + res.append(cur) + + return res +``` + +```java +public class Solution { + public List> suggestedProducts(String[] products, String searchWord) { + List> res = new ArrayList<>(); + int m = searchWord.length(); + Arrays.sort(products); + + for (int i = 0; i < m; i++) { + List cur = new ArrayList<>(); + for (String w : products) { + if (w.length() <= i) continue; + + boolean flag = true; + for (int j = 0; j <= i; j++) { + if (w.charAt(j) != searchWord.charAt(j)) { + flag = false; + break; + } + } + + if (flag) { + cur.add(w); + if (cur.size() == 3) break; + } + } + + if (cur.isEmpty()) { + while (i < m) { + res.add(new ArrayList<>()); + i++; + } + break; + } + + res.add(cur); + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector> suggestedProducts(vector& products, string searchWord) { + vector> res; + int m = searchWord.size(); + sort(products.begin(), products.end()); + + for (int i = 0; i < m; i++) { + vector cur; + for (const string& w : products) { + if (w.size() <= i) continue; + + bool flag = true; + for (int j = 0; j <= i; j++) { + if (w[j] != searchWord[j]) { + flag = false; + break; + } + } + + if (flag) { + cur.push_back(w); + if (cur.size() == 3) break; + } + } + + if (cur.empty()) { + while (i < m) { + res.push_back({}); + i++; + } + break; + } + + res.push_back(cur); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} products + * @param {string} searchWord + * @return {string[][]} + */ + suggestedProducts(products, searchWord) { + let res = []; + let m = searchWord.length; + products.sort(); + + for (let i = 0; i < m; i++) { + let cur = []; + for (let w of products) { + if (w.length <= i) continue; + + let flag = true; + for (let j = 0; j <= i; j++) { + if (w[j] !== searchWord[j]) { + flag = false; + break; + } + } + + if (flag) { + cur.push(w); + if (cur.length === 3) break; + } + } + + if (cur.length === 0) { + while (i < m) { + res.push([]); + i++; + } + break; + } + + res.push(cur); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n + m * n)$ +* Space complexity: + * $O(n)$ or $O(1)$ space for the sorting algorithm. + * $O(m * w)$ space for the output array. + +> Where $n$ is the total number of characters in the string array $products$, $m$ is the length of the string $searchWord$, and $w$ is the average length of each word in the given string array. + +--- + +## 2. Sorting + Binary Search + +::tabs-start + +```python +class Solution: + def suggestedProducts(self, products: List[str], searchWord: str) -> List[List[str]]: + res = [] + m = len(searchWord) + products.sort() + + prefix = [] + start = 0 + + def binary_search(target, start): + l, r = start, len(products) + while l < r: + mid = l + (r - l) // 2 + if products[mid] >= target: + r = mid + else: + l = mid + 1 + return l + + for i in range(m): + prefix.append(searchWord[i]) + start = binary_search("".join(prefix), start) + + cur = [] + for j in range(start, min(start + 3, len(products))): + if products[j].startswith("".join(prefix)): + cur.append(products[j]) + else: + break + + res.append(cur) + + return res +``` + +```java +public class Solution { + public List> suggestedProducts(String[] products, String searchWord) { + List> res = new ArrayList<>(); + int m = searchWord.length(); + Arrays.sort(products); + + StringBuilder prefix = new StringBuilder(); + int start = 0; + + for (int i = 0; i < m; i++) { + prefix.append(searchWord.charAt(i)); + start = binarySearch(products, prefix.toString(), start); + + List cur = new ArrayList<>(); + for (int j = start; j < Math.min(start + 3, products.length); j++) { + if (products[j].startsWith(prefix.toString())) { + cur.add(products[j]); + } else { + break; + } + } + + res.add(cur); + } + + return res; + } + + private int binarySearch(String[] products, String target, int start) { + int l = start, r = products.length; + while (l < r) { + int mid = l + (r - l) / 2; + if (products[mid].compareTo(target) >= 0) { + r = mid; + } else { + l = mid + 1; + } + } + return l; + } +} +``` + +```cpp +class Solution { +public: + vector> suggestedProducts(vector& products, string searchWord) { + vector> res; + int m = searchWord.size(); + sort(products.begin(), products.end()); + + string prefix = ""; + int start = 0; + + for (int i = 0; i < m; i++) { + prefix += searchWord[i]; + start = binarySearch(products, prefix, start); + + vector cur; + for (int j = start; j < min(start + 3, (int)products.size()); j++) { + if (products[j].substr(0, prefix.size()) == prefix) { + cur.push_back(products[j]); + } else { + break; + } + } + + res.push_back(cur); + } + + return res; + } + +private: + int binarySearch(vector& products, string target, int start) { + int l = start, r = products.size(); + while (l < r) { + int mid = l + (r - l) / 2; + if (products[mid] >= target) { + r = mid; + } else { + l = mid + 1; + } + } + return l; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} products + * @param {string} searchWord + * @return {string[][]} + */ + suggestedProducts(products, searchWord) { + let res = []; + let m = searchWord.length; + products.sort(); + + let prefix = []; + let start = 0; + + for (let i = 0; i < m; i++) { + prefix.push(searchWord[i]); + start = this.binarySearch(products, prefix.join(""), start); + + let cur = []; + for (let j = start; j < Math.min(start + 3, products.length); j++) { + if (products[j].startsWith(prefix.join(""))) { + cur.push(products[j]); + } else { + break; + } + } + + res.push(cur); + } + + return res; + } + + /** + * @param {string[]} products + * @param {string} target + * @param {number} start + * @return {number} + */ + binarySearch(products, target, start) { + let l = start, r = products.length; + while (l < r) { + let mid = Math.floor(l + (r - l) / 2); + if (products[mid] >= target) { + r = mid; + } else { + l = mid + 1; + } + } + return l; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n + m * w * \log N)$ +* Space complexity: + * $O(n)$ or $O(1)$ space for the sorting algorithm. + * $O(m * w)$ space for the output array. + +> Where $n$ is the total number of characters in the string array $products$, $N$ is the size of the array $products$, $m$ is the length of the string $searchWord$, and $w$ is the average length of each word in the given string array. + +--- + +## 3. Sorting + Binary Search (Built-In Function) + +::tabs-start + +```python +class Solution: + def suggestedProducts(self, products: List[str], searchWord: str) -> List[List[str]]: + res = [] + m = len(searchWord) + products.sort() + + prefix = "" + start = 0 + for i in range(m): + prefix += searchWord[i] + start = bisect_left(products, prefix, start) + + cur = [] + for j in range(start, min(start + 3, len(products))): + if products[j].startswith(prefix): + cur.append(products[j]) + else: + break + + res.append(cur) + + return res +``` + +```cpp +class Solution { +public: + vector> suggestedProducts(vector& products, string searchWord) { + vector> res; + int m = searchWord.size(); + sort(products.begin(), products.end()); + + string prefix = ""; + int start = 0; + for (int i = 0; i < m; i++) { + prefix += searchWord[i]; + start = lower_bound(products.begin() + start, products.end(), prefix) - products.begin(); + + vector cur; + for (int j = start; j < min(start + 3, (int)products.size()); j++) { + if (products[j].find(prefix) == 0) { + cur.push_back(products[j]); + } else { + break; + } + } + + res.push_back(cur); + } + + return res; + } +}; +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n + m * w * \log N)$ +* Space complexity: + * $O(n)$ or $O(1)$ space for the sorting algorithm. + * $O(m * w)$ space for the output array. + +> Where $n$ is the total number of characters in the string array $products$, $N$ is the size of the array $products$, $m$ is the length of the string $searchWord$, and $w$ is the average length of each word in the given string array. + +--- + +## 4. Sorting + Two Pointers + +::tabs-start + +```python +class Solution: + def suggestedProducts(self, products: List[str], searchWord: str) -> List[List[str]]: + res = [] + products.sort() + + l, r = 0, len(products) - 1 + for i in range(len(searchWord)): + c = searchWord[i] + + while l <= r and (len(products[l]) <= i or products[l][i] != c): + l += 1 + while l <= r and (len(products[r]) <= i or products[r][i] != c): + r -= 1 + + res.append([]) + remain = r - l + 1 + for j in range(min(3, remain)): + res[-1].append(products[l + j]) + + return res +``` + +```java +public class Solution { + public List> suggestedProducts(String[] products, String searchWord) { + List> res = new ArrayList<>(); + Arrays.sort(products); + + int l = 0, r = products.length - 1; + for (int i = 0; i < searchWord.length(); i++) { + char c = searchWord.charAt(i); + + while (l <= r && (products[l].length() <= i || products[l].charAt(i) != c)) { + l++; + } + while (l <= r && (products[r].length() <= i || products[r].charAt(i) != c)) { + r--; + } + + List cur = new ArrayList<>(); + int remain = r - l + 1; + for (int j = 0; j < Math.min(3, remain); j++) { + cur.add(products[l + j]); + } + + res.add(cur); + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector> suggestedProducts(vector& products, string searchWord) { + vector> res; + sort(products.begin(), products.end()); + + int l = 0, r = products.size() - 1; + for (int i = 0; i < searchWord.size(); i++) { + char c = searchWord[i]; + + while (l <= r && (products[l].size() <= i || products[l][i] != c)) { + l++; + } + while (l <= r && (products[r].size() <= i || products[r][i] != c)) { + r--; + } + + vector cur; + int remain = r - l + 1; + for (int j = 0; j < min(3, remain); j++) { + cur.push_back(products[l + j]); + } + + res.push_back(cur); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} products + * @param {string} searchWord + * @return {string[][]} + */ + suggestedProducts(products, searchWord) { + let res = []; + products.sort(); + + let l = 0, r = products.length - 1; + for (let i = 0; i < searchWord.length; i++) { + let c = searchWord[i]; + + while (l <= r && (products[l].length <= i || products[l][i] !== c)) { + l++; + } + while (l <= r && (products[r].length <= i || products[r][i] !== c)) { + r--; + } + + let cur = []; + let remain = r - l + 1; + for (let j = 0; j < Math.min(3, remain); j++) { + cur.push(products[l + j]); + } + + res.push(cur); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n + m * w + N)$ +* Space complexity: + * $O(n)$ or $O(1)$ space for the sorting algorithm. + * $O(m * w)$ space for the output array. + +> Where $n$ is the total number of characters in the string array $products$, $N$ is the size of the array $products$, $m$ is the length of the string $searchWord$, and $w$ is the average length of each word in the given string array. \ No newline at end of file diff --git a/articles/shortest-bridge.md b/articles/shortest-bridge.md new file mode 100644 index 000000000..14f78e190 --- /dev/null +++ b/articles/shortest-bridge.md @@ -0,0 +1,1068 @@ +## 1. Depth First Search + Breadth First Search - I + +::tabs-start + +```python +class Solution: + def shortestBridge(self, grid: List[List[int]]) -> int: + N = len(grid) + direct = [[0, 1], [0, -1], [1, 0], [-1, 0]] + + def invalid(r, c): + return r < 0 or c < 0 or r == N or c == N + + visit = set() + + def dfs(r, c): + if invalid(r, c) or not grid[r][c] or (r, c) in visit: + return + visit.add((r, c)) + for dr, dc in direct: + dfs(r + dr, c + dc) + + def bfs(): + res, q = 0, deque(visit) + while q: + for _ in range(len(q)): + r, c = q.popleft() + for dr, dc in direct: + curR, curC = r + dr, c + dc + if invalid(curR, curC) or (curR, curC) in visit: + continue + if grid[curR][curC]: + return res + q.append((curR, curC)) + visit.add((curR, curC)) + res += 1 + + for r in range(N): + for c in range(N): + if grid[r][c]: + dfs(r, c) + return bfs() +``` + +```java +public class Solution { + private int N; + private boolean[][] visited; + private final int[][] direct = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}}; + + public int shortestBridge(int[][] grid) { + N = grid.length; + visited = new boolean[N][N]; + + boolean found = false; + for (int r = 0; r < N; r++) { + if (found) break; + for (int c = 0; c < N; c++) { + if (grid[r][c] == 1) { + dfs(grid, r, c); + found = true; + break; + } + } + } + + return bfs(grid); + } + + private void dfs(int[][] grid, int r, int c) { + if (r < 0 || c < 0 || r >= N || c >= N || grid[r][c] == 0 || visited[r][c]) + return; + + visited[r][c] = true; + + for (int[] d : direct) { + dfs(grid, r + d[0], c + d[1]); + } + } + + private int bfs(int[][] grid) { + Queue q = new LinkedList<>(); + for (int r = 0; r < N; r++) { + for (int c = 0; c < N; c++) { + if (visited[r][c]) { + q.offer(new int[]{r, c}); + } + } + } + + int res = 0; + while (!q.isEmpty()) { + for (int i = q.size(); i > 0; i--) { + int[] cell = q.poll(); + int r = cell[0], c = cell[1]; + + for (int[] d : direct) { + int curR = r + d[0], curC = c + d[1]; + + if (curR < 0 || curC < 0 || curR >= N || curC >= N || visited[curR][curC]) + continue; + + if (grid[curR][curC] == 1) return res; + + q.offer(new int[]{curR, curC}); + visited[curR][curC] = true; + } + } + res++; + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int N; + vector> visited; + vector> direct = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}}; + + int shortestBridge(vector>& grid) { + N = grid.size(); + visited = vector>(N, vector(N, false)); + + bool found = false; + for (int r = 0; r < N; r++) { + if (found) break; + for (int c = 0; c < N; c++) { + if (grid[r][c] == 1) { + dfs(grid, r, c); + found = true; + break; + } + } + } + + return bfs(grid); + } + +private: + void dfs(vector>& grid, int r, int c) { + if (r < 0 || c < 0 || r >= N || c >= N || grid[r][c] == 0 || visited[r][c]) + return; + + visited[r][c] = true; + for (auto& d : direct) { + dfs(grid, r + d[0], c + d[1]); + } + } + + int bfs(vector>& grid) { + queue> q; + for (int r = 0; r < N; r++) { + for (int c = 0; c < N; c++) { + if (visited[r][c]) { + q.push({r, c}); + } + } + } + + int res = 0; + while (!q.empty()) { + for (int i = q.size(); i > 0; i--) { + auto [r, c] = q.front(); q.pop(); + + for (auto& d : direct) { + int curR = r + d[0], curC = c + d[1]; + + if (curR < 0 || curC < 0 || curR >= N || curC >= N || visited[curR][curC]) + continue; + + if (grid[curR][curC] == 1) return res; + q.push({curR, curC}); + visited[curR][curC] = true; + } + } + res++; + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} grid + * @return {number} + */ + shortestBridge(grid) { + const N = grid.length; + const direct = [[0, 1], [0, -1], [1, 0], [-1, 0]]; + const visited = Array.from({ length: N }, () => Array(N).fill(false)); + const q = new Queue(); + + const dfs = (r, c) => { + if (r < 0 || c < 0 || r >= N || c >= N || grid[r][c] === 0 || visited[r][c]) + return; + visited[r][c] = true; + q.push([r, c]); + + for (const [dr, dc] of direct) { + dfs(r + dr, c + dc); + } + }; + + const bfs = () => { + let res = 0; + while (!q.isEmpty()) { + for (let i = q.size(); i > 0; i--) { + const [r, c] = q.pop(); + for (const [dr, dc] of direct) { + const curR = r + dr, curC = c + dc; + + if (curR < 0 || curC < 0 || curR >= N || curC >= N || visited[curR][curC]) + continue; + if (grid[curR][curC] === 1) return res; + + q.push([curR, curC]); + visited[curR][curC] = true; + } + } + res++; + } + }; + + for (let r = 0; r < N; r++) { + for (let c = 0; c < N; c++) { + if (grid[r][c] === 1) { + dfs(r, c); + return bfs(); + } + } + } + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n ^ 2)$ + +--- + +## 2. Depth First Search + Breadth First Search - II + +::tabs-start + +```python +class Solution: + def shortestBridge(self, grid: list[list[int]]) -> int: + N, direct = len(grid), [(0, 1), (0, -1), (1, 0), (-1, 0)] + + def dfs(r, c): + if 0 <= r < N and 0 <= c < N and grid[r][c] == 1: + grid[r][c] = 2 + q.append((r, c)) + for dr, dc in direct: + dfs(r + dr, c + dc) + + q = deque() + for r in range(N): + for c in range(N): + if grid[r][c]: + dfs(r, c) + break + if q: break + + res = 0 + while q: + for _ in range(len(q)): + r, c = q.popleft() + for dr, dc in direct: + nr, nc = r + dr, c + dc + if 0 <= nr < N and 0 <= nc < N: + if grid[nr][nc] == 1: + return res + if grid[nr][nc] == 0: + grid[nr][nc] = 2 + q.append((nr, nc)) + res += 1 +``` + +```java +public class Solution { + private int[][] direct = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}}; + + public int shortestBridge(int[][] grid) { + int N = grid.length; + Queue q = new LinkedList<>(); + + boolean found = false; + for (int r = 0; r < N; r++) { + if (found) break; + for (int c = 0; c < N; c++) { + if (grid[r][c] == 1) { + dfs(grid, r, c, q); + found = true; + break; + } + } + } + + int res = 0; + while (!q.isEmpty()) { + for (int i = q.size(); i > 0; i--) { + int[] cell = q.poll(); + int r = cell[0], c = cell[1]; + + for (int[] d : direct) { + int nr = r + d[0], nc = c + d[1]; + + if (nr < 0 || nc < 0 || nr >= N || nc >= N) continue; + if (grid[nr][nc] == 1) return res; + + if (grid[nr][nc] == 0) { + grid[nr][nc] = 2; + q.offer(new int[]{nr, nc}); + } + } + } + res++; + } + return res; + } + + private void dfs(int[][] grid, int r, int c, Queue q) { + if (r < 0 || c < 0 || r >= grid.length || c >= grid.length || grid[r][c] != 1) + return; + + grid[r][c] = 2; + q.offer(new int[]{r, c}); + for (int[] d : direct) { + dfs(grid, r + d[0], c + d[1], q); + } + } +} +``` + +```cpp +class Solution { + vector> direct; + +public: + int shortestBridge(vector>& grid) { + int N = grid.size(); + direct = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}}; + queue> q; + + bool found = false; + for (int r = 0; r < N; r++) { + if (found) break; + for (int c = 0; c < N; c++) { + if (grid[r][c] == 1) { + dfs(grid, r, c, q); + found = true; + break; + } + } + } + + int res = 0; + while (!q.empty()) { + for (int i = q.size(); i > 0; i--) { + auto [r, c] = q.front(); q.pop(); + + for (auto& d : direct) { + int nr = r + d[0], nc = c + d[1]; + + if (nr < 0 || nc < 0 || nr >= N || nc >= N) continue; + if (grid[nr][nc] == 1) return res; + + if (grid[nr][nc] == 0) { + grid[nr][nc] = 2; + q.push({nr, nc}); + } + } + } + res++; + } + return res; + } + +private: + void dfs(vector>& grid, int r, int c, queue>& q) { + if (r < 0 || c < 0 || r >= grid.size() || c >= grid.size() || grid[r][c] != 1) + return; + + grid[r][c] = 2; + q.push({r, c}); + for (auto& d : direct) { + dfs(grid, r + d[0], c + d[1], q); + } + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} grid + * @return {number} + */ + shortestBridge(grid) { + const N = grid.length; + const direct = [[0, 1], [0, -1], [1, 0], [-1, 0]]; + const q = new Queue(); + + const dfs = (r, c) => { + if (r < 0 || c < 0 || r >= N || c >= N || grid[r][c] !== 1) return; + grid[r][c] = 2; + q.push([r, c]); + for (const [dr, dc] of direct) { + dfs(r + dr, c + dc); + } + }; + + let found = false; + for (let r = 0; r < N; r++) { + if (found) break; + for (let c = 0; c < N; c++) { + if (grid[r][c] === 1) { + dfs(r, c); + found = true; + break; + } + } + } + + let res = 0; + while (!q.isEmpty()) { + for (let i = q.size(); i > 0; i--) { + const [r, c] = q.pop(); + for (const [dr, dc] of direct) { + let nr = r + dr, nc = c + dc; + if (nr < 0 || nc < 0 || nr >= N || nc >= N) continue; + if (grid[nr][nc] === 1) return res; + if (grid[nr][nc] === 0) { + grid[nr][nc] = 2; + q.push([nr, nc]); + } + } + } + res++; + } + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n ^ 2)$ + +--- + +## 3. Breadth First Search + +::tabs-start + +```python +class Solution: + def shortestBridge(self, grid: list[list[int]]) -> int: + N, direct = len(grid), [(0, 1), (0, -1), (1, 0), (-1, 0)] + q2 = deque() + + found = False + for r in range(N): + if found: break + for c in range(N): + if grid[r][c] == 1: + q1 = deque([(r, c)]) + grid[r][c] = 2 + while q1: + x, y = q1.popleft() + q2.append((x, y)) + for dx, dy in direct: + nx, ny = x + dx, y + dy + if 0 <= nx < N and 0 <= ny < N and grid[nx][ny] == 1: + grid[nx][ny] = 2 + q1.append((nx, ny)) + found = True + break + + res = 0 + while q2: + for _ in range(len(q2)): + x, y = q2.popleft() + for dx, dy in direct: + nx, ny = x + dx, y + dy + if 0 <= nx < N and 0 <= ny < N: + if grid[nx][ny] == 1: + return res + if grid[nx][ny] == 0: + grid[nx][ny] = 2 + q2.append((nx, ny)) + res += 1 + + return res +``` + +```java +public class Solution { + public int shortestBridge(int[][] grid) { + int N = grid.length; + int[][] direct = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}}; + Queue q2 = new LinkedList<>(); + + boolean found = false; + for (int r = 0; r < N; r++) { + if (found) break; + for (int c = 0; c < N; c++) { + if (grid[r][c] == 1) { + Queue q1 = new LinkedList<>(); + q1.offer(new int[]{r, c}); + grid[r][c] = 2; + + while (!q1.isEmpty()) { + int[] cell = q1.poll(); + int x = cell[0], y = cell[1]; + q2.offer(new int[]{x, y}); + + for (int[] d : direct) { + int nx = x + d[0], ny = y + d[1]; + if (nx >= 0 && ny >= 0 && nx < N && ny < N && grid[nx][ny] == 1) { + grid[nx][ny] = 2; + q1.offer(new int[]{nx, ny}); + } + } + } + found = true; + break; + } + } + } + + int res = 0; + while (!q2.isEmpty()) { + for (int i = q2.size(); i > 0; i--) { + int[] cell = q2.poll(); + int x = cell[0], y = cell[1]; + + for (int[] d : direct) { + int nx = x + d[0], ny = y + d[1]; + + if (nx >= 0 && ny >= 0 && nx < N && ny < N) { + if (grid[nx][ny] == 1) return res; + if (grid[nx][ny] == 0) { + grid[nx][ny] = 2; + q2.offer(new int[]{nx, ny}); + } + } + } + } + res++; + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int shortestBridge(vector>& grid) { + int N = grid.size(); + vector> direct = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}}; + queue> q2; + + bool found = false; + for (int r = 0; r < N; r++) { + if (found) break; + for (int c = 0; c < N; c++) { + if (grid[r][c] == 1) { + queue> q1; + q1.push({r, c}); + grid[r][c] = 2; + + while (!q1.empty()) { + auto [x, y] = q1.front(); q1.pop(); + q2.push({x, y}); + + for (auto& d : direct) { + int nx = x + d[0], ny = y + d[1]; + if (nx >= 0 && ny >= 0 && nx < N && ny < N && grid[nx][ny] == 1) { + grid[nx][ny] = 2; + q1.push({nx, ny}); + } + } + } + found = true; + break; + } + } + } + + int res = 0; + while (!q2.empty()) { + for (int i = q2.size(); i > 0; i--) { + auto [x, y] = q2.front(); q2.pop(); + + for (auto& d : direct) { + int nx = x + d[0], ny = y + d[1]; + + if (nx >= 0 && ny >= 0 && nx < N && ny < N) { + if (grid[nx][ny] == 1) return res; + if (grid[nx][ny] == 0) { + grid[nx][ny] = 2; + q2.push({nx, ny}); + } + } + } + } + res++; + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} grid + * @return {number} + */ + shortestBridge(grid) { + const N = grid.length; + const direct = [[0, 1], [0, -1], [1, 0], [-1, 0]]; + const q2 = new Queue(); + + let found = false; + for (let r = 0; r < N; r++) { + if (found) break; + for (let c = 0; c < N; c++) { + if (grid[r][c] === 1) { + const q1 = new Queue([[r, c]]); + grid[r][c] = 2; + + while (!q1.isEmpty()) { + let [x, y] = q1.pop(); + q2.push([x, y]); + + for (let [dx, dy] of direct) { + let nx = x + dx, ny = y + dy; + if (nx >= 0 && ny >= 0 && nx < N && ny < N && grid[nx][ny] === 1) { + grid[nx][ny] = 2; + q1.push([nx, ny]); + } + } + } + found = true; + break; + } + } + } + + let res = 0; + while (!q2.isEmpty()) { + for (let i = q2.size(); i > 0; i--) { + const [x, y] = q2.pop(); + + for (let [dx, dy] of direct) { + let nx = x + dx, ny = y + dy; + if (nx >= 0 && ny >= 0 && nx < N && ny < N) { + if (grid[nx][ny] === 1) return res; + if (grid[nx][ny] === 0) { + grid[nx][ny] = 2; + q2.push([nx, ny]); + } + } + } + } + res++; + } + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n ^ 2)$ + +--- + +## 4. Disjoint Set Union + Breadth First Search + +::tabs-start + +```python +class DSU: + def __init__(self, n): + self.parent = list(range(n)) + self.rank = [1] * n + + def find(self, node): + cur = node + while cur != self.parent[cur]: + self.parent[cur] = self.parent[self.parent[cur]] + cur = self.parent[cur] + return cur + + def union(self, u, v): + pu, pv = self.find(u), self.find(v) + if pu == pv: + return False + if self.rank[pv] > self.rank[pu]: + pu, pv = pv, pu + self.parent[pv] = pu + self.rank[pu] += self.rank[pv] + return True + +class Solution: + def shortestBridge(self, grid: list[list[int]]) -> int: + n, direct = len(grid), [(0, 1), (0, -1), (1, 0), (-1, 0)] + dsu = DSU(n * n + 1) + + def idx(r, c): + return r * n + c + 1 + + for r in range(n): + for c in range(n): + if grid[r][c] == 1: + first_island = dsu.find(idx(r, c)) + if c + 1 < n and grid[r][c + 1] == 1: + dsu.union(idx(r, c), idx(r, c + 1)) + if r + 1 < n and grid[r + 1][c] == 1: + dsu.union(idx(r, c), idx(r + 1, c)) + + q = deque() + for r in range(n): + for c in range(n): + if grid[r][c] == 1: + if dsu.find(idx(r, c)) != first_island: + continue + for dx, dy in direct: + nr, nc = r + dx, c + dy + if 0 <= nr < n and 0 <= nc < n and grid[nr][nc] == 0: + q.append((r,c)) + break + + res = 0 + while q: + for _ in range(len(q)): + r, c = q.popleft() + for dx, dy in direct: + nr, nc = r + dx, c + dy + if 0 <= nr < n and 0 <= nc < n: + if grid[nr][nc] == 1 and dsu.union(idx(r, c), idx(nr, nc)): + return res + if grid[nr][nc] == 0: + grid[nr][nc] = 1 + dsu.union(idx(r, c), idx(nr, nc)) + q.append((nr, nc)) + res += 1 +``` + +```java +class DSU { + private int[] parent, rank; + + public DSU(int n) { + parent = new int[n]; + rank = new int[n]; + for (int i = 0; i < n; i++) parent[i] = i; + Arrays.fill(rank, 1); + } + + public int find(int node) { + if (parent[node] != node) { + parent[node] = find(parent[node]); + } + return parent[node]; + } + + public boolean union(int u, int v) { + int pu = find(u), pv = find(v); + if (pu == pv) return false; + + if (rank[pv] > rank[pu]) { + int temp = pu; + pu = pv; + pv = temp; + } + parent[pv] = pu; + rank[pu] += rank[pv]; + return true; + } +} + +public class Solution { + private int n; + private int idx(int r, int c) { + return r * n + c + 1; + } + + public int shortestBridge(int[][] grid) { + n = grid.length; + int[][] direct = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}}; + DSU dsu = new DSU(n * n + 1); + Queue q = new LinkedList<>(); + + + int firstIsland = -1; + for (int r = 0; r < n; r++) { + for (int c = 0; c < n; c++) { + if (grid[r][c] == 1) { + firstIsland = dsu.find(idx(r, c)); + if (c + 1 < n && grid[r][c + 1] == 1) + dsu.union(idx(r, c), idx(r, c + 1)); + if (r + 1 < n && grid[r + 1][c] == 1) + dsu.union(idx(r, c), idx(r + 1, c)); + } + } + } + + for (int r = 0; r < n; r++) { + for (int c = 0; c < n; c++) { + if (grid[r][c] == 1 && dsu.find(idx(r, c)) == firstIsland) { + for (int[] d : direct) { + int nr = r + d[0], nc = c + d[1]; + if (nr >= 0 && nc >= 0 && nr < n && nc < n && grid[nr][nc] == 0) { + q.offer(new int[]{r, c}); + break; + } + } + } + } + } + + int res = 0; + while (!q.isEmpty()) { + for (int i = q.size(); i > 0; i--) { + int[] cell = q.poll(); + int r = cell[0], c = cell[1]; + + for (int[] d : direct) { + int nr = r + d[0], nc = c + d[1]; + if (nr >= 0 && nc >= 0 && nr < n && nc < n) { + if (grid[nr][nc] == 1 && dsu.union(idx(r, c), idx(nr, nc))) { + return res; + } + if (grid[nr][nc] == 0) { + grid[nr][nc] = 1; + dsu.union(idx(r, c), idx(nr, nc)); + q.offer(new int[]{nr, nc}); + } + } + } + } + res++; + } + return res; + } +} +``` + +```cpp +class DSU { +public: + vector parent, rank; + + DSU(int n) : parent(n), rank(n, 1) { + for (int i = 0; i < n; i++) parent[i] = i; + } + + int find(int node) { + if (parent[node] != node) + parent[node] = find(parent[node]); + return parent[node]; + } + + bool unionSet(int u, int v) { + int pu = find(u), pv = find(v); + if (pu == pv) return false; + if (rank[pv] > rank[pu]) swap(pu, pv); + parent[pv] = pu; + rank[pu] += rank[pv]; + return true; + } +}; + +class Solution { +public: + int shortestBridge(vector>& grid) { + int N = grid.size(); + vector> direct = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}}; + DSU dsu(N * N + 1); + queue> q; + + auto idx = [&](int r, int c) { + return r * N + c + 1; + }; + + int firstIsland = -1; + for (int r = 0; r < N; r++) { + for (int c = 0; c < N; c++) { + if (grid[r][c] == 1) { + firstIsland = dsu.find(idx(r, c)); + if (c + 1 < N && grid[r][c + 1] == 1) + dsu.unionSet(idx(r, c), idx(r, c + 1)); + if (r + 1 < N && grid[r + 1][c] == 1) + dsu.unionSet(idx(r, c), idx(r + 1, c)); + } + } + } + + for (int r = 0; r < N; r++) { + for (int c = 0; c < N; c++) { + if (grid[r][c] == 1 && dsu.find(idx(r, c)) == firstIsland) { + for (auto& d : direct) { + int nr = r + d[0], nc = c + d[1]; + if (nr >= 0 && nc >= 0 && nr < N && nc < N && grid[nr][nc] == 0) { + q.push({r, c}); + break; + } + } + } + } + } + + int res = 0; + while (!q.empty()) { + for (int i = q.size(); i > 0; i--) { + auto [r, c] = q.front();q.pop(); + for (auto& d : direct) { + int nr = r + d[0], nc = c + d[1]; + if (nr >= 0 && nc >= 0 && nr < N && nc < N) { + if (grid[nr][nc] == 1 && dsu.unionSet(idx(r, c), idx(nr, nc))) + return res; + if (grid[nr][nc] == 0) { + grid[nr][nc] = 1; + dsu.unionSet(idx(r, c), idx(nr, nc)); + q.push({nr, nc}); + } + } + } + } + res++; + } + return res; + } +}; +``` + +```javascript +class DSU { + /** + * @constructor + * @param {number} n + */ + constructor(n) { + this.parent = Array.from({ length: n }, (_, i) => i); + this.rank = Array(n).fill(1); + } + + /** + * @param {number} node + * @return {number} + */ + find(node) { + if (this.parent[node] !== node) { + this.parent[node] = this.find(this.parent[node]); + } + return this.parent[node]; + } + + /** + * @param {number} u + * @param {number} v + * @return {boolean} + */ + union(u, v) { + let pu = this.find(u), + pv = this.find(v); + if (pu === pv) return false; + + if (this.rank[pv] > this.rank[pu]) [pu, pv] = [pv, pu]; + this.parent[pv] = pu; + this.rank[pu] += this.rank[pv]; + return true; + } +} + +class Solution { + /** + * @param {number[][]} grid + * @return {number} + */ + shortestBridge(grid) { + const N = grid.length; + const direct = [[0, 1], [0, -1], [1, 0], [-1, 0]]; + const dsu = new DSU(N * N + 1); + const q = new Queue(); + + const idx = (r, c) => r * N + c + 1; + + let firstIsland = -1; + for (let r = 0; r < N; r++) { + for (let c = 0; c < N; c++) { + if (grid[r][c] === 1) { + firstIsland = dsu.find(idx(r, c)); + if (c + 1 < N && grid[r][c + 1] === 1) + dsu.union(idx(r, c), idx(r, c + 1)); + if (r + 1 < N && grid[r + 1][c] === 1) + dsu.union(idx(r, c), idx(r + 1, c)); + } + } + } + + + for (let r = 0; r < N; r++) { + for (let c = 0; c < N; c++) { + if (grid[r][c] === 1 && dsu.find(idx(r, c)) === firstIsland) { + for (const [dx, dy] of direct) { + let nr = r + dx, + nc = c + dy; + if (nr >= 0 && nc >= 0 && nr < N && nc < N && grid[nr][nc] === 0) { + q.push([r, c]); + break; + } + } + } + } + } + + let res = 0; + while (!q.isEmpty()) { + for (let i = q.size(); i > 0; i--) { + const [r, c] = q.pop(); + for (let [dx, dy] of direct) { + let nr = r + dx, + nc = c + dy; + if (nr >= 0 && nc >= 0 && nr < N && nc < N) { + if (grid[nr][nc] === 1 && dsu.union(idx(r, c), idx(nr, nc))) { + return res; + } + if (grid[nr][nc] === 0) { + grid[nr][nc] = 1; + dsu.union(idx(r, c), idx(nr, nc)); + q.push([nr, nc]); + } + } + } + } + res++; + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n ^ 2)$ \ No newline at end of file diff --git a/articles/shortest-path-in-binary-matrix.md b/articles/shortest-path-in-binary-matrix.md new file mode 100644 index 000000000..c0a87d383 --- /dev/null +++ b/articles/shortest-path-in-binary-matrix.md @@ -0,0 +1,475 @@ +## 1. Breadth First Search + +::tabs-start + +```python +class Solution: + def shortestPathBinaryMatrix(self, grid: list[list[int]]) -> int: + N = len(grid) + if grid[0][0] or grid[N - 1][N - 1]: + return -1 + + q = deque([(0, 0, 1)]) + visit = set((0, 0)) + direct = [(0, 1), (1, 0), (0, -1), (-1, 0), + (1, 1), (-1, -1), (1, -1), (-1, 1)] + + while q: + r, c, length = q.popleft() + if r == N - 1 and c == N - 1: + return length + + for dr, dc in direct: + nr, nc = r + dr, c + dc + if (0 <= nr < N and 0 <= nc < N and grid[nr][nc] == 0 and + (nr, nc) not in visit): + q.append((nr, nc, length + 1)) + visit.add((nr, nc)) + + return -1 +``` + +```java +public class Solution { + public int shortestPathBinaryMatrix(int[][] grid) { + int N = grid.length; + if (grid[0][0] == 1 || grid[N - 1][N - 1] == 1) return -1; + + int[][] directions = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}, + {1, 1}, {-1, -1}, {1, -1}, {-1, 1}}; + boolean[][] visit = new boolean[N][N]; + + Queue q = new LinkedList<>(); + q.offer(new int[]{0, 0, 1}); + visit[0][0] = true; + + while (!q.isEmpty()) { + int[] cell = q.poll(); + int r = cell[0], c = cell[1], length = cell[2]; + + if (r == N - 1 && c == N - 1) return length; + + for (int[] d : directions) { + int nr = r + d[0], nc = c + d[1]; + if (nr >= 0 && nc >= 0 && nr < N && nc < N && + grid[nr][nc] == 0 && !visit[nr][nc]) { + q.offer(new int[]{nr, nc, length + 1}); + visit[nr][nc] = true; + } + } + } + return -1; + } +} +``` + +```cpp +class Solution { +public: + int shortestPathBinaryMatrix(vector>& grid) { + int N = grid.size(); + if (grid[0][0] == 1 || grid[N - 1][N - 1] == 1) return -1; + + vector> directions = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}, + {1, 1}, {-1, -1}, {1, -1}, {-1, 1}}; + vector> visit(N, vector(N, false)); + + queue> q; + q.push({0, 0, 1}); + visit[0][0] = true; + + while (!q.empty()) { + auto [r, c, length] = q.front(); + q.pop(); + + if (r == N - 1 && c == N - 1) return length; + + for (auto [dr, dc] : directions) { + int nr = r + dr, nc = c + dc; + if (nr >= 0 && nc >= 0 && nr < N && nc < N && + grid[nr][nc] == 0 && !visit[nr][nc]) { + q.push({nr, nc, length + 1}); + visit[nr][nc] = true; + } + } + } + return -1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} grid + * @return {number} + */ + shortestPathBinaryMatrix(grid) { + const N = grid.length; + if (grid[0][0] === 1 || grid[N - 1][N - 1] === 1) return -1; + + const directions = [ + [0, 1], [1, 0], [0, -1], [-1, 0], + [1, 1], [-1, -1], [1, -1], [-1, 1] + ]; + const visit = Array.from({ length: N }, () => + Array(N).fill(false) + ); + + const q = new Queue([[0, 0, 1]]); + visit[0][0] = true; + + while (!q.isEmpty()) { + const [r, c, length] = q.pop(); + if (r === N - 1 && c === N - 1) return length; + + for (const [dr, dc] of directions) { + const nr = r + dr, nc = c + dc; + if (nr >= 0 && nc >= 0 && nr < N && nc < N && + grid[nr][nc] === 0 && !visit[nr][nc]) { + q.push([nr, nc, length + 1]); + visit[nr][nc] = true; + } + } + } + return -1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n ^ 2)$ + +--- + +## 2. Breadth First Search (Overwriting the Input) + +::tabs-start + +```python +class Solution: + def shortestPathBinaryMatrix(self, grid: list[list[int]]) -> int: + N = len(grid) + direct = [0, 1, 0, -1, 0, 1, 1, -1, -1, 1] + + if grid[0][0] or grid[N - 1][N - 1]: + return -1 + + q = deque([(0, 0)]) + grid[0][0] = 1 + + while q: + r, c = q.popleft() + dist = grid[r][c] + + if r == N - 1 and c == N - 1: + return dist + + for d in range(9): + nr, nc = r + direct[d], c + direct[d + 1] + if 0 <= nr < N and 0 <= nc < N and grid[nr][nc] == 0: + grid[nr][nc] = dist + 1 + q.append((nr, nc)) + + return -1 +``` + +```java +public class Solution { + public int shortestPathBinaryMatrix(int[][] grid) { + int N = grid.length; + int[] direct = {0, 1, 0, -1, 0, 1, 1, -1, -1, 1}; + + if (grid[0][0] == 1 || grid[N - 1][N - 1] == 1) + return -1; + + Queue q = new LinkedList<>(); + q.offer(new int[]{0, 0}); + grid[0][0] = 1; + + while (!q.isEmpty()) { + int[] cell = q.poll(); + int r = cell[0], c = cell[1]; + int dist = grid[r][c]; + + if (r == N - 1 && c == N - 1) + return dist; + + for (int d = 0; d < 9; d++) { + int nr = r + direct[d], nc = c + direct[d + 1]; + + if (nr >= 0 && nc >= 0 && nr < N && nc < N && grid[nr][nc] == 0) { + grid[nr][nc] = dist + 1; + q.offer(new int[]{nr, nc}); + } + } + } + + return -1; + } +} +``` + +```cpp +class Solution { +public: + int shortestPathBinaryMatrix(vector>& grid) { + int N = grid.size(); + int direct[10] = {0, 1, 0, -1, 0, 1, 1, -1, -1, 1}; + + if (grid[0][0] || grid[N - 1][N - 1]) + return -1; + + queue> q; + q.push({0, 0}); + grid[0][0] = 1; + + while (!q.empty()) { + auto [r, c] = q.front(); + q.pop(); + int dist = grid[r][c]; + + if (r == N - 1 && c == N - 1) + return dist; + + for (int d = 0; d < 9; d++) { + int nr = r + direct[d], nc = c + direct[d + 1]; + + if (nr >= 0 && nc >= 0 && nr < N && nc < N && grid[nr][nc] == 0) { + grid[nr][nc] = dist + 1; + q.push({nr, nc}); + } + } + } + + return -1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} grid + * @return {number} + */ + shortestPathBinaryMatrix(grid) { + const N = grid.length; + const direct = [0, 1, 0, -1, 0, 1, 1, -1, -1, 1]; + + if (grid[0][0] || grid[N - 1][N - 1]) + return -1; + + let q = [[0, 0]]; + grid[0][0] = 1; + + while (q.length) { + let [r, c] = q.shift(); + let dist = grid[r][c]; + + if (r === N - 1 && c === N - 1) + return dist; + + for (let d = 0; d < 9; d++) { + let nr = r + direct[d], nc = c + direct[d + 1]; + + if (nr >= 0 && nc >= 0 && nr < N && nc < N && grid[nr][nc] === 0) { + grid[nr][nc] = dist + 1; + q.push([nr, nc]); + } + } + } + + return -1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n ^ 2)$ + +--- + +## 3. Bidirectional Breadth First Search + +::tabs-start + +```python +class Solution: + def shortestPathBinaryMatrix(self, grid: list[list[int]]) -> int: + N = len(grid) + if grid[0][0] or grid[N - 1][N - 1]: + return -1 + if N == 1: + return 1 + + direct = [0, 1, 0, -1, 0, 1, 1, -1, -1, 1] + q1 = deque([(0, 0)]) + q2 = deque([(N - 1, N - 1)]) + grid[0][0] = -1 + grid[N - 1][N - 1] = -2 + + res = 2 + start, end = -1, -2 + while q1 and q2: + for _ in range(len(q1)): + r, c = q1.popleft() + for d in range(9): + nr, nc = r + direct[d], c + direct[d + 1] + if 0 <= nr < N and 0 <= nc < N: + if grid[nr][nc] == end: + return res + if grid[nr][nc] == 0: + grid[nr][nc] = start + q1.append((nr, nc)) + + q1, q2 = q2, q1 + start, end = end, start + res += 1 + + return -1 +``` + +```java +public class Solution { + public int shortestPathBinaryMatrix(int[][] grid) { + int N = grid.length; + if (grid[0][0] == 1 || grid[N - 1][N - 1] == 1) return -1; + if (N == 1) return 1; + + int[] direct = {0, 1, 0, -1, 0, 1, 1, -1, -1, 1}; + Queue q1 = new LinkedList<>(), q2 = new LinkedList<>(); + q1.offer(new int[]{0, 0}); + q2.offer(new int[]{N - 1, N - 1}); + grid[0][0] = -1; + grid[N - 1][N - 1] = -2; + + int res = 2, start = -1, end = -2; + while (!q1.isEmpty() && !q2.isEmpty()) { + for (int i = q1.size(); i > 0; i--) { + int[] cell = q1.poll(); + int r = cell[0], c = cell[1]; + + for (int d = 0; d < 9; d++) { + int nr = r + direct[d], nc = c + direct[d + 1]; + if (nr >= 0 && nc >= 0 && nr < N && nc < N) { + if (grid[nr][nc] == end) return res; + if (grid[nr][nc] == 0) { + grid[nr][nc] = start; + q1.offer(new int[]{nr, nc}); + } + } + } + } + Queue temp = q1; + q1 = q2; + q2 = temp; + int tempVal = start; + start = end; + end = tempVal; + res++; + } + + return -1; + } +} +``` + +```cpp +class Solution { +public: + int shortestPathBinaryMatrix(vector>& grid) { + int N = grid.size(); + if (grid[0][0] || grid[N - 1][N - 1]) return -1; + if (N == 1) return 1; + + int direct[10] = {0, 1, 0, -1, 0, 1, 1, -1, -1, 1}; + queue> q1, q2; + q1.push({0, 0}); + q2.push({N - 1, N - 1}); + grid[0][0] = -1; + grid[N - 1][N - 1] = -2; + + int res = 2, start = -1, end = -2; + while (!q1.empty() && !q2.empty()) { + for (int i = q1.size(); i > 0; i--) { + auto [r, c] = q1.front(); + q1.pop(); + + for (int d = 0; d < 9; d++) { + int nr = r + direct[d], nc = c + direct[d + 1]; + if (nr >= 0 && nc >= 0 && nr < N && nc < N) { + if (grid[nr][nc] == end) return res; + if (grid[nr][nc] == 0) { + grid[nr][nc] = start; + q1.push({nr, nc}); + } + } + } + } + swap(q1, q2); + swap(start, end); + res++; + } + + return -1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} grid + * @return {number} + */ + shortestPathBinaryMatrix(grid) { + const N = grid.length; + if (grid[0][0] || grid[N - 1][N - 1]) return -1; + if (N === 1) return 1; + + const direct = [0, 1, 0, -1, 0, 1, 1, -1, -1, 1]; + let q1 = new Queue([[0, 0]]); + let q2 = new Queue([[N - 1, N - 1]]); + grid[0][0] = -1; + grid[N - 1][N - 1] = -2; + + let res = 2, start = -1, end = -2; + while (!q1.isEmpty() && !q2.isEmpty()) { + for (let i = q1.size(); i > 0; i--) { + const [r, c] = q1.pop(); + for (let d = 0; d < 9; d++) { + let nr = r + direct[d], nc = c + direct[d + 1]; + if (nr >= 0 && nc >= 0 && nr < N && nc < N) { + if (grid[nr][nc] === end) return res; + if (grid[nr][nc] === 0) { + grid[nr][nc] = start; + q1.push([nr, nc]); + } + } + } + } + [q1, q2] = [q2, q1]; + [start, end] = [end, start]; + res++; + } + + return -1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n ^ 2)$ \ No newline at end of file diff --git a/articles/single-element-in-a-sorted-array.md b/articles/single-element-in-a-sorted-array.md new file mode 100644 index 000000000..e2a5a8b0e --- /dev/null +++ b/articles/single-element-in-a-sorted-array.md @@ -0,0 +1,438 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def singleNonDuplicate(self, nums: List[int]) -> int: + n = len(nums) + for i in range(n): + if ((i and nums[i] == nums[i - 1]) or + (i < n - 1 and nums[i] == nums[i + 1]) + ): + continue + return nums[i] +``` + +```java +public class Solution { + public int singleNonDuplicate(int[] nums) { + int n = nums.length; + for (int i = 0; i < n; i++) { + if ((i > 0 && nums[i] == nums[i - 1]) || + (i < n - 1 && nums[i] == nums[i + 1])) { + continue; + } + return nums[i]; + } + return -1; + } +} +``` + +```cpp +class Solution { +public: + int singleNonDuplicate(vector& nums) { + int n = nums.size(); + for (int i = 0; i < n; i++) { + if ((i > 0 && nums[i] == nums[i - 1]) || + (i < n - 1 && nums[i] == nums[i + 1])) { + continue; + } + return nums[i]; + } + return -1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + singleNonDuplicate(nums) { + const n = nums.length; + for (let i = 0; i < n; i++) { + if ((i > 0 && nums[i] === nums[i - 1]) || + (i < n - 1 && nums[i] === nums[i + 1])) { + continue; + } + return nums[i]; + } + return -1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ + +--- + +## 2. Brute Force (Bitwise Xor) + +::tabs-start + +```python +class Solution: + def singleNonDuplicate(self, nums: List[int]) -> int: + xorr = 0 + for num in nums: + xorr ^= num + return xorr +``` + +```java +public class Solution { + public int singleNonDuplicate(int[] nums) { + int xorr = 0; + for (int num : nums) { + xorr ^= num; + } + return xorr; + } +} +``` + +```cpp +class Solution { +public: + int singleNonDuplicate(vector& nums) { + int xorr = 0; + for (int num : nums) { + xorr ^= num; + } + return xorr; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + singleNonDuplicate(nums) { + let xorr = 0; + for (const num of nums) { + xorr ^= num; + } + return xorr; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ + +--- + +## 3. Binary Search + +::tabs-start + +```python +class Solution: + def singleNonDuplicate(self, nums: List[int]) -> int: + l, r = 0, len(nums) - 1 + + while l <= r: + m = l + ((r - l) // 2) + if ((m - 1 < 0 or nums[m - 1] != nums[m]) and + (m + 1 == len(nums) or nums[m] != nums[m + 1])): + return nums[m] + + leftSize = m - 1 if nums[m - 1] == nums[m] else m + if leftSize % 2: + r = m - 1 + else: + l = m + 1 +``` + +```java +public class Solution { + public int singleNonDuplicate(int[] nums) { + int l = 0, r = nums.length - 1; + + while (l <= r) { + int m = l + (r - l) / 2; + if ((m - 1 < 0 || nums[m - 1] != nums[m]) && + (m + 1 == nums.length || nums[m] != nums[m + 1])) { + return nums[m]; + } + + int leftSize = (m - 1 >= 0 && nums[m - 1] == nums[m]) ? m - 1 : m; + if (leftSize % 2 == 1) { + r = m - 1; + } else { + l = m + 1; + } + } + + return -1; + } +} +``` + +```cpp +class Solution { +public: + int singleNonDuplicate(vector& nums) { + int l = 0, r = nums.size() - 1; + + while (l <= r) { + int m = l + (r - l) / 2; + if ((m - 1 < 0 || nums[m - 1] != nums[m]) && + (m + 1 == nums.size() || nums[m] != nums[m + 1])) { + return nums[m]; + } + + int leftSize = (m - 1 >= 0 && nums[m - 1] == nums[m]) ? m - 1 : m; + if (leftSize % 2 == 1) { + r = m - 1; + } else { + l = m + 1; + } + } + + return -1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + singleNonDuplicate(nums) { + let l = 0, r = nums.length - 1; + + while (l <= r) { + let m = l + Math.floor((r - l) / 2); + if ((m - 1 < 0 || nums[m - 1] !== nums[m]) && + (m + 1 === nums.length || nums[m] !== nums[m + 1])) { + return nums[m]; + } + + let leftSize = (m - 1 >= 0 && nums[m - 1] === nums[m]) ? m - 1 : m; + if (leftSize % 2 === 1) { + r = m - 1; + } else { + l = m + 1; + } + } + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(\log n)$ +* Space complexity: $O(1)$ + +--- + +## 4. Binary Search On Even Indexes + +::tabs-start + +```python +class Solution: + def singleNonDuplicate(self, nums: List[int]) -> int: + l, r = 0, len(nums) - 1 + + while l < r: + m = l + (r - l) // 2 + if m & 1: + m -= 1 + if nums[m] != nums[m + 1]: + r = m + else: + l = m + 2 + + return nums[l] +``` + +```java +public class Solution { + public int singleNonDuplicate(int[] nums) { + int l = 0, r = nums.length - 1; + + while (l < r) { + int m = l + (r - l) / 2; + if ((m & 1) == 1) { + m--; + } + if (nums[m] != nums[m + 1]) { + r = m; + } else { + l = m + 2; + } + } + + return nums[l]; + } +} +``` + +```cpp +class Solution { +public: + int singleNonDuplicate(vector& nums) { + int l = 0, r = nums.size() - 1; + + while (l < r) { + int m = l + (r - l) / 2; + if (m & 1) { + m--; + } + if (nums[m] != nums[m + 1]) { + r = m; + } else { + l = m + 2; + } + } + + return nums[l]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + singleNonDuplicate(nums) { + let l = 0, r = nums.length - 1; + + while (l < r) { + let m = Math.floor(l + (r - l) / 2); + if (m & 1) { + m--; + } + if (nums[m] !== nums[m + 1]) { + r = m; + } else { + l = m + 2; + } + } + + return nums[l]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(\log n)$ +* Space complexity: $O(1)$ + +--- + +## 5. Binary Search + Bit Manipulation + +::tabs-start + +```python +class Solution: + def singleNonDuplicate(self, nums: List[int]) -> int: + l, r = 0, len(nums) - 1 + + while l < r: + m = (l + r) >> 1 + if nums[m] != nums[m ^ 1]: + r = m + else: + l = m + 1 + + return nums[l] +``` + +```java +public class Solution { + public int singleNonDuplicate(int[] nums) { + int l = 0, r = nums.length - 1; + + while (l < r) { + int m = (l + r) >> 1; + if (nums[m] != nums[m ^ 1]) { + r = m; + } else { + l = m + 1; + } + } + + return nums[l]; + } +} +``` + +```cpp +class Solution { +public: + int singleNonDuplicate(vector& nums) { + int l = 0, r = nums.size() - 1; + + while (l < r) { + int m = (l + r) >> 1; + if (nums[m] != nums[m ^ 1]) { + r = m; + } else { + l = m + 1; + } + } + + return nums[l]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + singleNonDuplicate(nums) { + let l = 0, r = nums.length - 1; + + while (l < r) { + let m = (l + r) >> 1; + if (nums[m] !== nums[m ^ 1]) { + r = m; + } else { + l = m + 1; + } + } + + return nums[l]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(\log n)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/sort-list.md b/articles/sort-list.md new file mode 100644 index 000000000..768ec1c4c --- /dev/null +++ b/articles/sort-list.md @@ -0,0 +1,730 @@ +## 1. Convert To Array + +::tabs-start + +```python +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, val=0, next=None): +# self.val = val +# self.next = next +class Solution: + def sortList(self, head: Optional[ListNode]) -> Optional[ListNode]: + arr = [] + cur = head + + while cur: + arr.append(cur.val) + cur = cur.next + + arr.sort() + cur = head + for val in arr: + cur.val = val + cur = cur.next + + return head +``` + +```java +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode() {} + * ListNode(int val) { this.val = val; } + * ListNode(int val, ListNode next) { this.val = val; this.next = next; } + * } + */ +public class Solution { + public ListNode sortList(ListNode head) { + if (head == null) return null; + + List arr = new ArrayList<>(); + ListNode cur = head; + + while (cur != null) { + arr.add(cur.val); + cur = cur.next; + } + + Collections.sort(arr); + cur = head; + for (int val : arr) { + cur.val = val; + cur = cur.next; + } + + return head; + } +} +``` + +```cpp +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode() : val(0), next(nullptr) {} + * ListNode(int x) : val(x), next(nullptr) {} + * ListNode(int x, ListNode *next) : val(x), next(next) {} + * }; + */ +class Solution { +public: + ListNode* sortList(ListNode* head) { + if (!head) return nullptr; + + vector arr; + ListNode* cur = head; + + while (cur) { + arr.push_back(cur->val); + cur = cur->next; + } + + sort(arr.begin(), arr.end()); + cur = head; + for (int val : arr) { + cur->val = val; + cur = cur->next; + } + + return head; + } +}; +``` + +```javascript +/** + * Definition for singly-linked list. + * class ListNode { + * constructor(val = 0, next = null) { + * this.val = val; + * this.next = next; + * } + * } + */ +class Solution { + /** + * @param {ListNode} head + * @return {ListNode} + */ + sortList(head) { + if (!head) return null; + + let arr = []; + let cur = head; + + while (cur) { + arr.push(cur.val); + cur = cur.next; + } + + arr.sort((a, b) => a - b); + cur = head; + for (let val of arr) { + cur.val = val; + cur = cur.next; + } + + return head; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Recursive Merge Sort + +::tabs-start + +```python +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, val=0, next=None): +# self.val = val +# self.next = next +class Solution: + def sortList(self, head: Optional[ListNode]) -> Optional[ListNode]: + if not head or not head.next: + return head + + left = head + right = self.getMid(head) + tmp = right.next + right.next = None + right = tmp + + left = self.sortList(left) + right = self.sortList(right) + return self.merge(left, right) + + def getMid(self, head): + slow, fast = head, head.next + while fast and fast.next: + slow = slow.next + fast = fast.next.next + return slow + + def merge(self, list1, list2): + tail = dummy = ListNode() + while list1 and list2: + if list1.val < list2.val: + tail.next = list1 + list1 = list1.next + else: + tail.next = list2 + list2 = list2.next + tail = tail.next + + if list1: + tail.next = list1 + if list2: + tail.next = list2 + + return dummy.next +``` + +```java +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode() {} + * ListNode(int val) { this.val = val; } + * ListNode(int val, ListNode next) { this.val = val; this.next = next; } + * } + */ +public class Solution { + public ListNode sortList(ListNode head) { + if (head == null || head.next == null) { + return head; + } + + ListNode left = head; + ListNode right = getMid(head); + ListNode temp = right.next; + right.next = null; + right = temp; + + left = sortList(left); + right = sortList(right); + return merge(left, right); + } + + private ListNode getMid(ListNode head) { + ListNode slow = head, fast = head.next; + while (fast != null && fast.next != null) { + slow = slow.next; + fast = fast.next.next; + } + return slow; + } + + private ListNode merge(ListNode list1, ListNode list2) { + ListNode dummy = new ListNode(0); + ListNode tail = dummy; + + while (list1 != null && list2 != null) { + if (list1.val < list2.val) { + tail.next = list1; + list1 = list1.next; + } else { + tail.next = list2; + list2 = list2.next; + } + tail = tail.next; + } + + if (list1 != null) { + tail.next = list1; + } + if (list2 != null) { + tail.next = list2; + } + + return dummy.next; + } +} +``` + +```cpp +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode() : val(0), next(nullptr) {} + * ListNode(int x) : val(x), next(nullptr) {} + * ListNode(int x, ListNode *next) : val(x), next(next) {} + * }; + */ +class Solution { +public: + ListNode* sortList(ListNode* head) { + if (!head || !head->next) { + return head; + } + + ListNode* left = head; + ListNode* right = getMid(head); + ListNode* temp = right->next; + right->next = nullptr; + right = temp; + + left = sortList(left); + right = sortList(right); + return merge(left, right); + } + +private: + ListNode* getMid(ListNode* head) { + ListNode* slow = head; + ListNode* fast = head->next; + while (fast && fast->next) { + slow = slow->next; + fast = fast->next->next; + } + return slow; + } + + ListNode* merge(ListNode* list1, ListNode* list2) { + ListNode dummy(0); + ListNode* tail = &dummy; + + while (list1 && list2) { + if (list1->val < list2->val) { + tail->next = list1; + list1 = list1->next; + } else { + tail->next = list2; + list2 = list2->next; + } + tail = tail->next; + } + + if (list1) { + tail->next = list1; + } + if (list2) { + tail->next = list2; + } + + return dummy.next; + } +}; +``` + +```javascript +/** + * Definition for singly-linked list. + * class ListNode { + * constructor(val = 0, next = null) { + * this.val = val; + * this.next = next; + * } + * } + */ +class Solution { + /** + * @param {ListNode} head + * @return {ListNode} + */ + sortList(head) { + if (!head || !head.next) { + return head; + } + + let left = head; + let right = this.getMid(head); + let temp = right.next; + right.next = null; + right = temp; + + left = this.sortList(left); + right = this.sortList(right); + return this.merge(left, right); + } + + /** + * @param {ListNode} head + * @return {ListNode} + */ + getMid(head) { + let slow = head, fast = head.next; + while (fast && fast.next) { + slow = slow.next; + fast = fast.next.next; + } + return slow; + } + + /** + * @param {ListNode} list1 + * @param {ListNode} list2 + * @return {ListNode} + */ + merge(list1, list2) { + let dummy = new ListNode(0); + let tail = dummy; + + while (list1 && list2) { + if (list1.val < list2.val) { + tail.next = list1; + list1 = list1.next; + } else { + tail.next = list2; + list2 = list2.next; + } + tail = tail.next; + } + + if (list1) { + tail.next = list1; + } + if (list2) { + tail.next = list2; + } + + return dummy.next; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(\log n)$ for recursion stack. + +--- + +## 3. Iterative Merge Sort + +::tabs-start + +```python +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, val=0, next=None): +# self.val = val +# self.next = next +class Solution: + def sortList(self, head: Optional[ListNode]) -> Optional[ListNode]: + if not head or not head.next: + return head + + length = 0 + cur = head + while cur: + length += 1 + cur = cur.next + + dummy = ListNode(0) + dummy.next = head + step = 1 + + while step < length: + prev, curr = dummy, dummy.next + while curr: + left = curr + right = self.split(left, step) + curr = self.split(right, step) + merged = self.merge(left, right) + prev.next = merged + while prev.next: + prev = prev.next + step *= 2 + + return dummy.next + + def split(self, head, step): + if not head: + return None + for _ in range(step - 1): + if not head.next: + break + head = head.next + next_part = head.next + head.next = None + return next_part + + def merge(self, list1, list2): + tail = dummy = ListNode(0) + while list1 and list2: + if list1.val < list2.val: + tail.next = list1 + list1 = list1.next + else: + tail.next = list2 + list2 = list2.next + tail = tail.next + + tail.next = list1 or list2 + return dummy.next +``` + +```java +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode() {} + * ListNode(int val) { this.val = val; } + * ListNode(int val, ListNode next) { this.val = val; this.next = next; } + * } + */ +public class Solution { + public ListNode sortList(ListNode head) { + if (head == null || head.next == null) { + return head; + } + + int length = 0; + ListNode cur = head; + while (cur != null) { + length++; + cur = cur.next; + } + + ListNode dummy = new ListNode(0); + dummy.next = head; + int step = 1; + + while (step < length) { + ListNode prev = dummy, curr = dummy.next; + while (curr != null) { + ListNode left = curr; + ListNode right = split(left, step); + curr = split(right, step); + ListNode merged = merge(left, right); + prev.next = merged; + while (prev.next != null) { + prev = prev.next; + } + } + step *= 2; + } + + return dummy.next; + } + + private ListNode split(ListNode head, int step) { + if (head == null) return null; + for (int i = 0; i < step - 1 && head.next != null; i++) { + head = head.next; + } + ListNode nextPart = head.next; + head.next = null; + return nextPart; + } + + private ListNode merge(ListNode list1, ListNode list2) { + ListNode dummy = new ListNode(0); + ListNode tail = dummy; + + while (list1 != null && list2 != null) { + if (list1.val < list2.val) { + tail.next = list1; + list1 = list1.next; + } else { + tail.next = list2; + list2 = list2.next; + } + tail = tail.next; + } + + tail.next = (list1 != null) ? list1 : list2; + return dummy.next; + } +} +``` + +```cpp +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode() {} + * ListNode(int val) { this.val = val; } + * ListNode(int val, ListNode next) { this.val = val; this.next = next; } + * } + */ +class Solution { +public: + ListNode* sortList(ListNode* head) { + if (!head || !head->next) { + return head; + } + + int length = 0; + ListNode* cur = head; + while (cur) { + length++; + cur = cur->next; + } + + ListNode dummy(0); + dummy.next = head; + int step = 1; + + while (step < length) { + ListNode* prev = &dummy, *curr = dummy.next; + while (curr) { + ListNode* left = curr; + ListNode* right = split(left, step); + curr = split(right, step); + ListNode* merged = merge(left, right); + prev->next = merged; + while (prev->next) { + prev = prev->next; + } + } + step *= 2; + } + + return dummy.next; + } + +private: + ListNode* split(ListNode* head, int step) { + if (!head) return nullptr; + for (int i = 0; i < step - 1 && head->next; i++) { + head = head->next; + } + ListNode* nextPart = head->next; + head->next = nullptr; + return nextPart; + } + + ListNode* merge(ListNode* list1, ListNode* list2) { + ListNode dummy(0); + ListNode* tail = &dummy; + + while (list1 && list2) { + if (list1->val < list2->val) { + tail->next = list1; + list1 = list1->next; + } else { + tail->next = list2; + list2 = list2->next; + } + tail = tail->next; + } + + tail->next = list1 ? list1 : list2; + return dummy.next; + } +}; +``` + +```javascript +/** + * Definition for singly-linked list. + * class ListNode { + * constructor(val = 0, next = null) { + * this.val = val; + * this.next = next; + * } + * } + */ +class Solution { + /** + * @param {ListNode} head + * @return {ListNode} + */ + sortList(head) { + if (!head || !head.next) { + return head; + } + + let length = 0; + let cur = head; + while (cur) { + length++; + cur = cur.next; + } + + let dummy = new ListNode(0); + dummy.next = head; + let step = 1; + + while (step < length) { + let prev = dummy, curr = dummy.next; + while (curr) { + let left = curr; + let right = this.split(left, step); + curr = this.split(right, step); + let merged = this.merge(left, right); + prev.next = merged; + while (prev.next) { + prev = prev.next; + } + } + step *= 2; + } + + return dummy.next; + } + + /** + * @param {ListNode} head + * @param {number} step + * @return {ListNode} + */ + split(head, step) { + if (!head) return null; + for (let i = 0; i < step - 1 && head.next; i++) { + head = head.next; + } + let nextPart = head.next; + head.next = null; + return nextPart; + } + + /** + * @param {ListNode} list1 + * @param {ListNode} list2 + * @return {ListNode} + */ + merge(list1, list2) { + let dummy = new ListNode(0); + let tail = dummy; + + while (list1 && list2) { + if (list1.val < list2.val) { + tail.next = list1; + list1 = list1.next; + } else { + tail.next = list2; + list2 = list2.next; + } + tail = tail.next; + } + + tail.next = list1 || list2; + return dummy.next; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/successful-pairs-of-spells-and-potions.md b/articles/successful-pairs-of-spells-and-potions.md new file mode 100644 index 000000000..87faf3177 --- /dev/null +++ b/articles/successful-pairs-of-spells-and-potions.md @@ -0,0 +1,461 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def successfulPairs(self, spells: List[int], potions: List[int], success: int) -> List[int]: + res = [] + + for s in spells: + cnt = 0 + for p in potions: + if s * p >= success: + cnt += 1 + res.append(cnt) + + return res +``` + +```java +public class Solution { + public int[] successfulPairs(int[] spells, int[] potions, long success) { + int[] res = new int[spells.length]; + + for (int i = 0; i < spells.length; i++) { + int cnt = 0; + for (int p : potions) { + if ((long) spells[i] * p >= success) { + cnt++; + } + } + res[i] = cnt; + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector successfulPairs(vector& spells, vector& potions, long long success) { + vector res(spells.size()); + + for (int i = 0; i < spells.size(); i++) { + int cnt = 0; + for (int p : potions) { + if ((long long) spells[i] * p >= success) { + cnt++; + } + } + res[i] = cnt; + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} spells + * @param {number[]} potions + * @param {number} success + * @return {number[]} + */ + successfulPairs(spells, potions, success) { + let res = []; + + for (let s of spells) { + let cnt = 0; + for (let p of potions) { + if (s * p >= success) { + cnt++; + } + } + res.push(cnt); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * m)$ +* Space complexity: $O(1)$ + +> Where $n$ and $m$ are the sizes of the arrays $spells$ and $potions$ respectively. + +--- + +## 2. Sorting + Binary Search + +::tabs-start + +```python +class Solution: + def successfulPairs(self, spells: List[int], potions: List[int], success: int) -> List[int]: + potions.sort() + res = [] + + for s in spells: + l, r = 0, len(potions) - 1 + idx = len(potions) + + while l <= r: + m = (l + r) // 2 + if s * potions[m] >= success: + r = m - 1 + idx = m + else: + l = m + 1 + + res.append(len(potions) - idx) + + return res +``` + +```java +public class Solution { + public int[] successfulPairs(int[] spells, int[] potions, long success) { + Arrays.sort(potions); + int[] res = new int[spells.length]; + + for (int i = 0; i < spells.length; i++) { + int l = 0, r = potions.length - 1, idx = potions.length; + + while (l <= r) { + int m = (l + r) / 2; + if ((long) spells[i] * potions[m] >= success) { + r = m - 1; + idx = m; + } else { + l = m + 1; + } + } + + res[i] = potions.length - idx; + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector successfulPairs(vector& spells, vector& potions, long long success) { + sort(potions.begin(), potions.end()); + vector res(spells.size()); + + for (int i = 0; i < spells.size(); i++) { + int l = 0, r = potions.size() - 1, idx = potions.size(); + + while (l <= r) { + int m = (l + r) / 2; + if ((long long) spells[i] * potions[m] >= success) { + r = m - 1; + idx = m; + } else { + l = m + 1; + } + } + + res[i] = potions.size() - idx; + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} spells + * @param {number[]} potions + * @param {number} success + * @return {number[]} + */ + successfulPairs(spells, potions, success) { + potions.sort((a, b) => a - b); + let res = []; + + for (let s of spells) { + let l = 0, r = potions.length - 1, idx = potions.length; + + while (l <= r) { + let m = Math.floor((l + r) / 2); + if (s * potions[m] >= success) { + r = m - 1; + idx = m; + } else { + l = m + 1; + } + } + + res.push(potions.length - idx); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O((m + n) * \log m)$ +* Space complexity: + * $O(1)$ or $O(m)$ extra space depending on the sorting algorithm. + * $O(n)$ space for the output array. + +> Where $n$ and $m$ are the sizes of the arrays $spells$ and $potions$ respectively. + +--- + +## 3. Sorting + Two Pointers + +::tabs-start + +```python +class Solution: + def successfulPairs(self, spells: List[int], potions: List[int], success: int) -> List[int]: + n, m = len(spells), len(potions) + S = spells[:] + count = defaultdict(int) + spells.sort() + potions.sort() + + j = m - 1 + for i in range(n): + while j >= 0 and spells[i] * potions[j] >= success: + j -= 1 + count[spells[i]] = m - j - 1 + + res = [0] * n + for i in range(n): + res[i] = count[S[i]] + + return res +``` + +```java +public class Solution { + public int[] successfulPairs(int[] spells, int[] potions, long success) { + int n = spells.length, m = potions.length; + int[] S = Arrays.copyOf(spells, n); + Map count = new HashMap<>(); + Arrays.sort(spells); + Arrays.sort(potions); + + int j = m - 1; + for (int i = 0; i < n; i++) { + while (j >= 0 && (long) spells[i] * potions[j] >= success) { + j--; + } + count.put(spells[i], m - j - 1); + } + + int[] res = new int[n]; + for (int i = 0; i < n; i++) { + res[i] = count.get(S[i]); + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector successfulPairs(vector& spells, vector& potions, long long success) { + int n = spells.size(), m = potions.size(); + vector S = spells; + unordered_map count; + sort(spells.begin(), spells.end()); + sort(potions.begin(), potions.end()); + + int j = m - 1; + for (int i = 0; i < n; i++) { + while (j >= 0 && (long long) spells[i] * potions[j] >= success) { + j--; + } + count[spells[i]] = m - j - 1; + } + + vector res(n); + for (int i = 0; i < n; i++) { + res[i] = count[S[i]]; + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} spells + * @param {number[]} potions + * @param {number} success + * @return {number[]} + */ + successfulPairs(spells, potions, success) { + const n = spells.length, m = potions.length; + const S = [...spells]; + const count = new Map(); + spells.sort((a, b) => a - b); + potions.sort((a, b) => a - b); + + let j = m - 1; + for (let i = 0; i < n; i++) { + while (j >= 0 && spells[i] * potions[j] >= success) { + j--; + } + count.set(spells[i], m - j - 1); + } + + return S.map(s => count.get(s)); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n + m\log m)$ +* Space complexity: + * $O(1)$ or $O(m + n)$ extra space depending on the sorting algorithm. + * $O(n)$ space for the output array. + +> Where $n$ and $m$ are the sizes of the arrays $spells$ and $potions$ respectively. + +--- + +## 4. Sorting + Two Pointers (Optimal) + +::tabs-start + +```python +class Solution: + def successfulPairs(self, spells: List[int], potions: List[int], success: int) -> List[int]: + n, m = len(spells), len(potions) + sIdx = list(range(n)) + sIdx.sort(key=lambda x: spells[x]) + potions.sort() + + j = m - 1 + res = [0] * n + for i in range(n): + while j >= 0 and spells[sIdx[i]] * potions[j] >= success: + j -= 1 + res[sIdx[i]] = m - j - 1 + + return res +``` + +```java +public class Solution { + public int[] successfulPairs(int[] spells, int[] potions, long success) { + int n = spells.length, m = potions.length; + Integer[] sIdx = new Integer[n]; + for (int i = 0; i < n; i++) sIdx[i] = i; + + Arrays.sort(sIdx, Comparator.comparingInt(i -> spells[i])); + Arrays.sort(potions); + + int j = m - 1; + int[] res = new int[n]; + for (int i = 0; i < n; i++) { + while (j >= 0 && (long) spells[sIdx[i]] * potions[j] >= success) { + j--; + } + res[sIdx[i]] = m - j - 1; + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector successfulPairs(vector& spells, vector& potions, long long success) { + int n = spells.size(), m = potions.size(); + vector sIdx(n); + for (int i = 0; i < n; i++) sIdx[i] = i; + + sort(sIdx.begin(), sIdx.end(), [&](int a, int b) { + return spells[a] < spells[b]; + }); + + sort(potions.begin(), potions.end()); + + int j = m - 1; + vector res(n); + for (int i = 0; i < n; i++) { + while (j >= 0 && (long long) spells[sIdx[i]] * potions[j] >= success) { + j--; + } + res[sIdx[i]] = m - j - 1; + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} spells + * @param {number[]} potions + * @param {number} success + * @return {number[]} + */ + successfulPairs(spells, potions, success) { + const n = spells.length, m = potions.length; + const sIdx = Array.from({ length: n }, (_, i) => i); + + sIdx.sort((a, b) => spells[a] - spells[b]); + potions.sort((a, b) => a - b); + + let j = m - 1; + const res = new Array(n).fill(0); + + for (let i = 0; i < n; i++) { + while (j >= 0 && spells[sIdx[i]] * potions[j] >= success) { + j--; + } + res[sIdx[i]] = m - j - 1; + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n + m\log m)$ +* Space complexity: + * $O(1)$ or $O(m + n)$ extra space depending on the sorting algorithm. + * $O(n)$ space for the output array. + +> Where $n$ and $m$ are the sizes of the arrays $spells$ and $potions$ respectively. \ No newline at end of file diff --git a/articles/sum-of-subarray-minimums.md b/articles/sum-of-subarray-minimums.md new file mode 100644 index 000000000..f1103db9b --- /dev/null +++ b/articles/sum-of-subarray-minimums.md @@ -0,0 +1,596 @@ +## 1. Brute FOrce + +::tabs-start + +```python +class Solution: + def sumSubarrayMins(self, arr: List[int]) -> int: + n, res = len(arr), 0 + MOD = 1000000007 + + for i in range(n): + minVal = arr[i] + for j in range(i, n): + minVal = min(minVal, arr[j]) + res = (res + minVal) % MOD + + return res +``` + +```java +public class Solution { + public int sumSubarrayMins(int[] arr) { + int n = arr.length, res = 0; + int MOD = 1000000007; + + for (int i = 0; i < n; i++) { + int minVal = arr[i]; + for (int j = i; j < n; j++) { + minVal = Math.min(minVal, arr[j]); + res = (res + minVal) % MOD; + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int sumSubarrayMins(vector& arr) { + int n = arr.size(), res = 0; + const int MOD = 1000000007; + + for (int i = 0; i < n; i++) { + int minVal = arr[i]; + for (int j = i; j < n; j++) { + minVal = min(minVal, arr[j]); + res = (res + minVal) % MOD; + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} arr + * @return {number} + */ + sumSubarrayMins(arr) { + const n = arr.length; + let res = 0; + const MOD = 1000000007; + + for (let i = 0; i < n; i++) { + let minVal = arr[i]; + for (let j = i; j < n; j++) { + minVal = Math.min(minVal, arr[j]); + res = (res + minVal) % MOD; + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(1)$ + +--- + +## 2. Monotonically Increasing Stack (Two Pass) + +::tabs-start + +```python +class Solution: + def sumSubarrayMins(self, arr: List[int]) -> int: + MOD = 10**9 + 7 + n = len(arr) + + # Compute previous smaller + prev_smaller = [-1] * n + stack = [] + for i in range(n): + while stack and arr[stack[-1]] > arr[i]: + stack.pop() + prev_smaller[i] = stack[-1] if stack else -1 + stack.append(i) + + # Compute next smaller + next_smaller = [n] * n + stack = [] + for i in range(n - 1, -1, -1): + while stack and arr[stack[-1]] >= arr[i]: + stack.pop() + next_smaller[i] = stack[-1] if stack else n + stack.append(i) + + res = 0 + for i in range(n): + left = i - prev_smaller[i] + right = next_smaller[i] - i + res = (res + arr[i] * left * right) % MOD + + return res +``` + +```java +public class Solution { + public int sumSubarrayMins(int[] arr) { + int MOD = 1000000007; + int n = arr.length; + + // Compute previous smaller + int[] prevSmaller = new int[n]; + Stack stack = new Stack<>(); + for (int i = 0; i < n; i++) { + while (!stack.isEmpty() && arr[stack.peek()] > arr[i]) { + stack.pop(); + } + prevSmaller[i] = stack.isEmpty() ? -1 : stack.peek(); + stack.push(i); + } + + // Compute next smaller + int[] nextSmaller = new int[n]; + stack = new Stack<>(); + for (int i = n - 1; i >= 0; i--) { + while (!stack.isEmpty() && arr[stack.peek()] >= arr[i]) { + stack.pop(); + } + nextSmaller[i] = stack.isEmpty() ? n : stack.peek(); + stack.push(i); + } + + // Calculate result + long res = 0; + for (int i = 0; i < n; i++) { + long left = i - prevSmaller[i]; + long right = nextSmaller[i] - i; + res = (res + arr[i] * left * right) % MOD; + } + + return (int) res; + } +} +``` + +```cpp +class Solution { +public: + int sumSubarrayMins(vector& arr) { + const int MOD = 1e9 + 7; + int n = arr.size(); + + // Compute previous smaller + vector prevSmaller(n, -1); + stack stack; + for (int i = 0; i < n; i++) { + while (!stack.empty() && arr[stack.top()] > arr[i]) { + stack.pop(); + } + prevSmaller[i] = stack.empty() ? -1 : stack.top(); + stack.push(i); + } + + // Compute next smaller + vector nextSmaller(n, n); + stack = {}; + for (int i = n - 1; i >= 0; i--) { + while (!stack.empty() && arr[stack.top()] >= arr[i]) { + stack.pop(); + } + nextSmaller[i] = stack.empty() ? n : stack.top(); + stack.push(i); + } + + // Calculate result + long long res = 0; + for (int i = 0; i < n; i++) { + long long left = i - prevSmaller[i]; + long long right = nextSmaller[i] - i; + res = (res + arr[i] * left * right) % MOD; + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} arr + * @return {number} + */ + sumSubarrayMins(arr) { + const MOD = 1e9 + 7; + const n = arr.length; + + // Compute previous smaller + const prevSmaller = new Array(n).fill(-1); + const stack = []; + for (let i = 0; i < n; i++) { + while (stack.length > 0 && arr[stack[stack.length - 1]] > arr[i]) { + stack.pop(); + } + prevSmaller[i] = stack.length > 0 ? stack[stack.length - 1] : -1; + stack.push(i); + } + + // Compute next smaller + const nextSmaller = new Array(n).fill(n); + stack.length = 0; + for (let i = n - 1; i >= 0; i--) { + while (stack.length > 0 && arr[stack[stack.length - 1]] >= arr[i]) { + stack.pop(); + } + nextSmaller[i] = stack.length > 0 ? stack[stack.length - 1] : n; + stack.push(i); + } + + // Calculate result + let res = 0; + for (let i = 0; i < n; i++) { + const left = i - prevSmaller[i]; + const right = nextSmaller[i] - i; + res = (res + arr[i] * left * right) % MOD; + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Monotonically Increasing Stack (One Pass) + +::tabs-start + +```python +class Solution: + def sumSubarrayMins(self, arr: List[int]) -> int: + MOD = 10 ** 9 + 7 + res = 0 + arr = [float("-inf")] + arr + [float("-inf")] + stack = [] # (index, num) + + for i, n in enumerate(arr): + while stack and n < stack[-1][1]: + j, m = stack.pop() + left = j - stack[-1][0] if stack else j + 1 + right = i - j + res = (res + m * left * right) % MOD + stack.append((i, n)) + + return res +``` + +```java +public class Solution { + public int sumSubarrayMins(int[] arr) { + int MOD = 1000000007; + int res = 0; + int[] newArr = new int[arr.length + 2]; + newArr[0] = Integer.MIN_VALUE; + newArr[newArr.length - 1] = Integer.MIN_VALUE; + System.arraycopy(arr, 0, newArr, 1, arr.length); + + Stack stack = new Stack<>(); + + for (int i = 0; i < newArr.length; i++) { + while (!stack.isEmpty() && newArr[i] < stack.peek()[1]) { + int[] top = stack.pop(); + int j = top[0], m = top[1]; + int left = stack.isEmpty() ? j + 1 : j - stack.peek()[0]; + int right = i - j; + res = (int) ((res + (long) m * left * right) % MOD); + } + stack.push(new int[]{i, newArr[i]}); + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int sumSubarrayMins(vector& arr) { + const int MOD = 1e9 + 7; + int res = 0; + vector newArr(arr.size() + 2, INT_MIN); + copy(arr.begin(), arr.end(), newArr.begin() + 1); + + stack> stack; + + for (int i = 0; i < newArr.size(); i++) { + while (!stack.empty() && newArr[i] < stack.top().second) { + auto [j, m] = stack.top(); + stack.pop(); + int left = stack.empty() ? j + 1 : j - stack.top().first; + int right = i - j; + res = (res + (long long) m * left * right % MOD) % MOD; + } + stack.emplace(i, newArr[i]); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} arr + * @return {number} + */ + sumSubarrayMins(arr) { + const MOD = 1e9 + 7; + let res = 0; + arr = [-Infinity, ...arr, -Infinity]; + let stack = []; + + for (let i = 0; i < arr.length; i++) { + while (stack.length > 0 && arr[i] < stack[stack.length - 1][1]) { + let [j, m] = stack.pop(); + let left = stack.length > 0 ? j - stack[stack.length - 1][0] : j + 1; + let right = i - j; + res = (res + m * left * right) % MOD; + } + stack.push([i, arr[i]]); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 4. Monotonically Increasing Stack (Optimal) + +::tabs-start + +```python +class Solution: + def sumSubarrayMins(self, arr: List[int]) -> int: + MOD = 10**9 + 7 + stack = [] + res, n = 0, len(arr) + + for i in range(n + 1): + while stack and (i == n or arr[i] < arr[stack[-1]]): + j = stack.pop() + left = j - (stack[-1] if stack else -1) + right = i - j + res = (res + arr[j] * left * right) % MOD + stack.append(i) + + return res +``` + +```java +public class Solution { + public int sumSubarrayMins(int[] arr) { + int MOD = 1000000007; + int n = arr.length; + Stack stack = new Stack<>(); + long res = 0; + + for (int i = 0; i <= n; i++) { + while (!stack.isEmpty() && (i == n || arr[i] < arr[stack.peek()])) { + int j = stack.pop(); + int left = j - (stack.isEmpty() ? -1 : stack.peek()); + int right = i - j; + res = (res + (long) arr[j] * left * right) % MOD; + } + stack.push(i); + } + + return (int) res; + } +} +``` + +```cpp +class Solution { +public: + int sumSubarrayMins(vector& arr) { + const int MOD = 1e9 + 7; + int n = arr.size(); + stack stack; + long long res = 0; + + for (int i = 0; i <= n; i++) { + while (!stack.empty() && (i == n || arr[i] < arr[stack.top()])) { + int j = stack.top(); + stack.pop(); + int left = j - (stack.empty() ? -1 : stack.top()); + int right = i - j; + res = (res + (long long) arr[j] * left * right) % MOD; + } + stack.push(i); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} arr + * @return {number} + */ + sumSubarrayMins(arr) { + const MOD = 1e9 + 7; + const n = arr.length; + const stack = []; + let res = 0; + + for (let i = 0; i <= n; i++) { + while (stack.length > 0 && (i === n || arr[i] < arr[stack[stack.length - 1]])) { + const j = stack.pop(); + const left = j - (stack.length > 0 ? stack[stack.length - 1] : -1); + const right = i - j; + res = (res + arr[j] * left * right) % MOD; + } + stack.push(i); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 5. Dynamic Programming + Stack + +::tabs-start + +```python +class Solution: + def sumSubarrayMins(self, arr: List[int]) -> int: + MOD = 10**9 + 7 + n = len(arr) + dp = [0] * n + stack, res = [], 0 + + for i in range(n): + while stack and arr[stack[-1]] > arr[i]: + stack.pop() + + j = stack[-1] if stack else -1 + dp[i] = (dp[j] if j != -1 else 0) + arr[i] * (i - j) + dp[i] %= MOD + res = (res + dp[i]) % MOD + stack.append(i) + + return res +``` + +```java +public class Solution { + public int sumSubarrayMins(int[] arr) { + int MOD = 1000000007; + int n = arr.length; + int[] dp = new int[n]; + Stack stack = new Stack<>(); + long res = 0; + + for (int i = 0; i < n; i++) { + while (!stack.isEmpty() && arr[stack.peek()] > arr[i]) { + stack.pop(); + } + + int j = stack.isEmpty() ? -1 : stack.peek(); + dp[i] = ((j != -1 ? dp[j] : 0) + arr[i] * (i - j)) % MOD; + res = (res + dp[i]) % MOD; + stack.push(i); + } + + return (int) res; + } +} +``` + +```cpp +class Solution { +public: + int sumSubarrayMins(vector& arr) { + const int MOD = 1e9 + 7; + int n = arr.size(); + vector dp(n, 0); + stack stack; + long long res = 0; + + for (int i = 0; i < n; i++) { + while (!stack.empty() && arr[stack.top()] > arr[i]) { + stack.pop(); + } + + int j = stack.empty() ? -1 : stack.top(); + dp[i] = ((j != -1 ? dp[j] : 0) + arr[i] * (i - j)) % MOD; + res = (res + dp[i]) % MOD; + stack.push(i); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} arr + * @return {number} + */ + sumSubarrayMins(arr) { + const MOD = 1e9 + 7; + const n = arr.length; + const dp = new Array(n).fill(0); + const stack = []; + let res = 0; + + for (let i = 0; i < n; i++) { + while (stack.length > 0 && arr[stack[stack.length - 1]] > arr[i]) { + stack.pop(); + } + + const j = stack.length > 0 ? stack[stack.length - 1] : -1; + dp[i] = ((j !== -1 ? dp[j] : 0) + arr[i] * (i - j)) % MOD; + res = (res + dp[i]) % MOD; + stack.push(i); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/swap-nodes-in-pairs.md b/articles/swap-nodes-in-pairs.md new file mode 100644 index 000000000..89edd0eee --- /dev/null +++ b/articles/swap-nodes-in-pairs.md @@ -0,0 +1,419 @@ +## 1. Convert TO Array + +::tabs-start + +```python +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, val=0, next=None): +# self.val = val +# self.next = next +class Solution: + def swapPairs(self, head: Optional[ListNode]) -> Optional[ListNode]: + if not head: + return None + arr = [] + cur = head + + while cur: + arr.append(cur) + cur = cur.next + + for i in range(0, len(arr) - 1, 2): + arr[i], arr[i + 1] = arr[i + 1], arr[i] + + for i in range(len(arr) - 1): + arr[i].next = arr[i + 1] + + arr[-1].next = None + return arr[0] +``` + +```java +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode() {} + * ListNode(int val) { this.val = val; } + * ListNode(int val, ListNode next) { this.val = val; this.next = next; } + * } + */ +public class Solution { + public ListNode swapPairs(ListNode head) { + if (head == null) return null; + + List arr = new ArrayList<>(); + ListNode cur = head; + + while (cur != null) { + arr.add(cur); + cur = cur.next; + } + + for (int i = 0; i < arr.size() - 1; i += 2) { + ListNode temp = arr.get(i); + arr.set(i, arr.get(i + 1)); + arr.set(i + 1, temp); + } + + for (int i = 0; i < arr.size() - 1; i++) { + arr.get(i).next = arr.get(i + 1); + } + + arr.get(arr.size() - 1).next = null; + return arr.get(0); + } +} +``` + +```cpp +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode() : val(0), next(nullptr) {} + * ListNode(int x) : val(x), next(nullptr) {} + * ListNode(int x, ListNode *next) : val(x), next(next) {} + * }; + */ +class Solution { +public: + ListNode* swapPairs(ListNode* head) { + if (!head) return nullptr; + + vector arr; + ListNode* cur = head; + + while (cur) { + arr.push_back(cur); + cur = cur->next; + } + + for (size_t i = 0; i + 1 < arr.size(); i += 2) { + swap(arr[i], arr[i + 1]); + } + + for (size_t i = 0; i + 1 < arr.size(); i++) { + arr[i]->next = arr[i + 1]; + } + + arr.back()->next = nullptr; + return arr[0]; + } +}; +``` + +```javascript +/** + * Definition for singly-linked list. + * class ListNode { + * constructor(val = 0, next = null) { + * this.val = val; + * this.next = next; + * } + * } + */ +class Solution { + /** + * @param {ListNode} head + * @return {ListNode} + */ + swapPairs(head) { + if (!head) return null; + + let arr = []; + let cur = head; + + while (cur) { + arr.push(cur); + cur = cur.next; + } + + for (let i = 0; i + 1 < arr.length; i += 2) { + [arr[i], arr[i + 1]] = [arr[i + 1], arr[i]]; + } + + for (let i = 0; i + 1 < arr.length; i++) { + arr[i].next = arr[i + 1]; + } + + arr[arr.length - 1].next = null; + return arr[0]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Recursion + +::tabs-start + +```python +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, val=0, next=None): +# self.val = val +# self.next = next +class Solution: + def swapPairs(self, head: Optional[ListNode]) -> Optional[ListNode]: + if not head or not head.next: + return head + + cur = head + nxt = head.next + cur.next = self.swapPairs(nxt.next) + nxt.next = cur + return nxt +``` + +```java +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode() {} + * ListNode(int val) { this.val = val; } + * ListNode(int val, ListNode next) { this.val = val; this.next = next; } + * } + */ +public class Solution { + public ListNode swapPairs(ListNode head) { + if (head == null || head.next == null) { + return head; + } + + ListNode cur = head; + ListNode nxt = head.next; + cur.next = swapPairs(nxt.next); + nxt.next = cur; + + return nxt; + } +} +``` + +```cpp +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode() : val(0), next(nullptr) {} + * ListNode(int x) : val(x), next(nullptr) {} + * ListNode(int x, ListNode *next) : val(x), next(next) {} + * }; + */ +class Solution { +public: + ListNode* swapPairs(ListNode* head) { + if (!head || !head->next) { + return head; + } + + ListNode* cur = head; + ListNode* nxt = head->next; + cur->next = swapPairs(nxt->next); + nxt->next = cur; + + return nxt; + } +}; +``` + +```javascript +/** + * Definition for singly-linked list. + * class ListNode { + * constructor(val = 0, next = null) { + * this.val = val; + * this.next = next; + * } + * } + */ +class Solution { + /** + * @param {ListNode} head + * @return {ListNode} + */ + swapPairs(head) { + if (!head || !head.next) { + return head; + } + + let cur = head; + let nxt = head.next; + cur.next = this.swapPairs(nxt.next); + nxt.next = cur; + + return nxt; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ for recursion stack. + +--- + +## 3. Iteration + +::tabs-start + +```python +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, val=0, next=None): +# self.val = val +# self.next = next +class Solution: + def swapPairs(self, head: Optional[ListNode]) -> Optional[ListNode]: + dummy = ListNode(0, head) + prev, curr = dummy, head + + while curr and curr.next: + nxtPair = curr.next.next + second = curr.next + + # Reverse this pair + second.next = curr + curr.next = nxtPair + prev.next = second + + # Update pointers + prev = curr + curr = nxtPair + + return dummy.next +``` + +```java +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode() {} + * ListNode(int val) { this.val = val; } + * ListNode(int val, ListNode next) { this.val = val; this.next = next; } + * } + */ +public class Solution { + public ListNode swapPairs(ListNode head) { + ListNode dummy = new ListNode(0, head); + ListNode prev = dummy, curr = head; + + while (curr != null && curr.next != null) { + ListNode nxtPair = curr.next.next; + ListNode second = curr.next; + + // Reverse this pair + second.next = curr; + curr.next = nxtPair; + prev.next = second; + + // Update pointers + prev = curr; + curr = nxtPair; + } + + return dummy.next; + } +} +``` + +```cpp +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode() : val(0), next(nullptr) {} + * ListNode(int x) : val(x), next(nullptr) {} + * ListNode(int x, ListNode *next) : val(x), next(next) {} + * }; + */ +class Solution { +public: + ListNode* swapPairs(ListNode* head) { + ListNode dummy(0, head); + ListNode* prev = &dummy, *curr = head; + + while (curr && curr->next) { + ListNode* nxtPair = curr->next->next; + ListNode* second = curr->next; + + // Reverse this pair + second->next = curr; + curr->next = nxtPair; + prev->next = second; + + // Update pointers + prev = curr; + curr = nxtPair; + } + + return dummy.next; + } +}; +``` + +```javascript +/** + * Definition for singly-linked list. + * class ListNode { + * constructor(val = 0, next = null) { + * this.val = val; + * this.next = next; + * } + * } + */ +class Solution { + /** + * @param {ListNode} head + * @return {ListNode} + */ + swapPairs(head) { + let dummy = new ListNode(0, head); + let prev = dummy, curr = head; + + while (curr && curr.next) { + let nxtPair = curr.next.next; + let second = curr.next; + + // Reverse this pair + second.next = curr; + curr.next = nxtPair; + prev.next = second; + + // Update pointers + prev = curr; + curr = nxtPair; + } + + return dummy.next; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/swapping-nodes-in-a-linked-list.md b/articles/swapping-nodes-in-a-linked-list.md new file mode 100644 index 000000000..8a0267213 --- /dev/null +++ b/articles/swapping-nodes-in-a-linked-list.md @@ -0,0 +1,785 @@ +## 1. Convert To Array + +::tabs-start + +```python +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, val=0, next=None): +# self.val = val +# self.next = next +class Solution: + def swapNodes(self, head: Optional[ListNode], k: int) -> Optional[ListNode]: + arr = [] + cur = head + while cur: + arr.append(cur.val) + cur = cur.next + + n = len(arr) + arr[k - 1], arr[n - k] = arr[n - k], arr[k - 1] + + cur, i = head, 0 + while cur: + cur.val = arr[i] + cur = cur.next + i += 1 + + return head +``` + +```java +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode() {} + * ListNode(int val) { this.val = val; } + * ListNode(int val, ListNode next) { this.val = val; this.next = next; } + * } + */ +public class Solution { + public ListNode swapNodes(ListNode head, int k) { + List arr = new ArrayList<>(); + ListNode cur = head; + + while (cur != null) { + arr.add(cur.val); + cur = cur.next; + } + + int n = arr.size(); + int temp = arr.get(k - 1); + arr.set(k - 1, arr.get(n - k)); + arr.set(n - k, temp); + + cur = head; + int i = 0; + while (cur != null) { + cur.val = arr.get(i); + cur = cur.next; + i++; + } + + return head; + } +} +``` + +```cpp +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode() : val(0), next(nullptr) {} + * ListNode(int x) : val(x), next(nullptr) {} + * ListNode(int x, ListNode *next) : val(x), next(next) {} + * }; + */ +class Solution { +public: + ListNode* swapNodes(ListNode* head, int k) { + vector arr; + ListNode* cur = head; + + while (cur) { + arr.push_back(cur->val); + cur = cur->next; + } + + int n = arr.size(); + swap(arr[k - 1], arr[n - k]); + + cur = head; + int i = 0; + while (cur) { + cur->val = arr[i]; + cur = cur->next; + i++; + } + + return head; + } +}; +``` + +```javascript +/** + * Definition for singly-linked list. + * class ListNode { + * constructor(val = 0, next = null) { + * this.val = val; + * this.next = next; + * } + * } + */ +class Solution { + /** + * @param {ListNode} head + * @param {number} k + * @return {ListNode} + */ + swapNodes(head, k) { + let arr = []; + let cur = head; + + while (cur) { + arr.push(cur.val); + cur = cur.next; + } + + let n = arr.length; + [arr[k - 1], arr[n - k]] = [arr[n - k], arr[k - 1]]; + + cur = head; + let i = 0; + while (cur) { + cur.val = arr[i]; + cur = cur.next; + i++; + } + + return head; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Recursion + +::tabs-start + +```python +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, val=0, next=None): +# self.val = val +# self.next = next +class Solution: + def swapNodes(self, head: Optional[ListNode], k: int) -> Optional[ListNode]: + left, right, startIdx = None, None, 0 + + def dfs(node): + nonlocal left, right, startIdx + if not node: + return 0 + + startIdx += 1 + if startIdx == k: + left = node + + endIdx = dfs(node.next) + 1 + if endIdx == k: + right = node + + return endIdx + + dfs(head) + if left and right: + left.val, right.val = right.val, left.val + + return head +``` + +```java +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode() {} + * ListNode(int val) { this.val = val; } + * ListNode(int val, ListNode next) { this.val = val; this.next = next; } + * } + */ +public class Solution { + public ListNode swapNodes(ListNode head, int k) { + ListNode[] left = new ListNode[1]; + ListNode[] right = new ListNode[1]; + int[] startIdx = {0}; + + dfs(head, k, startIdx, left, right); + + if (left[0] != null && right[0] != null) { + int temp = left[0].val; + left[0].val = right[0].val; + right[0].val = temp; + } + + return head; + } + + private int dfs(ListNode node, int k, int[] startIdx, ListNode[] left, ListNode[] right) { + if (node == null) { + return 0; + } + + startIdx[0]++; + if (startIdx[0] == k) { + left[0] = node; + } + + int endIdx = dfs(node.next, k, startIdx, left, right) + 1; + if (endIdx == k) { + right[0] = node; + } + + return endIdx; + } +} +``` + +```cpp +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode() : val(0), next(nullptr) {} + * ListNode(int x) : val(x), next(nullptr) {} + * ListNode(int x, ListNode *next) : val(x), next(next) {} + * }; + */ +class Solution { +public: + ListNode* swapNodes(ListNode* head, int k) { + ListNode* left = nullptr; + ListNode* right = nullptr; + int startIdx = 0; + + dfs(head, k, startIdx, left, right); + + if (left && right) { + swap(left->val, right->val); + } + + return head; + } + +private: + int dfs(ListNode* node, int k, int& startIdx, ListNode*& left, ListNode*& right) { + if (!node) { + return 0; + } + + startIdx++; + if (startIdx == k) { + left = node; + } + + int endIdx = dfs(node->next, k, startIdx, left, right) + 1; + if (endIdx == k) { + right = node; + } + + return endIdx; + } +}; +``` + +```javascript +/** + * Definition for singly-linked list. + * class ListNode { + * constructor(val = 0, next = null) { + * this.val = val; + * this.next = next; + * } + * } + */ +class Solution { + /** + * @param {ListNode} head + * @param {number} k + * @return {ListNode} + */ + swapNodes(head, k) { + let left = null, right = null, startIdx = 0; + + const dfs = (node) => { + if (!node) return 0; + + startIdx++; + if (startIdx === k) left = node; + + let endIdx = dfs(node.next) + 1; + if (endIdx === k) right = node; + + return endIdx; + }; + + dfs(head); + if (left && right) { + [left.val, right.val] = [right.val, left.val]; + } + + return head; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ for recursion stack. + +--- + +## 3. Iteration (Two Pass) + +::tabs-start + +```python +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, val=0, next=None): +# self.val = val +# self.next = next +class Solution: + def swapNodes(self, head: Optional[ListNode], k: int) -> Optional[ListNode]: + n = 0 + cur = head + while cur: + n += 1 + cur = cur.next + + left, right = None, None + cur = head + for i in range(1, n + 1): + if i == k: + left = cur + if i == (n - k + 1): + right = cur + cur = cur.next + + left.val, right.val = right.val, left.val + return head +``` + +```java +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode() {} + * ListNode(int val) { this.val = val; } + * ListNode(int val, ListNode next) { this.val = val; this.next = next; } + * } + */ +public class Solution { + public ListNode swapNodes(ListNode head, int k) { + int n = 0; + ListNode cur = head; + while (cur != null) { + n++; + cur = cur.next; + } + + ListNode left = null, right = null; + cur = head; + for (int i = 1; i <= n; i++) { + if (i == k) { + left = cur; + } + if (i == (n - k + 1)) { + right = cur; + } + cur = cur.next; + } + + int temp = left.val; + left.val = right.val; + right.val = temp; + + return head; + } +} +``` + +```cpp +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode() : val(0), next(nullptr) {} + * ListNode(int x) : val(x), next(nullptr) {} + * ListNode(int x, ListNode *next) : val(x), next(next) {} + * }; + */ +class Solution { +public: + ListNode* swapNodes(ListNode* head, int k) { + int n = 0; + ListNode* cur = head; + while (cur) { + n++; + cur = cur->next; + } + + ListNode* left = nullptr; + ListNode* right = nullptr; + cur = head; + for (int i = 1; i <= n; i++) { + if (i == k) { + left = cur; + } + if (i == (n - k + 1)) { + right = cur; + } + cur = cur->next; + } + + swap(left->val, right->val); + return head; + } +}; +``` + +```javascript +/** + * Definition for singly-linked list. + * class ListNode { + * constructor(val = 0, next = null) { + * this.val = val; + * this.next = next; + * } + * } + */ +class Solution { + /** + * @param {ListNode} head + * @param {number} k + * @return {ListNode} + */ + swapNodes(head, k) { + let n = 0; + let cur = head; + while (cur) { + n++; + cur = cur.next; + } + + let left = null, right = null; + cur = head; + for (let i = 1; i <= n; i++) { + if (i === k) { + left = cur; + } + if (i === (n - k + 1)) { + right = cur; + } + cur = cur.next; + } + + [left.val, right.val] = [right.val, left.val]; + return head; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ + +--- + +## 4. Iteration (One Pass) - I + +::tabs-start + +```python +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, val=0, next=None): +# self.val = val +# self.next = next +class Solution: + def swapNodes(self, head: Optional[ListNode], k: int) -> Optional[ListNode]: + cur = head + for _ in range(k - 1): + cur = cur.next + + left = cur + right = head + + while cur.next: + cur = cur.next + right = right.next + + left.val, right.val = right.val, left.val + return head +``` + +```java +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode() {} + * ListNode(int val) { this.val = val; } + * ListNode(int val, ListNode next) { this.val = val; this.next = next; } + * } + */ +public class Solution { + public ListNode swapNodes(ListNode head, int k) { + ListNode cur = head; + for (int i = 0; i < k - 1; i++) { + cur = cur.next; + } + + ListNode left = cur; + ListNode right = head; + + while (cur.next != null) { + cur = cur.next; + right = right.next; + } + + int temp = left.val; + left.val = right.val; + right.val = temp; + + return head; + } +} +``` + +```cpp +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode() : val(0), next(nullptr) {} + * ListNode(int x) : val(x), next(nullptr) {} + * ListNode(int x, ListNode *next) : val(x), next(next) {} + * }; + */ +class Solution { +public: + ListNode* swapNodes(ListNode* head, int k) { + ListNode* cur = head; + for (int i = 0; i < k - 1; i++) { + cur = cur->next; + } + + ListNode* left = cur; + ListNode* right = head; + + while (cur->next) { + cur = cur->next; + right = right->next; + } + + swap(left->val, right->val); + return head; + } +}; +``` + +```javascript +/** + * Definition for singly-linked list. + * class ListNode { + * constructor(val = 0, next = null) { + * this.val = val; + * this.next = next; + * } + * } + */ +class Solution { + /** + * @param {ListNode} head + * @param {number} k + * @return {ListNode} + */ + swapNodes(head, k) { + let cur = head; + for (let i = 0; i < k - 1; i++) { + cur = cur.next; + } + + let left = cur; + let right = head; + + while (cur.next) { + cur = cur.next; + right = right.next; + } + + [left.val, right.val] = [right.val, left.val]; + return head; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ + +--- + +## 5. Iteration (One Pass) - II + +::tabs-start + +```python +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, val=0, next=None): +# self.val = val +# self.next = next +class Solution: + def swapNodes(self, head: Optional[ListNode], k: int) -> Optional[ListNode]: + left, right = None, None + cur = head + + while cur: + if right: + right = right.next + if k == 1: + left = cur + right = head + k -= 1 + cur = cur.next + + left.val, right.val = right.val, left.val + return head +``` + +```java +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode() {} + * ListNode(int val) { this.val = val; } + * ListNode(int val, ListNode next) { this.val = val; this.next = next; } + * } + */ +public class Solution { + public ListNode swapNodes(ListNode head, int k) { + ListNode left = null, right = null, cur = head; + + while (cur != null) { + if (right != null) { + right = right.next; + } + if (k == 1) { + left = cur; + right = head; + } + k--; + cur = cur.next; + } + + int temp = left.val; + left.val = right.val; + right.val = temp; + + return head; + } +} +``` + +```cpp +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode() : val(0), next(nullptr) {} + * ListNode(int x) : val(x), next(nullptr) {} + * ListNode(int x, ListNode *next) : val(x), next(next) {} + * }; + */ +class Solution { +public: + ListNode* swapNodes(ListNode* head, int k) { + ListNode* left = nullptr; + ListNode* right = nullptr; + ListNode* cur = head; + + while (cur) { + if (right) { + right = right->next; + } + if (k == 1) { + left = cur; + right = head; + } + k--; + cur = cur->next; + } + + swap(left->val, right->val); + return head; + } +}; +``` + +```javascript +/** + * Definition for singly-linked list. + * class ListNode { + * constructor(val = 0, next = null) { + * this.val = val; + * this.next = next; + * } + * } + */ +class Solution { + /** + * @param {ListNode} head + * @param {number} k + * @return {ListNode} + */ + swapNodes(head, k) { + let left = null, right = null, cur = head; + + while (cur) { + if (right) { + right = right.next; + } + if (k === 1) { + left = cur; + right = head; + } + k--; + cur = cur.next; + } + + [left.val, right.val] = [right.val, left.val]; + return head; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/valid-perfect-square.md b/articles/valid-perfect-square.md new file mode 100644 index 000000000..c5682c5d4 --- /dev/null +++ b/articles/valid-perfect-square.md @@ -0,0 +1,447 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def isPerfectSquare(self, num: int) -> bool: + for i in range(1, num + 1): + sq = i * i + if sq > num: + return False + if sq == num: + return True +``` + +```java +public class Solution { + public boolean isPerfectSquare(int num) { + for (long i = 1; i <= num; i++) { + long sq = i * i; + if (sq > num) { + return false; + } + if (sq == num) { + return true; + } + } + return false; + } +} +``` + +```cpp +class Solution { +public: + bool isPerfectSquare(int num) { + for (long long i = 1; i <= num; i++) { + long long sq = i * i; + if (sq > num) { + return false; + } + if (sq == num) { + return true; + } + } + return false; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} num + * @return {boolean} + */ + isPerfectSquare(num) { + for (let i = 1; i <= num; i++) { + let sq = i * i; + if (sq > num) { + return false; + } + if (sq === num) { + return true; + } + } + return false; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(\sqrt {n})$ +* Space complexity: $O(1)$ + +--- + +## 2. In-Built Function + +::tabs-start + +```python +class Solution: + def isPerfectSquare(self, num: int) -> bool: + sqRoot = int(sqrt(num)) + return sqRoot * sqRoot == num +``` + +```java +public class Solution { + public boolean isPerfectSquare(int num) { + int sqRoot = (int) Math.sqrt(num); + return sqRoot * sqRoot == num; + } +} +``` + +```cpp +class Solution { +public: + bool isPerfectSquare(int num) { + int sqRoot = (int) sqrt(num); + return sqRoot * sqRoot == num; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} num + * @return {boolean} + */ + isPerfectSquare(num) { + let sqRoot = Math.floor(Math.sqrt(num)); + return sqRoot * sqRoot === num; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(1)$ +* Space complexity: $O(1)$ + +--- + +## 3. Binary Search + +::tabs-start + +```python +class Solution: + def isPerfectSquare(self, num: int) -> bool: + l, r = 1, num + + while l <= r: + m = l + (r - l) // 2 + sq = m * m + if sq > num: + r = m - 1 + elif sq < num: + l = m + 1 + else: + return True + + return False +``` + +```java +public class Solution { + public boolean isPerfectSquare(int num) { + long l = 1, r = num; + + while (l <= r) { + long m = l + (r - l) / 2; + long sq = m * m; + if (sq > num) { + r = m - 1; + } else if (sq < num) { + l = m + 1; + } else { + return true; + } + } + + return false; + } +} +``` + +```cpp +class Solution { +public: + bool isPerfectSquare(int num) { + long long l = 1, r = num; + + while (l <= r) { + long long m = l + (r - l) / 2; + long long sq = m * m; + if (sq > num) { + r = m - 1; + } else if (sq < num) { + l = m + 1; + } else { + return true; + } + } + + return false; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} num + * @return {boolean} + */ + isPerfectSquare(num) { + let l = 1, r = num; + + while (l <= r) { + let m = Math.floor(l + (r - l) / 2); + let sq = m * m; + if (sq > num) { + r = m - 1; + } else if (sq < num) { + l = m + 1; + } else { + return true; + } + } + + return false; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(\log n)$ +* Space complexity: $O(1)$ + +--- + +## 4. Math + +::tabs-start + +```python +class Solution: + def isPerfectSquare(self, num: int) -> bool: + i = 1 + while num > 0: + num -= i + i += 2 + return num == 0 +``` + +```java +public class Solution { + public boolean isPerfectSquare(int num) { + int i = 1; + while (num > 0) { + num -= i; + i += 2; + } + return num == 0; + } +} +``` + +```cpp +class Solution { +public: + bool isPerfectSquare(int num) { + int i = 1; + while (num > 0) { + num -= i; + i += 2; + } + return num == 0; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} num + * @return {boolean} + */ + isPerfectSquare(num) { + let i = 1; + while (num > 0) { + num -= i; + i += 2; + } + return num === 0; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(\sqrt {n})$ +* Space complexity: $O(1)$ + +--- + +## 5. Newton's Method + +::tabs-start + +```python +class Solution: + def isPerfectSquare(self, num: int) -> bool: + r = num + while r * r > num: + r = (r + (num // r)) // 2 + return r * r == num +``` + +```java +public class Solution { + public boolean isPerfectSquare(int num) { + long r = num; + while (r * r > num) { + r = (r + num / r) / 2; + } + return r * r == num; + } +} +``` + +```cpp +class Solution { +public: + bool isPerfectSquare(int num) { + long long r = num; + while (r * r > num) { + r = (r + num / r) / 2; + } + return r * r == num; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} num + * @return {boolean} + */ + isPerfectSquare(num) { + let r = num; + while (r * r > num) { + r = Math.floor((r + Math.floor(num / r)) / 2); + } + return r * r === num; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(\log n)$ +* Space complexity: $O(1)$ + +--- + +## 6. Bit Manipulation + +::tabs-start + +```python +class Solution: + def isPerfectSquare(self, num: int) -> bool: + r, mask = 0, 1 << 15 + + while mask > 0: + r |= mask + if r > (num // r): + r ^= mask + mask >>= 1 + + return r * r == num +``` + +```java +public class Solution { + public boolean isPerfectSquare(int num) { + int r = 0, mask = 1 << 15; + + while (mask > 0) { + r |= mask; + if (r > (num / r)) { + r ^= mask; + } + mask >>= 1; + } + + return r * r == num; + } +} +``` + +```cpp +class Solution { +public: + bool isPerfectSquare(int num) { + int r = 0, mask = 1 << 15; + + while (mask > 0) { + r |= mask; + if (r > (num / r)) { + r ^= mask; + } + mask >>= 1; + } + + return r * r == num; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} num + * @return {boolean} + */ + isPerfectSquare(num) { + let r = 0, mask = 1 << 15; + + while (mask > 0) { + r |= mask; + if (r > Math.floor(num / r)) { + r ^= mask; + } + mask >>= 1; + } + + return r * r === num; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(1)$ since we iterate at most $15$ times. +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/validate-stack-sequences.md b/articles/validate-stack-sequences.md new file mode 100644 index 000000000..00867b541 --- /dev/null +++ b/articles/validate-stack-sequences.md @@ -0,0 +1,160 @@ +## 1. Stack + +::tabs-start + +```python +class Solution: + def validateStackSequences(self, pushed: List[int], popped: List[int]) -> bool: + i = 0 + stack = [] + for n in pushed: + stack.append(n) + while i < len(popped) and stack and popped[i] == stack[-1]: + stack.pop() + i += 1 + return not stack +``` + +```java +public class Solution { + public boolean validateStackSequences(int[] pushed, int[] popped) { + Stack stack = new Stack<>(); + int i = 0; + for (int n : pushed) { + stack.push(n); + while (i < popped.length && !stack.isEmpty() && popped[i] == stack.peek()) { + stack.pop(); + i++; + } + } + return stack.isEmpty(); + } +} +``` + +```cpp +class Solution { +public: + bool validateStackSequences(vector& pushed, vector& popped) { + stack stk; + int i = 0; + for (int n : pushed) { + stk.push(n); + while (i < popped.size() && !stk.empty() && popped[i] == stk.top()) { + stk.pop(); + i++; + } + } + return stk.empty(); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} pushed + * @param {number[]} popped + * @return {boolean} + */ + validateStackSequences(pushed, popped) { + const stack = []; + let i = 0; + for (const n of pushed) { + stack.push(n); + while (i < popped.length && stack.length > 0 && popped[i] === stack[stack.length - 1]) { + stack.pop(); + i++; + } + } + return stack.length === 0; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Two Pointers + +::tabs-start + +```python +class Solution: + def validateStackSequences(self, pushed: List[int], popped: List[int]) -> bool: + l = r = 0 + for num in pushed: + pushed[l] = num + l += 1 + while l > 0 and pushed[l - 1] == popped[r]: + r += 1 + l -= 1 + return l == 0 +``` + +```java +public class Solution { + public boolean validateStackSequences(int[] pushed, int[] popped) { + int l = 0, r = 0; + for (int num : pushed) { + pushed[l++] = num; + while (l > 0 && pushed[l - 1] == popped[r]) { + r++; + l--; + } + } + return l == 0; + } +} +``` + +```cpp +class Solution { +public: + bool validateStackSequences(vector& pushed, vector& popped) { + int l = 0, r = 0; + for (int& num : pushed) { + pushed[l++] = num; + while (l > 0 && pushed[l - 1] == popped[r]) { + r++; + l--; + } + } + return l == 0; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} pushed + * @param {number[]} popped + * @return {boolean} + */ + validateStackSequences(pushed, popped) { + let l = 0, r = 0; + for (const num of pushed) { + pushed[l++] = num; + while (l > 0 && pushed[l - 1] === popped[r]) { + r++; + l--; + } + } + return l === 0; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. \ No newline at end of file From 3585d128324c9c2e1d1510313eb47e1d6b88ae93 Mon Sep 17 00:00:00 2001 From: Sri Hari <94112314+Srihari2222@users.noreply.github.com> Date: Mon, 3 Feb 2025 00:49:58 +0530 Subject: [PATCH 32/45] Sri Hari: Batch-5/Neetcode-ALL/Develop-articles (#3836) * Batch-5/Neetcode-ALL/Added-articles * Batch-5/Neetcode-ALL/Added-articles * Batch-5/Neetcode-ALL/Added-articles * Batch-5/Neetcode-ALL/Added-articles * Batch-5/Neetcode-ALL/Added-articles --- articles/as-far-from-land-as-possible.md | 757 ++++++++++ articles/convert-bst-to-greater-tree.md | 643 +++++++++ articles/design-linked-list.md | 1262 +++++++++++++++++ articles/even-odd-tree.md | 543 +++++++ articles/find-bottom-left-tree-value.md | 633 +++++++++ .../find-largest-value-in-each-tree-row.md | 462 ++++++ articles/flip-equivalent-binary-trees.md | 492 +++++++ articles/insertion-sort-list.md | 411 ++++++ ...imum-fuel-cost-to-report-to-the-capital.md | 299 ++++ ...imum-score-of-a-path-between-two-cities.md | 646 +++++++++ articles/number-of-closed-islands.md | 836 +++++++++++ articles/number-of-enclaves.md | 607 ++++++++ ...eudo-palindromic-paths-in-a-binary-tree.md | 806 +++++++++++ .../shortest-path-with-alternating-colors.md | 543 +++++++ .../smallest-string-starting-from-leaf.md | 474 +++++++ articles/split-linked-list-in-parts.md | 343 +++++ articles/sum-root-to-leaf-numbers.md | 652 +++++++++ articles/trim-a-binary-search-tree.md | 488 +++++++ articles/unique-binary-search-trees-ii.md | 670 +++++++++ articles/validate-binary-tree-nodes.md | 668 +++++++++ 20 files changed, 12235 insertions(+) create mode 100644 articles/as-far-from-land-as-possible.md create mode 100644 articles/convert-bst-to-greater-tree.md create mode 100644 articles/design-linked-list.md create mode 100644 articles/even-odd-tree.md create mode 100644 articles/find-bottom-left-tree-value.md create mode 100644 articles/find-largest-value-in-each-tree-row.md create mode 100644 articles/flip-equivalent-binary-trees.md create mode 100644 articles/insertion-sort-list.md create mode 100644 articles/minimum-fuel-cost-to-report-to-the-capital.md create mode 100644 articles/minimum-score-of-a-path-between-two-cities.md create mode 100644 articles/number-of-closed-islands.md create mode 100644 articles/number-of-enclaves.md create mode 100644 articles/pseudo-palindromic-paths-in-a-binary-tree.md create mode 100644 articles/shortest-path-with-alternating-colors.md create mode 100644 articles/smallest-string-starting-from-leaf.md create mode 100644 articles/split-linked-list-in-parts.md create mode 100644 articles/sum-root-to-leaf-numbers.md create mode 100644 articles/trim-a-binary-search-tree.md create mode 100644 articles/unique-binary-search-trees-ii.md create mode 100644 articles/validate-binary-tree-nodes.md diff --git a/articles/as-far-from-land-as-possible.md b/articles/as-far-from-land-as-possible.md new file mode 100644 index 000000000..15382ba80 --- /dev/null +++ b/articles/as-far-from-land-as-possible.md @@ -0,0 +1,757 @@ +## 1. Brute Force (BFS) + +::tabs-start + +```python +class Solution: + def maxDistance(self, grid: list[list[int]]) -> int: + N = len(grid) + direct = [[0, 1], [0, -1], [1, 0], [-1, 0]] + + def bfs(row, col): + q = deque([(row, col)]) + visit = [[False] * N for _ in range(N)] + dist, visit[row][col] = 0, True + + while q: + dist += 1 + for _ in range(len(q)): + r, c = q.popleft() + for dx, dy in direct: + newR, newC = r + dx, c + dy + if min(newR, newC) < 0 or max(newR, newC) >= N or visit[newR][newC]: + continue + if grid[newR][newC] == 1: + return dist + visit[newR][newC] = True + q.append((newR, newC)) + return -1 + + res = -1 + for r in range(N): + for c in range(N): + if grid[r][c] == 0: + res = max(res, bfs(r, c)) + if res == -1: + return res + + return res +``` + +```java +public class Solution { + private static final int[][] direct = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}}; + + public int maxDistance(int[][] grid) { + int N = grid.length; + int res = -1; + + for (int r = 0; r < N; r++) { + for (int c = 0; c < N; c++) { + if (grid[r][c] == 0) { + res = Math.max(res, bfs(grid, r, c, N)); + if (res == -1) return res; + } + } + } + return res; + } + + private int bfs(int[][] grid, int row, int col, int N) { + Queue q = new LinkedList<>(); + boolean[][] visit = new boolean[N][N]; + q.offer(new int[]{row, col}); + visit[row][col] = true; + int dist = 0; + + while (!q.isEmpty()) { + dist++; + for (int i = q.size(); i > 0; i--) { + int[] cell = q.poll(); + int r = cell[0], c = cell[1]; + + for (int[] d : direct) { + int newR = r + d[0], newC = c + d[1]; + if (newR < 0 || newC < 0 || newR >= N || newC >= N || visit[newR][newC]) + continue; + if (grid[newR][newC] == 1) + return dist; + + visit[newR][newC] = true; + q.offer(new int[]{newR, newC}); + } + } + } + return -1; + } +} +``` + +```cpp +class Solution { +public: + int maxDistance(vector>& grid) { + int N = grid.size(); + int res = -1; + + for (int r = 0; r < N; r++) { + for (int c = 0; c < N; c++) { + if (grid[r][c] == 0) { + res = max(res, bfs(grid, r, c, N)); + if (res == -1) return res; + } + } + } + return res; + } + +private: + const int direct[4][2] = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}}; + + int bfs(vector>& grid, int row, int col, int N) { + queue> q; + vector> visit(N, vector(N, false)); + q.push({row, col}); + visit[row][col] = true; + int dist = 0; + + while (!q.empty()) { + dist++; + for (int i = q.size(); i > 0; i--) { + auto [r, c] = q.front();q.pop(); + + for (auto& d : direct) { + int newR = r + d[0], newC = c + d[1]; + if (newR < 0 || newC < 0 || newR >= N || newC >= N || visit[newR][newC]) + continue; + if (grid[newR][newC] == 1) + return dist; + + visit[newR][newC] = true; + q.push({newR, newC}); + } + } + } + return -1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} grid + * @return {number} + */ + maxDistance(grid) { + const N = grid.length; + const direct = [[0, 1], [0, -1], [1, 0], [-1, 0]]; + + const bfs = (row, col) => { + const q = new Queue([[row, col]]); + const visit = Array.from({ length: N }, () => Array(N).fill(false)); + visit[row][col] = true; + let dist = 0; + + while (!q.isEmpty()) { + dist++; + for (let i = q.size(); i > 0; i--) { + const [r, c] = q.pop(); + for (let d = 0; d < 4; d++) { + let newR = r + direct[d][0], newC = c + direct[d][1]; + if (newR < 0 || newC < 0 || newR >= N || newC >= N || visit[newR][newC]) + continue; + if (grid[newR][newC] === 1) + return dist; + + visit[newR][newC] = true; + q.push([newR, newC]); + } + } + } + return -1; + }; + + let res = -1; + for (let r = 0; r < N; r++) { + for (let c = 0; c < N; c++) { + if (grid[r][c] === 0) { + res = Math.max(res, bfs(r, c)); + if (res === -1) return res; + } + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 4)$ +* Space complexity: $O(n ^ 2)$ + +--- + +## 2. Multi Source BFS (Overwriting Input) + +::tabs-start + +```python +class Solution: + def maxDistance(self, grid: List[List[int]]) -> int: + N = len(grid) + q = deque() + + for r in range(N): + for c in range(N): + if grid[r][c]: + q.append([r, c]) + + res = -1 + direct = [[0, 1], [1, 0], [0, -1], [-1, 0]] + + while q: + r, c = q.popleft() + res = grid[r][c] + + for dr, dc in direct: + newR, newC = r + dr, c + dc + if min(newR, newC) >= 0 and max(newR, newC) < N and grid[newR][newC] == 0: + q.append([newR, newC]) + grid[newR][newC] = grid[r][c] + 1 + + return res - 1 if res > 1 else -1 +``` + +```java +public class Solution { + private static final int[][] direct = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}}; + + public int maxDistance(int[][] grid) { + int N = grid.length; + Queue q = new LinkedList<>(); + for (int r = 0; r < N; r++) { + for (int c = 0; c < N; c++) { + if (grid[r][c] == 1) { + q.offer(new int[]{r, c}); + } + } + } + + int res = -1; + while (!q.isEmpty()) { + int[] cell = q.poll(); + int r = cell[0], c = cell[1]; + res = grid[r][c]; + for (int d = 0; d < 4; d++) { + int newR = r + direct[d][0], newC = c + direct[d][1]; + if (newR >= 0 && newC >= 0 && newR < N && newC < N && grid[newR][newC] == 0) { + q.offer(new int[]{newR, newC}); + grid[newR][newC] = grid[r][c] + 1; + } + } + } + return res > 1 ? res - 1 : -1; + } +} +``` + +```cpp +class Solution { + const int direct[4][2] = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}}; + +public: + int maxDistance(vector>& grid) { + int N = grid.size(); + queue> q; + for (int r = 0; r < N; r++) { + for (int c = 0; c < N; c++) { + if (grid[r][c] == 1) { + q.push({r, c}); + } + } + } + + int res = -1; + while (!q.empty()) { + auto [r, c] = q.front();q.pop(); + res = grid[r][c]; + for (int d = 0; d < 4; d++) { + int newR = r + direct[d][0], newC = c + direct[d][1]; + if (newR >= 0 && newC >= 0 && newR < N && newC < N && grid[newR][newC] == 0) { + q.push({newR, newC}); + grid[newR][newC] = grid[r][c] + 1; + } + } + } + return res > 1 ? res - 1 : -1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} grid + * @return {number} + */ + maxDistance(grid) { + const N = grid.length; + const q = new Queue(); + for (let r = 0; r < N; r++) { + for (let c = 0; c < N; c++) { + if (grid[r][c] === 1) { + q.push([r, c]); + } + } + } + + let res = -1; + const direct = [[0, 1], [0, -1], [1, 0], [-1, 0]]; + while (!q.isEmpty()) { + const [r, c] = q.pop(); + res = grid[r][c]; + for (let d = 0; d < 4; d++) { + let newR = r + direct[d][0], newC = c + direct[d][1]; + if (newR >= 0 && newC >= 0 && newR < N && newC < N && grid[newR][newC] === 0) { + q.push([newR, newC]); + grid[newR][newC] = grid[r][c] + 1; + } + } + } + return res > 1 ? res - 1 : -1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n ^ 2)$ + +--- + +## 3. Multi Source BFS + +::tabs-start + +```python +class Solution: + def maxDistance(self, grid: List[List[int]]) -> int: + N = len(grid) + direct = [0, 1, 0, -1, 0] + visit = [[False] * N for _ in range(N)] + q = deque() + + for r in range(N): + for c in range(N): + if grid[r][c] == 1: + visit[r][c] = True + q.append((r, c)) + + res = 0 + while q: + res += 1 + for _ in range(len(q)): + r, c = q.popleft() + for d in range(4): + newR, newC = r + direct[d], c + direct[d + 1] + if 0 <= newR < N and 0 <= newC < N and not visit[newR][newC]: + q.append((newR, newC)) + visit[newR][newC] = True + + return res - 1 if res > 1 else -1 +``` + +```java +public class Solution { + public int maxDistance(int[][] grid) { + int N = grid.length; + int[] direct = {0, 1, 0, -1, 0}; + boolean[][] visit = new boolean[N][N]; + Queue q = new LinkedList<>(); + for (int r = 0; r < N; r++) { + for (int c = 0; c < N; c++) { + if (grid[r][c] == 1) { + visit[r][c] = true; + q.offer(new int[]{r, c}); + } + } + } + + int res = 0; + while (!q.isEmpty()) { + res++; + for (int i = q.size(); i > 0; i--) { + int[] cur = q.poll(); + int r = cur[0], c = cur[1]; + for (int d = 0; d < 4; d++) { + int newR = r + direct[d], newC = c + direct[d + 1]; + if (newR >= 0 && newC >= 0 && newR < N && newC < N && !visit[newR][newC]) { + q.offer(new int[]{newR, newC}); + visit[newR][newC] = true; + } + } + } + } + return res > 1 ? res - 1 : -1; + } +} +``` + +```cpp +class Solution { +public: + int maxDistance(vector>& grid) { + int N = grid.size(); + vector direct = {0, 1, 0, -1, 0}; + vector> visit(N, vector(N, false)); + queue> q; + for (int r = 0; r < N; r++) { + for (int c = 0; c < N; c++) { + if (grid[r][c] == 1) { + visit[r][c] = true; + q.push({r, c}); + } + } + } + + int res = 0; + while (!q.empty()) { + res++; + for (int i = q.size(); i > 0; i--) { + auto [r, c] = q.front();q.pop(); + for (int d = 0; d < 4; d++) { + int newR = r + direct[d], newC = c + direct[d + 1]; + if (newR >= 0 && newC >= 0 && newR < N && newC < N && !visit[newR][newC]) { + q.push({newR, newC}); + visit[newR][newC] = true; + } + } + } + } + return res > 1 ? res - 1 : -1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} grid + * @return {number} + */ + maxDistance(grid) { + const N = grid.length; + const direct = [0, 1, 0, -1, 0]; + const visit = Array.from({ length: N }, () => Array(N).fill(false)); + const q = new Queue(); + for (let r = 0; r < N; r++) { + for (let c = 0; c < N; c++) { + if (grid[r][c] === 1) { + visit[r][c] = true; + q.push([r, c]); + } + } + } + + let res = 0; + while (!q.isEmpty()) { + res++; + for (let i = q.size(); i > 0; i--) { + const [r, c] = q.pop(); + for (let d = 0; d < 4; d++) { + let newR = r + direct[d], newC = c + direct[d + 1]; + if (newR >= 0 && newC >= 0 && newR < N && newC < N && !visit[newR][newC]) { + q.push([newR, newC]); + visit[newR][newC] = true; + } + } + } + } + return res > 1 ? res - 1 : -1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n ^ 2)$ + +--- + +## 4. Dynamic Programming (Overwrting the Input) + +::tabs-start + +```python +class Solution: + def maxDistance(self, grid: List[List[int]]) -> int: + N, INF = len(grid), 10**6 + + for r in range(N): + for c in range(N): + if grid[r][c] == 1: + continue + grid[r][c] = INF + if r > 0: + grid[r][c] = min(grid[r][c], grid[r - 1][c] + 1) + if c > 0: + grid[r][c] = min(grid[r][c], grid[r][c - 1] + 1) + + res = 0 + for r in range(N - 1, -1, -1): + for c in range(N - 1, -1, -1): + if grid[r][c] == 1: + continue + if r < N - 1: + grid[r][c] = min(grid[r][c], grid[r + 1][c] + 1) + if c < N - 1: + grid[r][c] = min(grid[r][c], grid[r][c + 1] + 1) + res = max(res, grid[r][c]) + + return res - 1 if res < INF else -1 +``` + +```java +public class Solution { + public int maxDistance(int[][] grid) { + int N = grid.length, INF = 1000000; + + for (int r = 0; r < N; r++) { + for (int c = 0; c < N; c++) { + if (grid[r][c] == 1) continue; + grid[r][c] = INF; + if (r > 0) grid[r][c] = Math.min(grid[r][c], grid[r - 1][c] + 1); + if (c > 0) grid[r][c] = Math.min(grid[r][c], grid[r][c - 1] + 1); + } + } + + int res = 0; + for (int r = N - 1; r >= 0; r--) { + for (int c = N - 1; c >= 0; c--) { + if (grid[r][c] == 1) continue; + if (r < N - 1) grid[r][c] = Math.min(grid[r][c], grid[r + 1][c] + 1); + if (c < N - 1) grid[r][c] = Math.min(grid[r][c], grid[r][c + 1] + 1); + res = Math.max(res, grid[r][c]); + } + } + + return res < INF ? res - 1 : -1; + } +} +``` + +```cpp +class Solution { +public: + int maxDistance(vector>& grid) { + int N = grid.size(), INF = 1000000; + + for (int r = 0; r < N; r++) { + for (int c = 0; c < N; c++) { + if (grid[r][c] == 1) continue; + grid[r][c] = INF; + if (r > 0) grid[r][c] = min(grid[r][c], grid[r - 1][c] + 1); + if (c > 0) grid[r][c] = min(grid[r][c], grid[r][c - 1] + 1); + } + } + + int res = 0; + for (int r = N - 1; r >= 0; r--) { + for (int c = N - 1; c >= 0; c--) { + if (grid[r][c] == 1) continue; + if (r < N - 1) grid[r][c] = min(grid[r][c], grid[r + 1][c] + 1); + if (c < N - 1) grid[r][c] = min(grid[r][c], grid[r][c + 1] + 1); + res = max(res, grid[r][c]); + } + } + + return res < INF ? res - 1 : -1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} grid + * @return {number} + */ + maxDistance(grid) { + const N = grid.length, INF = 1000000; + + for (let r = 0; r < N; r++) { + for (let c = 0; c < N; c++) { + if (grid[r][c] === 1) continue; + grid[r][c] = INF; + if (r > 0) grid[r][c] = Math.min(grid[r][c], grid[r - 1][c] + 1); + if (c > 0) grid[r][c] = Math.min(grid[r][c], grid[r][c - 1] + 1); + } + } + + let res = 0; + for (let r = N - 1; r >= 0; r--) { + for (let c = N - 1; c >= 0; c--) { + if (grid[r][c] === 1) continue; + if (r < N - 1) grid[r][c] = Math.min(grid[r][c], grid[r + 1][c] + 1); + if (c < N - 1) grid[r][c] = Math.min(grid[r][c], grid[r][c + 1] + 1); + res = Math.max(res, grid[r][c]); + } + } + + return res < INF ? res - 1 : -1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(1)$ extra space. + +--- + +## 5. Dynamic Programming + +::tabs-start + +```python +class Solution: + def maxDistance(self, grid: List[List[int]]) -> int: + N = len(grid) + res, INF = -1, 10**6 + dp = [[INF] * (N + 2) for _ in range(N + 2)] + + for r in range(1, N + 1): + for c in range(1, N + 1): + if grid[r - 1][c - 1] == 1: + dp[r][c] = 0 + else: + dp[r][c] = min(dp[r - 1][c], dp[r][c - 1]) + 1 + + for r in range(N, 0, -1): + for c in range(N, 0, -1): + if grid[r - 1][c - 1] == 0: + dp[r][c] = min(dp[r][c], min(dp[r + 1][c], dp[r][c + 1]) + 1) + res = max(res, dp[r][c]) + + return res if res < INF else -1 +``` + +```java +public class Solution { + public int maxDistance(int[][] grid) { + int N = grid.length; + int INF = 1000000; + int[][] dp = new int[N + 2][N + 2]; + for (int[] row : dp) { + Arrays.fill(row, INF); + } + + for (int r = 1; r <= N; r++) { + for (int c = 1; c <= N; c++) { + if (grid[r - 1][c - 1] == 1) { + dp[r][c] = 0; + } else { + dp[r][c] = Math.min(dp[r - 1][c], dp[r][c - 1]) + 1; + } + } + } + + int res = -1; + for (int r = N; r > 0; r--) { + for (int c = N; c > 0; c--) { + if (grid[r - 1][c - 1] == 0) { + dp[r][c] = Math.min(dp[r][c], Math.min(dp[r + 1][c], dp[r][c + 1]) + 1); + res = Math.max(res, dp[r][c]); + } + } + } + return res < INF ? res : -1; + } +} +``` + +```cpp +class Solution { +public: + int maxDistance(vector>& grid) { + int N = grid.size(); + int INF = 1000000, res = -1; + vector> dp(N + 2, vector(N + 2, INF)); + + for (int r = 1; r <= N; r++) { + for (int c = 1; c <= N; c++) { + if (grid[r - 1][c - 1] == 1) { + dp[r][c] = 0; + } else { + dp[r][c] = min(dp[r - 1][c], dp[r][c - 1]) + 1; + } + } + } + + for (int r = N; r > 0; r--) { + for (int c = N; c > 0; c--) { + if (grid[r - 1][c - 1] == 0) { + dp[r][c] = min(dp[r][c], min(dp[r + 1][c], dp[r][c + 1]) + 1); + res = max(res, dp[r][c]); + } + } + } + return res < INF ? res : -1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} grid + * @return {number} + */ + maxDistance(grid) { + const N = grid.length; + const INF = 1000000; + let res = -1; + let dp = Array.from({ length: N + 2 }, () => Array(N + 2).fill(INF)); + + for (let r = 1; r <= N; r++) { + for (let c = 1; c <= N; c++) { + if (grid[r - 1][c - 1] === 1) { + dp[r][c] = 0; + } else { + dp[r][c] = Math.min(dp[r - 1][c], dp[r][c - 1]) + 1; + } + } + } + + for (let r = N; r > 0; r--) { + for (let c = N; c > 0; c--) { + if (grid[r - 1][c - 1] === 0) { + dp[r][c] = Math.min(dp[r][c], Math.min(dp[r + 1][c], dp[r][c + 1]) + 1); + res = Math.max(res, dp[r][c]); + } + } + } + return res < INF ? res : -1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n ^ 2)$ \ No newline at end of file diff --git a/articles/convert-bst-to-greater-tree.md b/articles/convert-bst-to-greater-tree.md new file mode 100644 index 000000000..9e01d51be --- /dev/null +++ b/articles/convert-bst-to-greater-tree.md @@ -0,0 +1,643 @@ +## 1. Depth First Search (Two Pass) + +::tabs-start + +```python +# 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 +class Solution: + def convertBST(self, root: Optional[TreeNode]) -> Optional[TreeNode]: + def getSum(node): + if not node: + return 0 + return node.val + getSum(node.left) + getSum(node.right) + + totalSum = getSum(root) + + def dfs(node): + nonlocal totalSum + if not node: + return + + dfs(node.left) + tmp = node.val + node.val = totalSum + totalSum -= tmp + dfs(node.right) + + dfs(root) + return root +``` + +```java +/** + * 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; + * } + * } + */ +public class Solution { + private int totalSum; + + public TreeNode convertBST(TreeNode root) { + totalSum = getSum(root); + dfs(root); + return root; + } + + private int getSum(TreeNode node) { + if (node == null) return 0; + return node.val + getSum(node.left) + getSum(node.right); + } + + private void dfs(TreeNode node) { + if (node == null) return; + + dfs(node.left); + int tmp = node.val; + node.val = totalSum; + totalSum -= tmp; + dfs(node.right); + } +} +``` + +```cpp +/** + * 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: + int totalSum; + + TreeNode* convertBST(TreeNode* root) { + totalSum = getSum(root); + dfs(root); + return root; + } + +private: + int getSum(TreeNode* node) { + if (!node) return 0; + return node->val + getSum(node->left) + getSum(node->right); + } + + void dfs(TreeNode* node) { + if (!node) return; + + dfs(node->left); + int tmp = node->val; + node->val = totalSum; + totalSum -= tmp; + dfs(node->right); + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {TreeNode} + */ + convertBST(root) { + const getSum = (node) => { + if (!node) return 0; + return node.val + getSum(node.left) + getSum(node.right); + }; + + let totalSum = getSum(root); + + const dfs = (node) => { + if (!node) return; + + dfs(node.left); + let tmp = node.val; + node.val = totalSum; + totalSum -= tmp; + dfs(node.right); + }; + + dfs(root); + return root; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ for recursion stack. + +--- + +## 2. Depth First Search (One Pass) + +::tabs-start + +```python +# 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 +class Solution: + def convertBST(self, root: Optional[TreeNode]) -> Optional[TreeNode]: + curSum = 0 + + def dfs(node): + nonlocal curSum + if not node: + return + + dfs(node.right) + tmp = node.val + node.val += curSum + curSum += tmp + dfs(node.left) + + dfs(root) + return root +``` + +```java +/** + * 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; + * } + * } + */ +public class Solution { + private int curSum = 0; + + public TreeNode convertBST(TreeNode root) { + dfs(root); + return root; + } + + private void dfs(TreeNode node) { + if (node == null) return; + + dfs(node.right); + int tmp = node.val; + node.val += curSum; + curSum += tmp; + dfs(node.left); + } +} +``` + +```cpp +/** + * 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: + int curSum = 0; + + TreeNode* convertBST(TreeNode* root) { + dfs(root); + return root; + } + +private: + void dfs(TreeNode* node) { + if (!node) return; + + dfs(node->right); + int tmp = node->val; + node->val += curSum; + curSum += tmp; + dfs(node->left); + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {TreeNode} + */ + convertBST(root) { + let curSum = 0; + + const dfs = (node) => { + if (!node) return; + + dfs(node.right); + let tmp = node.val; + node.val += curSum; + curSum += tmp; + dfs(node.left); + }; + + dfs(root); + return root; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ for recursion stack. + +--- + +## 3. Iterative DFS + +::tabs-start + +```python +# 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 +class Solution: + def convertBST(self, root: Optional[TreeNode]) -> Optional[TreeNode]: + curSum = 0 + stack = [] + node = root + + while stack or node: + while node: + stack.append(node) + node = node.right + + node = stack.pop() + curSum += node.val + node.val = curSum + node = node.left + + return root +``` + +```java +/** + * 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; + * } + * } + */ +public class Solution { + public TreeNode convertBST(TreeNode root) { + int curSum = 0; + Stack stack = new Stack<>(); + TreeNode node = root; + + while (!stack.isEmpty() || node != null) { + while (node != null) { + stack.push(node); + node = node.right; + } + + node = stack.pop(); + curSum += node.val; + node.val = curSum; + node = node.left; + } + return root; + } +} +``` + +```cpp +/** + * 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* convertBST(TreeNode* root) { + int curSum = 0; + stack st; + TreeNode* node = root; + + while (!st.empty() || node) { + while (node) { + st.push(node); + node = node->right; + } + + node = st.top(); st.pop(); + curSum += node->val; + node->val = curSum; + node = node->left; + } + return root; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {TreeNode} + */ + convertBST(root) { + let curSum = 0; + const stack = []; + let node = root; + + while (stack.length || node) { + while (node) { + stack.push(node); + node = node.right; + } + + node = stack.pop(); + curSum += node.val; + node.val = curSum; + node = node.left; + } + return root; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 4. Morris Traversal + +::tabs-start + +```python +# 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 +class Solution: + def convertBST(self, root: Optional[TreeNode]) -> Optional[TreeNode]: + curSum = 0 + cur = root + + while cur: + if cur.right: + prev = cur.right + while prev.left and prev.left != cur: + prev = prev.left + + if not prev.left: + prev.left = cur + cur = cur.right + else: + prev.left = None + curSum += cur.val + cur.val = curSum + cur = cur.left + else: + curSum += cur.val + cur.val = curSum + cur = cur.left + + return root +``` + +```java +/** + * 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; + * } + * } + */ +public class Solution { + public TreeNode convertBST(TreeNode root) { + int curSum = 0; + TreeNode cur = root; + + while (cur != null) { + if (cur.right != null) { + TreeNode prev = cur.right; + while (prev.left != null && prev.left != cur) { + prev = prev.left; + } + + if (prev.left == null) { + prev.left = cur; + cur = cur.right; + } else { + prev.left = null; + curSum += cur.val; + cur.val = curSum; + cur = cur.left; + } + } else { + curSum += cur.val; + cur.val = curSum; + cur = cur.left; + } + } + return root; + } +} +``` + +```cpp +/** + * 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* convertBST(TreeNode* root) { + int curSum = 0; + TreeNode* cur = root; + + while (cur) { + if (cur->right) { + TreeNode* prev = cur->right; + while (prev->left && prev->left != cur) { + prev = prev->left; + } + + if (!prev->left) { + prev->left = cur; + cur = cur->right; + } else { + prev->left = nullptr; + curSum += cur->val; + cur->val = curSum; + cur = cur->left; + } + } else { + curSum += cur->val; + cur->val = curSum; + cur = cur->left; + } + } + return root; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {TreeNode} + */ + convertBST(root) { + let curSum = 0; + let cur = root; + + while (cur) { + if (cur.right) { + let prev = cur.right; + while (prev.left && prev.left !== cur) { + prev = prev.left; + } + + if (!prev.left) { + prev.left = cur; + cur = cur.right; + } else { + prev.left = null; + curSum += cur.val; + cur.val = curSum; + cur = cur.left; + } + } else { + curSum += cur.val; + cur.val = curSum; + cur = cur.left; + } + } + return root; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/design-linked-list.md b/articles/design-linked-list.md new file mode 100644 index 000000000..618c2335c --- /dev/null +++ b/articles/design-linked-list.md @@ -0,0 +1,1262 @@ +## 1. Singly Linked List + +::tabs-start + +```python +class ListNode: + def __init__(self, val: int): + self.val = val + self.next = None + +class MyLinkedList: + def __init__(self): + self.head = ListNode(0) + self.size = 0 + + def get(self, index: int) -> int: + if index >= self.size: + return -1 + cur = self.head.next + for _ in range(index): + cur = cur.next + return cur.val + + def addAtHead(self, val: int) -> None: + node = ListNode(val) + node.next = self.head.next + self.head.next = node + self.size += 1 + + def addAtTail(self, val: int) -> None: + node = ListNode(val) + cur = self.head + while cur.next: + cur = cur.next + cur.next = node + self.size += 1 + + def addAtIndex(self, index: int, val: int) -> None: + if index > self.size: + return + cur = self.head + for _ in range(index): + cur = cur.next + node = ListNode(val) + node.next = cur.next + cur.next = node + self.size += 1 + + def deleteAtIndex(self, index: int) -> None: + if index >= self.size: + return + cur = self.head + for _ in range(index): + cur = cur.next + cur.next = cur.next.next + self.size -= 1 +``` + +```java +class ListNode { + int val; + ListNode next; + ListNode(int val) { + this.val = val; + this.next = null; + } +} + +public class MyLinkedList { + private ListNode head; + private int size; + MyLinkedList() { + head = new ListNode(0); + size = 0; + } + public int get(int index) { + if (index >= size) return -1; + ListNode cur = head.next; + for (int i = 0; i < index; i++) { + cur = cur.next; + } + return cur.val; + } + public void addAtHead(int val) { + ListNode node = new ListNode(val); + node.next = head.next; + head.next = node; + size++; + } + public void addAtTail(int val) { + ListNode node = new ListNode(val); + ListNode cur = head; + while (cur.next != null) { + cur = cur.next; + } + cur.next = node; + size++; + } + public void addAtIndex(int index, int val) { + if (index > size) return; + ListNode cur = head; + for (int i = 0; i < index; i++) { + cur = cur.next; + } + ListNode node = new ListNode(val); + node.next = cur.next; + cur.next = node; + size++; + } + public void deleteAtIndex(int index) { + if (index >= size) return; + ListNode cur = head; + for (int i = 0; i < index; i++) { + cur = cur.next; + } + cur.next = cur.next.next; + size--; + } +} +``` + +```cpp +class MyLinkedList { + struct ListNode { + int val; + ListNode* next; + ListNode(int val) : val(val), next(nullptr) {} + }; + +public: + ListNode* head; + int size; + MyLinkedList() { + head = new ListNode(0); + size = 0; + } + int get(int index) { + if (index >= size) return -1; + ListNode* cur = head->next; + for (int i = 0; i < index; i++) { + cur = cur->next; + } + return cur->val; + } + void addAtHead(int val) { + ListNode* node = new ListNode(val); + node->next = head->next; + head->next = node; + size++; + } + void addAtTail(int val) { + ListNode* node = new ListNode(val); + ListNode* cur = head; + while (cur->next != nullptr) { + cur = cur->next; + } + cur->next = node; + size++; + } + void addAtIndex(int index, int val) { + if (index > size) return; + ListNode* cur = head; + for (int i = 0; i < index; i++) { + cur = cur->next; + } + ListNode* node = new ListNode(val); + node->next = cur->next; + cur->next = node; + size++; + } + void deleteAtIndex(int index) { + if (index >= size) return; + ListNode* cur = head; + for (int i = 0; i < index; i++) { + cur = cur->next; + } + ListNode* temp = cur->next; + cur->next = cur->next->next; + delete temp; + size--; + } +}; +``` + +```javascript +class ListNode { + /** + * @constructor + * @param {number} val + */ + constructor(val) { + this.val = val; + this.next = null; + } +} + +class MyLinkedList { + constructor() { + this.head = new ListNode(0); + this.size = 0; + } + + /** + * @param {number} index + * @return {number} + */ + get(index) { + if (index >= this.size) return -1; + let cur = this.head.next; + for (let i = 0; i < index; i++) { + cur = cur.next; + } + return cur.val; + } + + /** + * @param {number} val + * @return {void} + */ + addAtHead(val) { + const node = new ListNode(val); + node.next = this.head.next; + this.head.next = node; + this.size++; + } + + /** + * @param {number} val + * @return {void} + */ + addAtTail(val) { + const node = new ListNode(val); + let cur = this.head; + while (cur.next !== null) { + cur = cur.next; + } + cur.next = node; + this.size++; + } + + /** + * @param {number} index + * @param {number} val + * @return {void} + */ + addAtIndex(index, val) { + if (index > this.size) return; + let cur = this.head; + for (let i = 0; i < index; i++) { + cur = cur.next; + } + const node = new ListNode(val); + node.next = cur.next; + cur.next = node; + this.size++; + } + + /** + * @param {number} index + * @return {void} + */ + deleteAtIndex(index) { + if (index >= this.size) return; + let cur = this.head; + for (let i = 0; i < index; i++) { + cur = cur.next; + } + cur.next = cur.next.next; + this.size--; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: + * $O(1)$ time for initialization. + * $O(1)$ time for $addAtHead()$. + * $O(n)$ time for $get()$, $addAtTail()$, $addAtIndex()$, $deleteAtIndex()$. +* Space complexity: $O(n)$ + +--- + +## 2. Singly Linked List (Optimal) + +::tabs-start + +```python +class ListNode: + def __init__(self, val=0, next=None): + self.val = val + self.next = next + +class MyLinkedList: + def __init__(self): + self.head = ListNode(0) + self.size = 0 + + def getPrev(self, index: int) -> ListNode: + cur = self.head + for _ in range(index): + cur = cur.next + return cur + + def get(self, index: int) -> int: + if index >= self.size: + return -1 + return self.getPrev(index).next.val + + def addAtHead(self, val: int) -> None: + self.addAtIndex(0, val) + + def addAtTail(self, val: int) -> None: + self.addAtIndex(self.size, val) + + def addAtIndex(self, index: int, val: int) -> None: + if index > self.size: + return + prev = self.getPrev(index) + node = ListNode(val, prev.next) + prev.next = node + self.size += 1 + + def deleteAtIndex(self, index: int) -> None: + if index >= self.size: + return + prev = self.getPrev(index) + prev.next = prev.next.next + self.size -= 1 +``` + +```java +class ListNode { + int val; + ListNode next; + ListNode(int val, ListNode next) { + this.val = val; + this.next = next; + } + ListNode(int val) { + this(val, null); + } +} + +public class MyLinkedList { + ListNode head; + int size; + + public MyLinkedList() { + head = new ListNode(0, null); + size = 0; + } + + private ListNode getPrev(int index) { + ListNode cur = head; + for (int i = 0; i < index; i++) { + cur = cur.next; + } + return cur; + } + + public int get(int index) { + if (index >= size) { + return -1; + } + return getPrev(index).next.val; + } + + public void addAtHead(int val) { + addAtIndex(0, val); + } + + public void addAtTail(int val) { + addAtIndex(size, val); + } + + public void addAtIndex(int index, int val) { + if (index > size) { + return; + } + ListNode prev = getPrev(index); + ListNode node = new ListNode(val, prev.next); + prev.next = node; + size++; + } + + public void deleteAtIndex(int index) { + if (index >= size) { + return; + } + ListNode prev = getPrev(index); + prev.next = prev.next.next; + size--; + } +} +``` + +```cpp + +class MyLinkedList { + struct ListNode { + int val; + ListNode* next; + ListNode(int val, ListNode* next) : val(val), next(next) {} + ListNode(int val) : val(val), next(nullptr) {} + }; + +public: + MyLinkedList() { + head = new ListNode(0, nullptr); + size = 0; + } + + int get(int index) { + if (index >= size) return -1; + return getPrev(index)->next->val; + } + + void addAtHead(int val) { + addAtIndex(0, val); + } + + void addAtTail(int val) { + addAtIndex(size, val); + } + + void addAtIndex(int index, int val) { + if (index > size) return; + ListNode* prev = getPrev(index); + ListNode* node = new ListNode(val, prev->next); + prev->next = node; + size++; + } + + void deleteAtIndex(int index) { + if (index >= size) return; + ListNode* prev = getPrev(index); + ListNode* toDelete = prev->next; + prev->next = prev->next->next; + delete toDelete; + size--; + } + +private: + ListNode* head; + int size; + + ListNode* getPrev(int index) { + ListNode* cur = head; + for (int i = 0; i < index; i++) { + cur = cur->next; + } + return cur; + } +}; +``` + +```javascript +class ListNode { + /** + * @constructor + * @param {number} + * @param {ListNode|null} + */ + constructor(val = 0, next = null) { + this.val = val; + this.next = next; + } +} + +class MyLinkedList { + constructor() { + this.head = new ListNode(0); + this.size = 0; + } + + /** + * @param {number} index + * @return {ListNode} + */ + getPrev(index) { + let cur = this.head; + for (let i = 0; i < index; i++) { + cur = cur.next; + } + return cur; + } + + /** + * @param {number} index + * @return {number} + */ + get(index) { + if (index >= this.size) { + return -1; + } + return this.getPrev(index).next.val; + } + + /** + * @param {number} val + * @return {void} + */ + addAtHead(val) { + this.addAtIndex(0, val); + } + + /** + * @param {number} val + * @return {void} + */ + addAtTail(val) { + this.addAtIndex(this.size, val); + } + + /** + * @param {number} index + * @param {number} val + * @return {void} + */ + addAtIndex(index, val) { + if (index > this.size) { + return; + } + let prev = this.getPrev(index); + let node = new ListNode(val, prev.next); + prev.next = node; + this.size++; + } + + /** + * @param {number} index + * @return {void} + */ + deleteAtIndex(index) { + if (index >= this.size) { + return; + } + let prev = this.getPrev(index); + prev.next = prev.next.next; + this.size--; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: + * $O(1)$ time for initialization. + * $O(1)$ time for $addAtHead()$. + * $O(n)$ time for $get()$, $addAtTail()$, $addAtIndex()$, $deleteAtIndex()$. +* Space complexity: $O(n)$ + +--- + +## 3. Doubly Linked List + +::tabs-start + +```python +class ListNode: + def __init__(self, val): + self.val = val + self.prev = None + self.next = None + +class MyLinkedList: + + def __init__(self): + self.head = ListNode(0) + self.tail = ListNode(0) + self.head.next = self.tail + self.tail.prev = self.head + + def get(self, index: int) -> int: + cur = self.head.next + while cur and index > 0: + cur = cur.next + index -= 1 + if cur and cur != self.tail and index == 0: + return cur.val + return -1 + + def addAtHead(self, val: int) -> None: + node, next, prev = ListNode(val), self.head.next, self.head + prev.next = node + next.prev = node + node.next = next + node.prev = prev + + def addAtTail(self, val: int) -> None: + node, next, prev = ListNode(val), self.tail, self.tail.prev + prev.next = node + next.prev = node + node.next = next + node.prev = prev + + def addAtIndex(self, index: int, val: int) -> None: + cur = self.head.next + while cur and index > 0: + cur = cur.next + index -= 1 + if cur and index == 0: + node, next, prev = ListNode(val), cur, cur.prev + prev.next = node + next.prev = node + node.next = next + node.prev = prev + + + def deleteAtIndex(self, index: int) -> None: + cur = self.head.next + while cur and index > 0: + cur = cur.next + index -= 1 + if cur and cur != self.tail and index == 0: + next, prev = cur.next, cur.prev + next.prev = prev + prev.next = next +``` + +```java +class ListNode { + int val; + ListNode prev; + ListNode next; + + ListNode(int val) { + this.val = val; + this.prev = null; + this.next = null; + } +} + +public class MyLinkedList { + ListNode head; + ListNode tail; + + MyLinkedList() { + head = new ListNode(0); + tail = new ListNode(0); + head.next = tail; + tail.prev = head; + } + + int get(int index) { + ListNode cur = head.next; + while (cur != null && index > 0) { + cur = cur.next; + index--; + } + if (cur != null && cur != tail && index == 0) { + return cur.val; + } + return -1; + } + + void addAtHead(int val) { + ListNode node = new ListNode(val); + ListNode next = head.next; + ListNode prev = head; + prev.next = node; + next.prev = node; + node.next = next; + node.prev = prev; + } + + void addAtTail(int val) { + ListNode node = new ListNode(val); + ListNode next = tail; + ListNode prev = tail.prev; + prev.next = node; + next.prev = node; + node.next = next; + node.prev = prev; + } + + void addAtIndex(int index, int val) { + ListNode cur = head.next; + while (cur != null && index > 0) { + cur = cur.next; + index--; + } + if (cur != null && index == 0) { + ListNode node = new ListNode(val); + ListNode next = cur; + ListNode prev = cur.prev; + prev.next = node; + next.prev = node; + node.next = next; + node.prev = prev; + } + } + + void deleteAtIndex(int index) { + ListNode cur = head.next; + while (cur != null && index > 0) { + cur = cur.next; + index--; + } + if (cur != null && cur != tail && index == 0) { + ListNode next = cur.next; + ListNode prev = cur.prev; + next.prev = prev; + prev.next = next; + } + } +} +``` + +```cpp +class MyLinkedList { + struct ListNode { + int val; + ListNode* prev; + ListNode* next; + ListNode(int val) : val(val), prev(nullptr), next(nullptr) {} + }; +public: + ListNode* head; + ListNode* tail; + + MyLinkedList() { + head = new ListNode(0); + tail = new ListNode(0); + head->next = tail; + tail->prev = head; + } + + int get(int index) { + ListNode* cur = head->next; + while (cur && index > 0) { + cur = cur->next; + index--; + } + if (cur && cur != tail && index == 0) { + return cur->val; + } + return -1; + } + + void addAtHead(int val) { + ListNode* node = new ListNode(val); + ListNode* next = head->next; + ListNode* prev = head; + prev->next = node; + next->prev = node; + node->next = next; + node->prev = prev; + } + + void addAtTail(int val) { + ListNode* node = new ListNode(val); + ListNode* next = tail; + ListNode* prev = tail->prev; + prev->next = node; + next->prev = node; + node->next = next; + node->prev = prev; + } + + void addAtIndex(int index, int val) { + ListNode* cur = head->next; + while (cur && index > 0) { + cur = cur->next; + index--; + } + if (cur && index == 0) { + ListNode* node = new ListNode(val); + ListNode* next = cur; + ListNode* prev = cur->prev; + prev->next = node; + next->prev = node; + node->next = next; + node->prev = prev; + } + } + + void deleteAtIndex(int index) { + ListNode* cur = head->next; + while (cur && index > 0) { + cur = cur->next; + index--; + } + if (cur && cur != tail && index == 0) { + ListNode* next = cur->next; + ListNode* prev = cur->prev; + next->prev = prev; + prev->next = next; + delete cur; + } + } +}; +``` + +```javascript +class ListNode { + /** + * @constructor + * @param {number} val + */ + constructor(val) { + this.val = val; + this.prev = null; + this.next = null; + } +} + +class MyLinkedList { + constructor() { + this.head = new ListNode(0); + this.tail = new ListNode(0); + this.head.next = this.tail; + this.tail.prev = this.head; + } + + /** + * @param {number} index + * @return {number} + */ + get(index) { + let cur = this.head.next; + while (cur && index > 0) { + cur = cur.next; + index--; + } + if (cur && cur !== this.tail && index === 0) { + return cur.val; + } + return -1; + } + + /** + * @param {number} val + * @return {void} + */ + addAtHead(val) { + const node = new ListNode(val); + const next = this.head.next; + const prev = this.head; + prev.next = node; + next.prev = node; + node.next = next; + node.prev = prev; + } + + /** + * @param {number} val + * @return {void} + */ + addAtTail(val) { + const node = new ListNode(val); + const next = this.tail; + const prev = this.tail.prev; + prev.next = node; + next.prev = node; + node.next = next; + node.prev = prev; + } + + /** + * @param {number} index + * @param {number} val + * @return {void} + */ + addAtIndex(index, val) { + let cur = this.head.next; + while (cur && index > 0) { + cur = cur.next; + index--; + } + if (cur && index === 0) { + const node = new ListNode(val); + const next = cur; + const prev = cur.prev; + prev.next = node; + next.prev = node; + node.next = next; + node.prev = prev; + } + } + + /** + * @param {number} index + * @return {void} + */ + deleteAtIndex(index) { + let cur = this.head.next; + while (cur && index > 0) { + cur = cur.next; + index--; + } + if (cur && cur !== this.tail && index === 0) { + const next = cur.next; + const prev = cur.prev; + next.prev = prev; + prev.next = next; + } + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: + * $O(1)$ time for initialization. + * $O(1)$ time for $addAtHead()$, $addAtTail()$. + * $O(n)$ time for $get()$, $addAtIndex()$, $deleteAtIndex()$. +* Space complexity: $O(n)$ + +--- + +## 4. Doubly Linked List (Optimal) + +::tabs-start + +```python +class ListNode: + def __init__(self, val=0, next=None, prev=None): + self.val = val + self.next = next + self.prev = prev + +class MyLinkedList: + def __init__(self): + self.head = ListNode(0) + self.tail = ListNode(0) + self.head.next = self.tail + self.tail.prev = self.head + self.size = 0 + + def getPrev(self, index: int) -> ListNode: + if index <= self.size // 2: + cur = self.head + for _ in range(index): + cur = cur.next + else: + cur = self.tail + for _ in range(self.size - index + 1): + cur = cur.prev + return cur + + def get(self, index: int) -> int: + if index >= self.size: + return -1 + return self.getPrev(index).next.val + + def addAtHead(self, val: int) -> None: + self.addAtIndex(0, val) + + def addAtTail(self, val: int) -> None: + self.addAtIndex(self.size, val) + + def addAtIndex(self, index: int, val: int) -> None: + if index > self.size: + return + node = ListNode(val) + prev = self.getPrev(index) + next = prev.next + prev.next = node + node.prev = prev + node.next = next + next.prev = node + self.size += 1 + + def deleteAtIndex(self, index: int) -> None: + if index >= self.size: + return + prev = self.getPrev(index) + cur = prev.next + next = cur.next + prev.next = next + next.prev = prev + self.size -= 1 +``` + +```java +class ListNode { + int val; + ListNode next; + ListNode prev; + + ListNode(int val) { + this(val, null, null); + } + + ListNode(int val, ListNode next, ListNode prev) { + this.val = val; + this.next = next; + this.prev = prev; + } +} + +public class MyLinkedList { + ListNode head; + ListNode tail; + int size; + + public MyLinkedList() { + head = new ListNode(0); + tail = new ListNode(0); + head.next = tail; + tail.prev = head; + size = 0; + } + + private ListNode getPrev(int index) { + if (index <= size / 2) { + ListNode cur = head; + for (int i = 0; i < index; i++) { + cur = cur.next; + } + return cur; + } else { + ListNode cur = tail; + for (int i = 0; i < size - index + 1; i++) { + cur = cur.prev; + } + return cur; + } + } + + public int get(int index) { + if (index >= size) return -1; + return getPrev(index).next.val; + } + + public void addAtHead(int val) { + addAtIndex(0, val); + } + + public void addAtTail(int val) { + addAtIndex(size, val); + } + + public void addAtIndex(int index, int val) { + if (index > size) return; + ListNode node = new ListNode(val); + ListNode prev = getPrev(index); + ListNode next = prev.next; + prev.next = node; + node.prev = prev; + node.next = next; + next.prev = node; + size++; + } + + public void deleteAtIndex(int index) { + if (index >= size) return; + ListNode prev = getPrev(index); + ListNode cur = prev.next; + ListNode next = cur.next; + prev.next = next; + next.prev = prev; + size--; + } +} +``` + +```cpp +class MyLinkedList { + struct ListNode { + int val; + ListNode* next; + ListNode* prev; + ListNode(int val = 0, ListNode* next = nullptr, ListNode* prev = nullptr) { + this->val = val; + this->next = next; + this->prev = prev; + } + }; + +public: + ListNode* head; + ListNode* tail; + int size; + + MyLinkedList() { + head = new ListNode(0); + tail = new ListNode(0); + head->next = tail; + tail->prev = head; + size = 0; + } + + ListNode* getPrev(int index) { + if (index <= size / 2) { + ListNode* cur = head; + for (int i = 0; i < index; i++) { + cur = cur->next; + } + return cur; + } else { + ListNode* cur = tail; + for (int i = 0; i < size - index + 1; i++) { + cur = cur->prev; + } + return cur; + } + } + + int get(int index) { + if (index >= size) return -1; + return getPrev(index)->next->val; + } + + void addAtHead(int val) { + addAtIndex(0, val); + } + + void addAtTail(int val) { + addAtIndex(size, val); + } + + void addAtIndex(int index, int val) { + if (index > size) return; + ListNode* node = new ListNode(val); + ListNode* prev = getPrev(index); + ListNode* next = prev->next; + prev->next = node; + node->prev = prev; + node->next = next; + next->prev = node; + size++; + } + + void deleteAtIndex(int index) { + if (index >= size) return; + ListNode* prev = getPrev(index); + ListNode* cur = prev->next; + ListNode* next = cur->next; + prev->next = next; + next->prev = prev; + delete cur; + size--; + } +}; +``` + +```javascript +class ListNode { + /** + * @constructor + * @param {number} + * @param {ListNode|null} + * @param {ListNode|null} + */ + constructor(val = 0, next = null, prev = null) { + this.val = val; + this.next = next; + this.prev = prev; + } +} + +class MyLinkedList { + constructor() { + this.head = new ListNode(0); + this.tail = new ListNode(0); + this.head.next = this.tail; + this.tail.prev = this.head; + this.size = 0; + } + + /** + * @param {number} index + * @return {ListNode} + */ + getPrev(index) { + let cur; + if (index <= this.size / 2) { + cur = this.head; + for (let i = 0; i < index; i++) { + cur = cur.next; + } + } else { + cur = this.tail; + for (let i = 0; i < this.size - index + 1; i++) { + cur = cur.prev; + } + } + return cur; + } + + /** + * @param {number} index + * @return {number} + */ + get(index) { + if (index >= this.size) { + return -1; + } + return this.getPrev(index).next.val; + } + + /** + * @param {number} val + * @return {void} + */ + addAtHead(val) { + this.addAtIndex(0, val); + } + + /** + * @param {number} val + * @return {void} + */ + addAtTail(val) { + this.addAtIndex(this.size, val); + } + + /** + * @param {number} index + * @param {number} val + * @return {void} + */ + addAtIndex(index, val) { + if (index > this.size) { + return; + } + const node = new ListNode(val); + const prev = this.getPrev(index); + const next = prev.next; + prev.next = node; + node.prev = prev; + node.next = next; + next.prev = node; + this.size++; + } + + /** + * @param {number} index + * @return {void} + */ + deleteAtIndex(index) { + if (index >= this.size) { + return; + } + const prev = this.getPrev(index); + const cur = prev.next; + const next = cur.next; + prev.next = next; + next.prev = prev; + this.size--; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: + * $O(1)$ time for initialization. + * $O(1)$ time for $addAtHead()$, $addAtTail()$. + * $O(n)$ time for $get()$, $addAtIndex()$, $deleteAtIndex()$. +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/even-odd-tree.md b/articles/even-odd-tree.md new file mode 100644 index 000000000..4c5b25687 --- /dev/null +++ b/articles/even-odd-tree.md @@ -0,0 +1,543 @@ +## 1. Breadth First Search + +::tabs-start + +```python +# 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 +class Solution: + def isEvenOddTree(self, root: Optional[TreeNode]) -> bool: + even = True + q = deque([root]) + + while q: + prev = float("-inf") if even else float("inf") + for _ in range(len(q)): + node = q.popleft() + + if even and (node.val % 2 == 0 or node.val <= prev): + return False + elif not even and (node.val % 2 == 1 or node.val >= prev): + return False + + if node.left: + q.append(node.left) + if node.right: + q.append(node.right) + + prev = node.val + + even = not even + + return True +``` + +```java +/** + * 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; + * } + * } + */ +public class Solution { + public boolean isEvenOddTree(TreeNode root) { + boolean even = true; + Queue q = new LinkedList<>(); + q.offer(root); + + while (!q.isEmpty()) { + int prev = even ? Integer.MIN_VALUE : Integer.MAX_VALUE; + for (int i = q.size(); i > 0; i--) { + TreeNode node = q.poll(); + + if (even && (node.val % 2 == 0 || node.val <= prev)) return false; + if (!even && (node.val % 2 == 1 || node.val >= prev)) return false; + + if (node.left != null) q.offer(node.left); + if (node.right != null) q.offer(node.right); + + prev = node.val; + } + even = !even; + } + return true; + } +} +``` + +```cpp +/** + * 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: + bool isEvenOddTree(TreeNode* root) { + bool even = true; + queue q; + q.push(root); + + while (!q.empty()) { + int prev = even ? INT_MIN : INT_MAX; + for (int i = q.size(); i > 0; i--) { + TreeNode* node = q.front();q.pop(); + + if (even && (node->val % 2 == 0 || node->val <= prev)) return false; + if (!even && (node->val % 2 == 1 || node->val >= prev)) return false; + + if (node->left) q.push(node->left); + if (node->right) q.push(node->right); + + prev = node->val; + } + even = !even; + } + return true; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {boolean} + */ + isEvenOddTree(root) { + let even = true; + const q = new Queue([root]); + + while (!q.isEmpty()) { + let prev = even ? -Infinity : Infinity; + for (let i = q.size(); i > 0; i--) { + let node = q.pop(); + + if (even && (node.val % 2 === 0 || node.val <= prev)) return false; + if (!even && (node.val % 2 === 1 || node.val >= prev)) return false; + + if (node.left) q.push(node.left); + if (node.right) q.push(node.right); + + prev = node.val; + } + even = !even; + } + return true; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Depth First Search + +::tabs-start + +```python +# 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 +class Solution: + def isEvenOddTree(self, root: Optional[TreeNode]) -> bool: + levels = [] + + def dfs(node, level): + if not node: + return True + + even = level % 2 == 0 + if ((even and node.val % 2 == 0) or + (not even and (node.val % 2 == 1)) + ): + return False + + if len(levels) == level: + levels.append(node.val) + else: + if ((even and node.val <= levels[level]) or + (not even and node.val >= levels[level]) + ): + return False + levels[level] = node.val + + return dfs(node.left, level + 1) and dfs(node.right, level + 1) + + return dfs(root, 0) +``` + +```java +/** + * 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; + * } + * } + */ +public class Solution { + List levels = new ArrayList<>(); + + private boolean dfs(TreeNode node, int level) { + if (node == null) return true; + + boolean even = level % 2 == 0; + if ((even && node.val % 2 == 0) || + (!even && node.val % 2 == 1)) { + return false; + } + + if (levels.size() == level) { + levels.add(node.val); + } else { + if ((even && node.val <= levels.get(level)) || + (!even && node.val >= levels.get(level))) { + return false; + } + levels.set(level, node.val); + } + + return dfs(node.left, level + 1) && dfs(node.right, level + 1); + } + + public boolean isEvenOddTree(TreeNode root) { + return dfs(root, 0); + } +} +``` + +```cpp +/** + * 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: + vector levels; + + bool dfs(TreeNode* node, int level) { + if (!node) return true; + + bool even = level % 2 == 0; + if ((even && node->val % 2 == 0) || + (!even && node->val % 2 == 1)) { + return false; + } + + if (levels.size() == level) { + levels.push_back(node->val); + } else { + if ((even && node->val <= levels[level]) || + (!even && node->val >= levels[level])) { + return false; + } + levels[level] = node->val; + } + + return dfs(node->left, level + 1) && dfs(node->right, level + 1); + } + + bool isEvenOddTree(TreeNode* root) { + return dfs(root, 0); + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {boolean} + */ + isEvenOddTree(root) { + const levels = []; + + const dfs = (node, level) => { + if (!node) return true; + + const even = level % 2 === 0; + if ((even && node.val % 2 === 0) || + (!even && node.val % 2 === 1)) { + return false; + } + + if (levels.length === level) { + levels.push(node.val); + } else { + if ((even && node.val <= levels[level]) || + (!even && node.val >= levels[level])) { + return false; + } + levels[level] = node.val; + } + + return dfs(node.left, level + 1) && dfs(node.right, level + 1); + }; + + return dfs(root, 0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Iterative DFS + +::tabs-start + +```python +# 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 +class Solution: + def isEvenOddTree(self, root: Optional[TreeNode]) -> bool: + stack = [(root, 0)] + levels = [] + + while stack: + node, level = stack.pop() + + even = level % 2 == 0 + if ((even and node.val % 2 == 0) or + (not even and node.val % 2 == 1) + ): + return False + + if len(levels) == level: + levels.append(node.val) + else: + if ((even and node.val <= levels[level]) or + (not even and node.val >= levels[level]) + ): + return False + levels[level] = node.val + + if node.right: + stack.append((node.right, level + 1)) + if node.left: + stack.append((node.left, level + 1)) + + return True +``` + +```java +/** + * 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; + * } + * } + */ +public class Solution { + public boolean isEvenOddTree(TreeNode root) { + Stack> stack = new Stack<>(); + stack.push(new Pair<>(root, 0)); + List levels = new ArrayList<>(); + + while (!stack.isEmpty()) { + Pair pair = stack.pop(); + TreeNode node = pair.getKey(); + int level = pair.getValue(); + + boolean even = level % 2 == 0; + if ((even && node.val % 2 == 0) || + (!even && node.val % 2 == 1)) + return false; + + if (levels.size() == level) { + levels.add(node.val); + } else { + if ((even && node.val <= levels.get(level)) || + (!even && node.val >= levels.get(level))) + return false; + levels.set(level, node.val); + } + + if (node.right != null) stack.push(new Pair<>(node.right, level + 1)); + if (node.left != null) stack.push(new Pair<>(node.left, level + 1)); + } + + return true; + } +} +``` + +```cpp +/** + * 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: + bool isEvenOddTree(TreeNode* root) { + stack> stack; + stack.push({root, 0}); + vector levels; + + while (!stack.empty()) { + auto [node, level] = stack.top(); + stack.pop(); + + bool even = level % 2 == 0; + if ((even && node->val % 2 == 0) || + (!even && node->val % 2 == 1)) + return false; + + if (levels.size() == level) { + levels.push_back(node->val); + } else { + if ((even && node->val <= levels[level]) || + (!even && node->val >= levels[level])) + return false; + levels[level] = node->val; + } + + if (node->right) stack.push({node->right, level + 1}); + if (node->left) stack.push({node->left, level + 1}); + } + + return true; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {boolean} + */ + isEvenOddTree(root) { + const stack = [[root, 0]]; + const levels = []; + + while (stack.length) { + const [node, level] = stack.pop(); + + const even = level % 2 === 0; + if ((even && node.val % 2 === 0) || + (!even && node.val % 2 === 1)) + return false; + + if (levels.length === level) { + levels.push(node.val); + } else { + if ((even && node.val <= levels[level]) || + (!even && node.val >= levels[level])) + return false; + levels[level] = node.val; + } + + if (node.right) stack.push([node.right, level + 1]); + if (node.left) stack.push([node.left, level + 1]); + } + + return true; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/find-bottom-left-tree-value.md b/articles/find-bottom-left-tree-value.md new file mode 100644 index 000000000..c04645674 --- /dev/null +++ b/articles/find-bottom-left-tree-value.md @@ -0,0 +1,633 @@ +## 1. Breadth First Search + +::tabs-start + +```python +# 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 +class Solution: + def findBottomLeftValue(self, root: Optional[TreeNode]) -> int: + q = deque([root]) + + while q: + node = q.popleft() + if node.right: + q.append(node.right) + if node.left: + q.append(node.left) + + return node.val +``` + +```java +/** + * 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; + * } + * } + */ +public class Solution { + public int findBottomLeftValue(TreeNode root) { + Queue q = new LinkedList<>(); + q.offer(root); + TreeNode node = null; + + while (!q.isEmpty()) { + node = q.poll(); + if (node.right != null) q.offer(node.right); + if (node.left != null) q.offer(node.left); + } + return node.val; + } +} +``` + +```cpp +/** + * 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: + int findBottomLeftValue(TreeNode* root) { + queue q; + q.push(root); + TreeNode* node = nullptr; + + while (!q.empty()) { + node = q.front(); + q.pop(); + if (node->right) q.push(node->right); + if (node->left) q.push(node->left); + } + return node->val; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {number} + */ + findBottomLeftValue(root) { + const q = new Queue([root]); + let node = null; + + while (!q.isEmpty()) { + node = q.pop(); + if (node.right) q.push(node.right); + if (node.left) q.push(node.left); + } + return node.val; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Depth First Search + +::tabs-start + +```python +# 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 +class Solution: + def findBottomLeftValue(self, root: Optional[TreeNode]) -> int: + self.maxDepth, self.res = -1, root.val + + def dfs(node, depth): + if not node: + return + if depth > self.maxDepth: + self.maxDepth, self.res = depth, node.val + + dfs(node.left, depth + 1) + dfs(node.right, depth + 1) + + dfs(root, 0) + return self.res +``` + +```java +/** + * 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; + * } + * } + */ +public class Solution { + private int maxDepth = -1; + private int res; + + public int findBottomLeftValue(TreeNode root) { + res = root.val; + dfs(root, 0); + return res; + } + + private void dfs(TreeNode node, int depth) { + if (node == null) return; + if (depth > maxDepth) { + maxDepth = depth; + res = node.val; + } + + dfs(node.left, depth + 1); + dfs(node.right, depth + 1); + } +} +``` + +```cpp +/** + * 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: + int maxDepth = -1, res; + + int findBottomLeftValue(TreeNode* root) { + res = root->val; + dfs(root, 0); + return res; + } + +private: + void dfs(TreeNode* node, int depth) { + if (!node) return; + if (depth > maxDepth) { + maxDepth = depth; + res = node->val; + } + + dfs(node->left, depth + 1); + dfs(node->right, depth + 1); + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {number} + */ + findBottomLeftValue(root) { + let maxDepth = -1, res = root.val; + + const dfs = (node, depth) => { + if (!node) return; + if (depth > maxDepth) { + maxDepth = depth; + res = node.val; + } + + dfs(node.left, depth + 1); + dfs(node.right, depth + 1); + }; + + dfs(root, 0); + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ for recursion stack. + +--- + +## 3. Iterative DFS + +::tabs-start + +```python +# 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 +class Solution: + def findBottomLeftValue(self, root: Optional[TreeNode]) -> int: + res, maxDepth = root.val, -1 + stack = [(root, 0)] + + while stack: + node, depth = stack.pop() + if depth > maxDepth: + maxDepth = depth + res = node.val + + if node.right: + stack.append((node.right, depth + 1)) + if node.left: + stack.append((node.left, depth + 1)) + + return res +``` + +```java +/** + * 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; + * } + * } + */ +public class Solution { + public int findBottomLeftValue(TreeNode root) { + int res = root.val, maxDepth = -1; + Stack> stack = new Stack<>(); + stack.push(new Pair<>(root, 0)); + + while (!stack.isEmpty()) { + Pair p = stack.pop(); + TreeNode node = p.getKey(); + int depth = p.getValue(); + if (depth > maxDepth) { + maxDepth = depth; + res = node.val; + } + + if (node.right != null) { + stack.push(new Pair<>(node.right, depth + 1)); + } + if (node.left != null) { + stack.push(new Pair<>(node.left, depth + 1)); + } + } + + return res; + } +} +``` + +```cpp +/** + * 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: + int findBottomLeftValue(TreeNode* root) { + int res = root->val, maxDepth = -1; + stack> stack; + stack.push({root, 0}); + + while (!stack.empty()) { + auto [node, depth] = stack.top();stack.pop(); + if (depth > maxDepth) { + maxDepth = depth; + res = node->val; + } + + if (node->right) { + stack.push({node->right, depth + 1}); + } + if (node->left) { + stack.push({node->left, depth + 1}); + } + } + + return res; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {number} + */ + findBottomLeftValue(root) { + let res = root.val, maxDepth = -1; + const stack = [[root, 0]]; + + while (stack.length) { + const [node, depth] = stack.pop(); + if (depth > maxDepth) { + maxDepth = depth; + res = node.val; + } + + if (node.right) stack.push([node.right, depth + 1]); + if (node.left) stack.push([node.left, depth + 1]); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 4. Morris Traversal + +::tabs-start + +```python +# 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 +class Solution: + def findBottomLeftValue(self, root: Optional[TreeNode]) -> int: + res, maxDepth, curDepth = root.val, -1, 0 + cur = root + + while cur: + if not cur.left: + if curDepth > maxDepth: + maxDepth, res = curDepth, cur.val + cur = cur.right + curDepth += 1 + else: + prev = cur.left + steps = 1 + while prev.right and prev.right != cur: + prev = prev.right + steps += 1 + + if not prev.right: + prev.right = cur + cur = cur.left + curDepth += 1 + else: + prev.right = None + curDepth -= steps + cur = cur.right + + return res +``` + +```java +/** + * 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; + * } + * } + */ +public class Solution { + public int findBottomLeftValue(TreeNode root) { + int res = root.val, maxDepth = -1, curDepth = 0; + TreeNode cur = root; + + while (cur != null) { + if (cur.left == null) { + if (curDepth > maxDepth) { + maxDepth = curDepth; + res = cur.val; + } + cur = cur.right; + curDepth++; + } else { + TreeNode prev = cur.left; + int steps = 1; + while (prev.right != null && prev.right != cur) { + prev = prev.right; + steps++; + } + + if (prev.right == null) { + prev.right = cur; + cur = cur.left; + curDepth++; + } else { + prev.right = null; + curDepth -= steps; + cur = cur.right; + } + } + } + return res; + } +} +``` + +```cpp +/** + * 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: + int findBottomLeftValue(TreeNode* root) { + int res = root->val, maxDepth = -1, curDepth = 0; + TreeNode* cur = root; + + while (cur) { + if (!cur->left) { + if (curDepth > maxDepth) { + maxDepth = curDepth; + res = cur->val; + } + cur = cur->right; + curDepth++; + } else { + TreeNode* prev = cur->left; + int steps = 1; + while (prev->right && prev->right != cur) { + prev = prev->right; + steps++; + } + + if (!prev->right) { + prev->right = cur; + cur = cur->left; + curDepth++; + } else { + prev->right = nullptr; + curDepth -= steps; + cur = cur->right; + } + } + } + return res; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {number} + */ + findBottomLeftValue(root) { + let res = root.val, maxDepth = -1, curDepth = 0; + let cur = root; + + while (cur) { + if (!cur.left) { + if (curDepth > maxDepth) { + maxDepth = curDepth; + res = cur.val; + } + cur = cur.right; + curDepth++; + } else { + let prev = cur.left, steps = 1; + while (prev.right && prev.right !== cur) { + prev = prev.right; + steps++; + } + + if (!prev.right) { + prev.right = cur; + cur = cur.left; + curDepth++; + } else { + prev.right = null; + curDepth -= steps; + cur = cur.right; + } + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. \ No newline at end of file diff --git a/articles/find-largest-value-in-each-tree-row.md b/articles/find-largest-value-in-each-tree-row.md new file mode 100644 index 000000000..d54ce9dfd --- /dev/null +++ b/articles/find-largest-value-in-each-tree-row.md @@ -0,0 +1,462 @@ +## 1. Breadth First Search + +::tabs-start + +```python +# 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 +class Solution: + def largestValues(self, root: Optional[TreeNode]) -> List[int]: + if not root: + return [] + + res = [] + q = deque([root]) + while q: + row_max = q[0].val + for _ in range(len(q)): + node = q.popleft() + row_max = max(row_max, node.val) + if node.left: + q.append(node.left) + if node.right: + q.append(node.right) + res.append(row_max) + + return res +``` + +```java +/** + * 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; + * } + * } + */ +public class Solution { + public List largestValues(TreeNode root) { + if (root == null) return new ArrayList<>(); + + List res = new ArrayList<>(); + Queue q = new LinkedList<>(); + q.offer(root); + + while (!q.isEmpty()) { + int rowMax = q.peek().val; + for (int i = q.size(); i > 0; i--) { + TreeNode node = q.poll(); + rowMax = Math.max(rowMax, node.val); + if (node.left != null) q.offer(node.left); + if (node.right != null) q.offer(node.right); + } + res.add(rowMax); + } + + return res; + } +} +``` + +```cpp +/** + * 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: + vector largestValues(TreeNode* root) { + if (!root) return {}; + + vector res; + queue q; + q.push(root); + + while (!q.empty()) { + int rowMax = q.front()->val; + for (int i = q.size(); i > 0; i--) { + TreeNode* node = q.front(); q.pop(); + rowMax = max(rowMax, node->val); + if (node->left) q.push(node->left); + if (node->right) q.push(node->right); + } + res.push_back(rowMax); + } + + return res; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {number[]} + */ + largestValues(root) { + if (!root) return []; + + const res = []; + const q = new Queue([root]); + + while (!q.isEmpty()) { + let rowMax = q.front().val; + for (let i = q.size(); i > 0; i--) { + const node = q.pop(); + rowMax = Math.max(rowMax, node.val); + if (node.left) q.push(node.left); + if (node.right) q.push(node.right); + } + res.push(rowMax); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Depth First Search + +::tabs-start + +```python +# 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 +class Solution: + def largestValues(self, root: Optional[TreeNode]) -> List[int]: + if not root: + return [] + + res = [] + def dfs(node, level): + if not node: + return + if level == len(res): + res.append(node.val) + else: + res[level] = max(res[level], node.val) + + dfs(node.left, level + 1) + dfs(node.right, level + 1) + + dfs(root, 0) + return res +``` + +```java +/** + * 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; + * } + * } + */ +public class Solution { + public List largestValues(TreeNode root) { + List res = new ArrayList<>(); + dfs(root, 0, res); + return res; + } + + private void dfs(TreeNode node, int level, List res) { + if (node == null) return; + if (level == res.size()) { + res.add(node.val); + } else { + res.set(level, Math.max(res.get(level), node.val)); + } + + dfs(node.left, level + 1, res); + dfs(node.right, level + 1, res); + } +} +``` + +```cpp +/** + * 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: + vector largestValues(TreeNode* root) { + vector res; + dfs(root, 0, res); + return res; + } + +private: + void dfs(TreeNode* node, int level, vector& res) { + if (!node) return; + if (level == res.size()) { + res.push_back(node->val); + } else { + res[level] = max(res[level], node->val); + } + + dfs(node->left, level + 1, res); + dfs(node->right, level + 1, res); + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {number[]} + */ + largestValues(root) { + if (!root) return []; + + const res = []; + const dfs = (node, level) => { + if (!node) return; + if (level === res.length) { + res.push(node.val); + } else { + res[level] = Math.max(res[level], node.val); + } + dfs(node.left, level + 1); + dfs(node.right, level + 1); + }; + + dfs(root, 0); + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ for recursion stack. + +--- + +## 3. Iterative DFS + +::tabs-start + +```python +# 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 +class Solution: + def largestValues(self, root: Optional[TreeNode]) -> List[int]: + if not root: + return [] + + res = [] + stack = [(root, 0)] + while stack: + node, level = stack.pop() + if level == len(res): + res.append(node.val) + else: + res[level] = max(res[level], node.val) + + if node.right: + stack.append((node.right, level + 1)) + if node.left: + stack.append((node.left, level + 1)) + + return res +``` + +```java +/** + * 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; + * } + * } + */ +public class Solution { + public List largestValues(TreeNode root) { + if (root == null) return new ArrayList<>(); + + List res = new ArrayList<>(); + Stack> stack = new Stack<>(); + stack.push(new Pair<>(root, 0)); + while (!stack.isEmpty()) { + Pair p = stack.pop(); + TreeNode node = p.getKey(); + int level = p.getValue(); + if (level == res.size()) { + res.add(node.val); + } else { + res.set(level, Math.max(res.get(level), node.val)); + } + + if (node.right != null) stack.push(new Pair<>(node.right, level + 1)); + if (node.left != null) stack.push(new Pair<>(node.left, level + 1)); + } + + return res; + } +} +``` + +```cpp +/** + * 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: + vector largestValues(TreeNode* root) { + if (!root) return {}; + + vector res; + stack> stk; + stk.push({root, 0}); + while (!stk.empty()) { + auto [node, level] = stk.top();stk.pop(); + if (level == res.size()) { + res.push_back(node->val); + } else { + res[level] = max(res[level], node->val); + } + + if (node->right) stk.push({node->right, level + 1}); + if (node->left) stk.push({node->left, level + 1}); + } + + return res; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {number[]} + */ + largestValues(root) { + if (!root) return []; + + const res = []; + const stack = [[root, 0]]; + while (stack.length) { + const [node, level] = stack.pop(); + if (level === res.length) { + res.push(node.val); + } else { + res[level] = Math.max(res[level], node.val); + } + + if (node.right) stack.push([node.right, level + 1]); + if (node.left) stack.push([node.left, level + 1]); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/flip-equivalent-binary-trees.md b/articles/flip-equivalent-binary-trees.md new file mode 100644 index 000000000..2a2549798 --- /dev/null +++ b/articles/flip-equivalent-binary-trees.md @@ -0,0 +1,492 @@ +## 1. Depth First Search + +::tabs-start + +```python +# 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 +class Solution: + def flipEquiv(self, root1: Optional[TreeNode], root2: Optional[TreeNode]) -> bool: + if not root1 or not root2: + return not root1 and not root2 + if root1.val != root2.val: + return False + + return ( + self.flipEquiv(root1.left, root2.left) and + self.flipEquiv(root1.right, root2.right) or + self.flipEquiv(root1.left, root2.right) and + self.flipEquiv(root1.right, root2.left) + ) +``` + +```java +/** + * 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; + * } + * } + */ +public class Solution { + public boolean flipEquiv(TreeNode root1, TreeNode root2) { + if (root1 == null || root2 == null) + return root1 == null && root2 == null; + if (root1.val != root2.val) + return false; + + return (flipEquiv(root1.left, root2.left) && + flipEquiv(root1.right, root2.right) || + flipEquiv(root1.left, root2.right) && + flipEquiv(root1.right, root2.left)); + } +} +``` + +```cpp +/** + * 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: + bool flipEquiv(TreeNode* root1, TreeNode* root2) { + if (!root1 || !root2) + return !root1 && !root2; + if (root1->val != root2->val) + return false; + + return (flipEquiv(root1->left, root2->left) && + flipEquiv(root1->right, root2->right) || + flipEquiv(root1->left, root2->right) && + flipEquiv(root1->right, root2->left)); + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root1 + * @param {TreeNode} root2 + * @return {boolean} + */ + flipEquiv(root1, root2) { + if (!root1 || !root2) + return !root1 && !root2; + if (root1.val !== root2.val) + return false; + + return (this.flipEquiv(root1.left, root2.left) && + this.flipEquiv(root1.right, root2.right) || + this.flipEquiv(root1.left, root2.right) && + this.flipEquiv(root1.right, root2.left)); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ for recursion stack. + +--- + +## 2. Breadth First Search + +::tabs-start + +```python +# 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 +class Solution: + def flipEquiv(self, root1: Optional[TreeNode], root2: Optional[TreeNode]) -> bool: + q = deque([(root1, root2)]) + + while q: + node1, node2 = q.popleft() + if not node1 or not node2: + if node1 != node2: + return False + continue + + if node1.val != node2.val: + return False + + if ((node1.right and node2.right and + node1.right.val == node2.right.val) or + (not node1.right and not node2.right) + ): + q.append((node1.left, node2.left)) + q.append((node1.right, node2.right)) + else: + q.append((node1.left, node2.right)) + q.append((node1.right, node2.left)) + + return True +``` + +```java +/** + * 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; + * } + * } + */ +public class Solution { + public boolean flipEquiv(TreeNode root1, TreeNode root2) { + Queue q = new LinkedList<>(); + q.offer(new TreeNode[]{root1, root2}); + + while (!q.isEmpty()) { + TreeNode[] pair = q.poll(); + TreeNode node1 = pair[0], node2 = pair[1]; + + if (node1 == null || node2 == null) { + if (node1 != node2) return false; + continue; + } + + if (node1.val != node2.val) return false; + + if ((node1.left != null && node2.left != null && + node1.left.val == node2.left.val) || + (node1.left == null && node2.left == null)) { + q.offer(new TreeNode[]{node1.left, node2.left}); + q.offer(new TreeNode[]{node1.right, node2.right}); + } else { + q.offer(new TreeNode[]{node1.left, node2.right}); + q.offer(new TreeNode[]{node1.right, node2.left}); + } + } + + return true; + } +} +``` + +```cpp +/** + * 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: + bool flipEquiv(TreeNode* root1, TreeNode* root2) { + queue> q; + q.push({root1, root2}); + + while (!q.empty()) { + auto [node1, node2] = q.front(); + q.pop(); + + if (!node1 || !node2) { + if (node1 != node2) return false; + continue; + } + + if (node1->val != node2->val) return false; + + if ((node1->left && node2->left && node1->left->val == node2->left->val) || + (!node1->left && !node2->left)) { + q.push({node1->left, node2->left}); + q.push({node1->right, node2->right}); + } else { + q.push({node1->left, node2->right}); + q.push({node1->right, node2->left}); + } + } + + return true; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root1 + * @param {TreeNode} root2 + * @return {boolean} + */ + flipEquiv(root1, root2) { + let q = new Queue([[root1, root2]]); + + while (!q.isEmpty()) { + let [node1, node2] = q.pop(); + + if (!node1 || !node2) { + if (node1 !== node2) return false; + continue; + } + + if (node1.val !== node2.val) return false; + + if ((node1.left && node2.left && + node1.left.val === node2.left.val) || + (!node1.left && !node2.left)) { + q.push([node1.left, node2.left]); + q.push([node1.right, node2.right]); + } else { + q.push([node1.left, node2.right]); + q.push([node1.right, node2.left]); + } + } + + return true; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Iterative DFS + +::tabs-start + +```python +# 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 +class Solution: + def flipEquiv(self, root1: Optional[TreeNode], root2: Optional[TreeNode]) -> bool: + stack = [(root1, root2)] + + while stack: + node1, node2 = stack.pop() + + if not node1 or not node2: + if node1 != node2: + return False + continue + + if node1.val != node2.val: + return False + + if ((node1.left and node2.left and + node1.left.val == node2.left.val) or + (not node1.left and not node2.left) + ): + stack.append((node1.left, node2.left)) + stack.append((node1.right, node2.right)) + else: + stack.append((node1.left, node2.right)) + stack.append((node1.right, node2.left)) + + return True +``` + +```java +/** + * 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; + * } + * } + */ +public class Solution { + public boolean flipEquiv(TreeNode root1, TreeNode root2) { + Stack stack = new Stack<>(); + stack.push(new TreeNode[]{root1, root2}); + + while (!stack.isEmpty()) { + TreeNode[] pair = stack.pop(); + TreeNode node1 = pair[0], node2 = pair[1]; + + if (node1 == null || node2 == null) { + if (node1 != node2) return false; + continue; + } + + if (node1.val != node2.val) return false; + + if ((node1.left != null && node2.left != null && + node1.left.val == node2.left.val) || + (node1.left == null && node2.left == null)) { + stack.push(new TreeNode[]{node1.left, node2.left}); + stack.push(new TreeNode[]{node1.right, node2.right}); + } else { + stack.push(new TreeNode[]{node1.left, node2.right}); + stack.push(new TreeNode[]{node1.right, node2.left}); + } + } + + return true; + } +} +``` + +```cpp +/** + * 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: + bool flipEquiv(TreeNode* root1, TreeNode* root2) { + stack> stk; + stk.push({root1, root2}); + + while (!stk.empty()) { + auto [node1, node2] = stk.top();stk.pop(); + + if (!node1 || !node2) { + if (node1 != node2) return false; + continue; + } + + if (node1->val != node2->val) return false; + + if ((node1->left && node2->left && node1->left->val == node2->left->val) || + (!node1->left && !node2->left)) { + stk.push({node1->left, node2->left}); + stk.push({node1->right, node2->right}); + } else { + stk.push({node1->left, node2->right}); + stk.push({node1->right, node2->left}); + } + } + + return true; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root1 + * @param {TreeNode} root2 + * @return {boolean} + */ + flipEquiv(root1, root2) { + let stack = [[root1, root2]]; + + while (stack.length > 0) { + let [node1, node2] = stack.pop(); + + if (!node1 || !node2) { + if (node1 !== node2) return false; + continue; + } + + if (node1.val !== node2.val) return false; + + if ((node1.left && node2.left && + node1.left.val === node2.left.val) || + (!node1.left && !node2.left)) { + stack.push([node1.left, node2.left]); + stack.push([node1.right, node2.right]); + } else { + stack.push([node1.left, node2.right]); + stack.push([node1.right, node2.left]); + } + } + + return true; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/insertion-sort-list.md b/articles/insertion-sort-list.md new file mode 100644 index 000000000..a567be9b0 --- /dev/null +++ b/articles/insertion-sort-list.md @@ -0,0 +1,411 @@ +## 1. Convert To Array + +::tabs-start + +```python +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, val=0, next=None): +# self.val = val +# self.next = next +class Solution: + def insertionSortList(self, head: Optional[ListNode]) -> Optional[ListNode]: + arr = [] + cur = head + while cur: + arr.append(cur.val) + cur = cur.next + + arr.sort() + cur = head + for val in arr: + cur.val = val + cur = cur.next + return head +``` + +```java +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode() {} + * ListNode(int val) { this.val = val; } + * ListNode(int val, ListNode next) { this.val = val; this.next = next; } + * } + */ +public class Solution { + public ListNode insertionSortList(ListNode head) { + List arr = new ArrayList<>(); + ListNode cur = head; + + while (cur != null) { + arr.add(cur.val); + cur = cur.next; + } + + Collections.sort(arr); + cur = head; + + for (int val : arr) { + cur.val = val; + cur = cur.next; + } + return head; + } +} +``` + +```cpp +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode() : val(0), next(nullptr) {} + * ListNode(int x) : val(x), next(nullptr) {} + * ListNode(int x, ListNode *next) : val(x), next(next) {} + * }; + */ +class Solution { +public: + ListNode* insertionSortList(ListNode* head) { + vector arr; + ListNode* cur = head; + + while (cur) { + arr.push_back(cur->val); + cur = cur->next; + } + + sort(arr.begin(), arr.end()); + cur = head; + + for (int val : arr) { + cur->val = val; + cur = cur->next; + } + return head; + } +}; +``` + +```javascript +/** + * Definition for singly-linked list. + * class ListNode { + * constructor(val = 0, next = null) { + * this.val = val; + * this.next = next; + * } + * } + */ +class Solution { + /** + * @param {ListNode} head + * @return {ListNode} + */ + insertionSortList(head) { + let arr = []; + let cur = head; + + while (cur) { + arr.push(cur.val); + cur = cur.next; + } + + arr.sort((a, b) => a - b); + cur = head; + + for (let val of arr) { + cur.val = val; + cur = cur.next; + } + return head; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Swapping Values + +::tabs-start + +```python +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, val=0, next=None): +# self.val = val +# self.next = next +class Solution: + def insertionSortList(self, head: Optional[ListNode]) -> Optional[ListNode]: + cur = head.next + while cur: + tmp = head + while tmp != cur: + if tmp.val > cur.val: + tmp.val, cur.val = cur.val, tmp.val + tmp = tmp.next + cur = cur.next + return head +``` + +```java +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode() {} + * ListNode(int val) { this.val = val; } + * ListNode(int val, ListNode next) { this.val = val; this.next = next; } + * } + */ +public class Solution { + public ListNode insertionSortList(ListNode head) { + for (ListNode cur = head.next; cur != null; cur = cur.next) { + for (ListNode tmp = head; tmp != cur; tmp = tmp.next) { + if (tmp.val > cur.val) { + int swap = tmp.val; + tmp.val = cur.val; + cur.val = swap; + } + } + } + return head; + } +} +``` + +```cpp +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode() : val(0), next(nullptr) {} + * ListNode(int x) : val(x), next(nullptr) {} + * ListNode(int x, ListNode *next) : val(x), next(next) {} + * }; + */ +class Solution { +public: + ListNode* insertionSortList(ListNode* head) { + for (ListNode* cur = head->next; cur; cur = cur->next) { + for (ListNode* tmp = head; tmp != cur; tmp = tmp->next) { + if (tmp->val > cur->val) { + swap(tmp->val, cur->val); + } + } + } + return head; + } +}; +``` + +```javascript +/** + * Definition for singly-linked list. + * class ListNode { + * constructor(val = 0, next = null) { + * this.val = val; + * this.next = next; + * } + * } + */ +class Solution { + /** + * @param {ListNode} head + * @return {ListNode} + */ + insertionSortList(head) { + for (let cur = head.next; cur; cur = cur.next) { + for (let tmp = head; tmp !== cur; tmp = tmp.next) { + if (tmp.val > cur.val) { + [tmp.val, cur.val] = [cur.val, tmp.val]; + } + } + } + return head; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(1)$ extra space. + +--- + +## 3. Swapping Nodes + +::tabs-start + +```python +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, val=0, next=None): +# self.val = val +# self.next = next +class Solution: + def insertionSortList(self, head: Optional[ListNode]) -> Optional[ListNode]: + dummy = ListNode(0, head) + prev, cur = head, head.next + + while cur: + if cur.val >= prev.val: + prev, cur = cur, cur.next + continue + + tmp = dummy + while cur.val > tmp.next.val: + tmp = tmp.next + + prev.next = cur.next + cur.next = tmp.next + tmp.next = cur + cur = prev.next + + return dummy.next +``` + +```java +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode() {} + * ListNode(int val) { this.val = val; } + * ListNode(int val, ListNode next) { this.val = val; this.next = next; } + * } + */ +public class Solution { + public ListNode insertionSortList(ListNode head) { + ListNode dummy = new ListNode(0, head); + ListNode prev = head, cur = head.next; + + while (cur != null) { + if (cur.val >= prev.val) { + prev = cur; + cur = cur.next; + continue; + } + + ListNode tmp = dummy; + while (tmp.next.val < cur.val) { + tmp = tmp.next; + } + + prev.next = cur.next; + cur.next = tmp.next; + tmp.next = cur; + cur = prev.next; + } + + return dummy.next; + } +} +``` + +```cpp +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode() : val(0), next(nullptr) {} + * ListNode(int x) : val(x), next(nullptr) {} + * ListNode(int x, ListNode *next) : val(x), next(next) {} + * }; + */ +class Solution { +public: + ListNode* insertionSortList(ListNode* head) { + ListNode* dummy = new ListNode(0, head); + ListNode* prev = head; + ListNode* cur = head->next; + + while (cur) { + if (cur->val >= prev->val) { + prev = cur; + cur = cur->next; + continue; + } + + ListNode* tmp = dummy; + while (tmp->next->val < cur->val) { + tmp = tmp->next; + } + + prev->next = cur->next; + cur->next = tmp->next; + tmp->next = cur; + cur = prev->next; + } + + return dummy->next; + } +}; +``` + +```javascript +/** + * Definition for singly-linked list. + * class ListNode { + * constructor(val = 0, next = null) { + * this.val = val; + * this.next = next; + * } + * } + */ +class Solution { + /** + * @param {ListNode} head + * @return {ListNode} + */ + insertionSortList(head) { + let dummy = new ListNode(0, head); + let prev = head, cur = head.next; + + while (cur) { + if (cur.val >= prev.val) { + prev = cur; + cur = cur.next; + continue; + } + + let tmp = dummy; + while (tmp.next.val < cur.val) { + tmp = tmp.next; + } + + prev.next = cur.next; + cur.next = tmp.next; + tmp.next = cur; + cur = prev.next; + } + + return dummy.next; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(1)$ extra space. \ No newline at end of file diff --git a/articles/minimum-fuel-cost-to-report-to-the-capital.md b/articles/minimum-fuel-cost-to-report-to-the-capital.md new file mode 100644 index 000000000..77dda576e --- /dev/null +++ b/articles/minimum-fuel-cost-to-report-to-the-capital.md @@ -0,0 +1,299 @@ +## 1. Depth First Search + +::tabs-start + +```python +class Solution: + def minimumFuelCost(self, roads: list[list[int]], seats: int) -> int: + adj = defaultdict(list) + for src, dst in roads: + adj[src].append(dst) + adj[dst].append(src) + + res = 0 + def dfs(node, parent): + nonlocal res + passengers = 0 + for child in adj[node]: + if child != parent: + p = dfs(child, node) + passengers += p + res += ceil(p / seats) + return passengers + 1 + + dfs(0, -1) + return res +``` + +```java +public class Solution { + private List[] adj; + private long res = 0; + + public long minimumFuelCost(int[][] roads, int seats) { + int n = roads.length + 1; + adj = new ArrayList[n]; + + for (int i = 0; i < n; i++) { + adj[i] = new ArrayList<>(); + } + + for (int[] road : roads) { + adj[road[0]].add(road[1]); + adj[road[1]].add(road[0]); + } + + dfs(0, -1, seats); + return res; + } + + private int dfs(int node, int parent, int seats) { + int passengers = 0; + for (int child : adj[node]) { + if (child != parent) { + int p = dfs(child, node, seats); + passengers += p; + res += Math.ceil((double) p / seats); + } + } + return passengers + 1; + } +} +``` + +```cpp +class Solution { +private: + vector> adj; + long long res = 0; + +public: + long long minimumFuelCost(vector>& roads, int seats) { + int n = roads.size() + 1; + adj.resize(n); + + for (auto& road : roads) { + adj[road[0]].push_back(road[1]); + adj[road[1]].push_back(road[0]); + } + + dfs(0, -1, seats); + return res; + } + +private: + int dfs(int node, int parent, int seats) { + int passengers = 0; + for (int child : adj[node]) { + if (child != parent) { + int p = dfs(child, node, seats); + passengers += p; + res += ceil((double) p / seats); + } + } + return passengers + 1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} roads + * @param {number} seats + * @return {number} + */ + minimumFuelCost(roads, seats) { + const n = roads.length + 1; + const adj = Array.from({ length: n }, () => []); + let res = 0; + + for (const [src, dst] of roads) { + adj[src].push(dst); + adj[dst].push(src); + } + + const dfs = (node, parent) => { + let passengers = 0; + for (const child of adj[node]) { + if (child !== parent) { + let p = dfs(child, node); + passengers += p; + res += Math.ceil(p / seats); + } + } + return passengers + 1; + }; + + dfs(0, -1); + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Topological Sort (Kahn's Algorithm) + +::tabs-start + +```python +class Solution: + def minimumFuelCost(self, roads: list[list[int]], seats: int) -> int: + n = len(roads) + 1 + adj = [[] for _ in range(n)] + indegree = [0] * n + passengers = [1] * n + res = 0 + + for src, dst in roads: + adj[src].append(dst) + adj[dst].append(src) + indegree[src] += 1 + indegree[dst] += 1 + + q = deque() + for i in range(1, n): + if indegree[i] == 1: + q.append(i) + + while q: + node = q.popleft() + res += math.ceil(passengers[node] / seats) + for parent in adj[node]: + indegree[parent] -= 1 + if indegree[parent] == 1 and parent != 0: + q.append(parent) + passengers[parent] += passengers[node] + + return res +``` + +```java +public class Solution { + public long minimumFuelCost(int[][] roads, int seats) { + int n = roads.length + 1; + List[] adj = new ArrayList[n]; + int[] indegree = new int[n]; + int[] passengers = new int[n]; + Arrays.fill(passengers, 1); + long res = 0; + + for (int i = 0; i < n; i++) adj[i] = new ArrayList<>(); + + for (int[] road : roads) { + int src = road[0], dst = road[1]; + adj[src].add(dst); + adj[dst].add(src); + indegree[src]++; + indegree[dst]++; + } + + Queue q = new LinkedList<>(); + for (int i = 1; i < n; i++) { + if (indegree[i] == 1) q.offer(i); + } + + while (!q.isEmpty()) { + int node = q.poll(); + res += (int) Math.ceil((double) passengers[node] / seats); + for (int parent : adj[node]) { + if (--indegree[parent] == 1 && parent != 0) q.offer(parent); + passengers[parent] += passengers[node]; + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + long long minimumFuelCost(vector>& roads, int seats) { + int n = roads.size() + 1; + vector> adj(n); + vector indegree(n, 0), passengers(n, 1); + long long res = 0; + + for (auto& road : roads) { + int src = road[0], dst = road[1]; + adj[src].push_back(dst); + adj[dst].push_back(src); + indegree[src]++; + indegree[dst]++; + } + + queue q; + for (int i = 1; i < n; i++) { + if (indegree[i] == 1) q.push(i); + } + + while (!q.empty()) { + int node = q.front();q.pop(); + res += ceil((double) passengers[node] / seats); + for (int parent : adj[node]) { + if (--indegree[parent] == 1 && parent != 0) q.push(parent); + passengers[parent] += passengers[node]; + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} roads + * @param {number} seats + * @return {number} + */ + minimumFuelCost(roads, seats) { + const n = roads.length + 1; + const adj = Array.from({ length: n }, () => []); + const indegree = new Array(n).fill(0); + const passengers = new Array(n).fill(1); + let res = 0; + + for (const [src, dst] of roads) { + adj[src].push(dst); + adj[dst].push(src); + indegree[src] += 1; + indegree[dst] += 1; + } + + const q = new Queue(); + for (let i = 1; i < n; i++) { + if (indegree[i] === 1) q.push(i); + } + + while (!q.isEmpty()) { + const node = q.pop(); + res += Math.ceil(passengers[node] / seats); + for (const parent of adj[node]) { + if (--indegree[parent] === 1 && parent !== 0) q.push(parent); + passengers[parent] += passengers[node]; + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/minimum-score-of-a-path-between-two-cities.md b/articles/minimum-score-of-a-path-between-two-cities.md new file mode 100644 index 000000000..820b21739 --- /dev/null +++ b/articles/minimum-score-of-a-path-between-two-cities.md @@ -0,0 +1,646 @@ +## 1. Depth First Search + +::tabs-start + +```python +class Solution: + def minScore(self, n: int, roads: list[list[int]]) -> int: + adj = defaultdict(list) + for src, dst, dist in roads: + adj[src].append((dst, dist)) + adj[dst].append((src, dist)) + + res = float("inf") + visit = set() + + def dfs(node): + nonlocal res + if node in visit: + return + visit.add(node) + for nei, dist in adj[node]: + res = min(res, dist) + dfs(nei) + + dfs(1) + return res +``` + +```java +public class Solution { + private List[] adj; + private boolean[] visit; + private int res; + + public int minScore(int n, int[][] roads) { + adj = new ArrayList[n + 1]; + visit = new boolean[n + 1]; + res = Integer.MAX_VALUE; + + for (int i = 0; i <= n; i++) { + adj[i] = new ArrayList<>(); + } + + for (int[] road : roads) { + adj[road[0]].add(new int[]{road[1], road[2]}); + adj[road[1]].add(new int[]{road[0], road[2]}); + } + + dfs(1); + return res; + } + + private void dfs(int node) { + if (visit[node]) return; + visit[node] = true; + + for (int[] edge : adj[node]) { + res = Math.min(res, edge[1]); + dfs(edge[0]); + } + } +} +``` + +```cpp +class Solution { +public: + vector>> adj; + vector visit; + int res; + + int minScore(int n, vector>& roads) { + adj.resize(n + 1); + visit.resize(n + 1, false); + res = INT_MAX; + + for (auto& road : roads) { + adj[road[0]].push_back({road[1], road[2]}); + adj[road[1]].push_back({road[0], road[2]}); + } + + dfs(1); + return res; + } + + void dfs(int node) { + if (visit[node]) return; + visit[node] = true; + + for (auto& edge : adj[node]) { + res = min(res, edge.second); + dfs(edge.first); + } + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number[][]} roads + * @return {number} + */ + minScore(n, roads) { + const adj = Array.from({ length: n + 1 }, () => []); + for (const [src, dst, dist] of roads) { + adj[src].push([dst, dist]); + adj[dst].push([src, dist]); + } + + let res = Infinity; + const visit = new Array(n + 1).fill(false); + + const dfs = (node) => { + if (visit[node]) return; + visit[node] = true; + + for (const [nei, dist] of adj[node]) { + res = Math.min(res, dist); + dfs(nei); + } + }; + + dfs(1); + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(V + E)$ +* Space complexity: $O(V + E)$ + +> Where $V$ is the number of vertices and $E$ is the number of edges. + +--- + +## 2. Breadth First Search + +::tabs-start + +```python +class Solution: + def minScore(self, n: int, roads: list[list[int]]) -> int: + adj = [[] for _ in range(n + 1)] + for src, dst, dist in roads: + adj[src].append((dst, dist)) + adj[dst].append((src, dist)) + + res = float("inf") + visit = [False] * (n + 1) + q = deque([1]) + visit[1] = True + + while q: + node = q.popleft() + for nei, dist in adj[node]: + res = min(res, dist) + if not visit[nei]: + visit[nei] = True + q.append(nei) + + return res +``` + +```java +public class Solution { + public int minScore(int n, int[][] roads) { + List[] adj = new ArrayList[n + 1]; + for (int i = 0; i <= n; i++) adj[i] = new ArrayList<>(); + + for (int[] road : roads) { + adj[road[0]].add(new int[]{road[1], road[2]}); + adj[road[1]].add(new int[]{road[0], road[2]}); + } + + int res = Integer.MAX_VALUE; + boolean[] visit = new boolean[n + 1]; + Queue q = new LinkedList<>(); + q.offer(1); + visit[1] = true; + + while (!q.isEmpty()) { + int node = q.poll(); + for (int[] edge : adj[node]) { + int nei = edge[0], dist = edge[1]; + res = Math.min(res, dist); + if (!visit[nei]) { + visit[nei] = true; + q.offer(nei); + } + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int minScore(int n, vector>& roads) { + vector>> adj(n + 1); + for (auto& road : roads) { + adj[road[0]].emplace_back(road[1], road[2]); + adj[road[1]].emplace_back(road[0], road[2]); + } + + int res = INT_MAX; + vector visit(n + 1, false); + queue q; + q.push(1); + visit[1] = true; + + while (!q.empty()) { + int node = q.front();q.pop(); + for (auto& [nei, dist] : adj[node]) { + res = min(res, dist); + if (!visit[nei]) { + visit[nei] = true; + q.push(nei); + } + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number[][]} roads + * @return {number} + */ + minScore(n, roads) { + const adj = Array.from({ length: n + 1 }, () => []); + for (const [src, dst, dist] of roads) { + adj[src].push([dst, dist]); + adj[dst].push([src, dist]); + } + + let res = Infinity; + const visit = new Array(n + 1).fill(false); + const q = new Queue([1]); + visit[1] = true; + + while (!q.isEmpty()) { + const node = q.pop(); + for (const [nei, dist] of adj[node]) { + res = Math.min(res, dist); + if (!visit[nei]) { + visit[nei] = true; + q.push(nei); + } + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(V + E)$ +* Space complexity: $O(V + E)$ + +> Where $V$ is the number of vertices and $E$ is the number of edges. + +--- + +## 3. Iterative DFS + +::tabs-start + +```python +class Solution: + def minScore(self, n: int, roads: list[list[int]]) -> int: + adj = [[] for _ in range(n + 1)] + for src, dst, dist in roads: + adj[src].append((dst, dist)) + adj[dst].append((src, dist)) + + res = float("inf") + visit = [False] * (n + 1) + stack = [1] + visit[1] = True + + while stack: + node = stack.pop() + for nei, dist in adj[node]: + res = min(res, dist) + if not visit[nei]: + visit[nei] = True + stack.append(nei) + + return res +``` + +```java +public class Solution { + public int minScore(int n, int[][] roads) { + List[] adj = new ArrayList[n + 1]; + for (int i = 0; i <= n; i++) adj[i] = new ArrayList<>(); + + for (int[] road : roads) { + adj[road[0]].add(new int[]{road[1], road[2]}); + adj[road[1]].add(new int[]{road[0], road[2]}); + } + + int res = Integer.MAX_VALUE; + boolean[] visit = new boolean[n + 1]; + Stack stack = new Stack<>(); + stack.push(1); + visit[1] = true; + + while (!stack.isEmpty()) { + int node = stack.pop(); + for (int[] edge : adj[node]) { + int nei = edge[0], dist = edge[1]; + res = Math.min(res, dist); + if (!visit[nei]) { + visit[nei] = true; + stack.push(nei); + } + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int minScore(int n, vector>& roads) { + vector>> adj(n + 1); + for (auto& road : roads) { + adj[road[0]].emplace_back(road[1], road[2]); + adj[road[1]].emplace_back(road[0], road[2]); + } + + int res = INT_MAX; + vector visit(n + 1, false); + stack stk; + stk.push(1); + visit[1] = true; + + while (!stk.empty()) { + int node = stk.top();stk.pop(); + for (auto& [nei, dist] : adj[node]) { + res = min(res, dist); + if (!visit[nei]) { + visit[nei] = true; + stk.push(nei); + } + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number[][]} roads + * @return {number} + */ + minScore(n, roads) { + const adj = Array.from({ length: n + 1 }, () => []); + for (const [src, dst, dist] of roads) { + adj[src].push([dst, dist]); + adj[dst].push([src, dist]); + } + + let res = Infinity; + const visit = new Array(n + 1).fill(false); + const stack = [1]; + visit[1] = true; + + while (stack.length) { + const node = stack.pop(); + for (const [nei, dist] of adj[node]) { + res = Math.min(res, dist); + if (!visit[nei]) { + visit[nei] = true; + stack.push(nei); + } + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(V + E)$ +* Space complexity: $O(V + E)$ + +> Where $V$ is the number of vertices and $E$ is the number of edges. + +--- + +## 4. Disjoint Set Union + +::tabs-start + +```python +class DSU: + def __init__(self, n): + self.Parent = list(range(n + 1)) + self.Size = [1] * (n + 1) + + def find(self, node): + if self.Parent[node] != node: + self.Parent[node] = self.find(self.Parent[node]) + return self.Parent[node] + + def union(self, u, v): + pu, pv = self.find(u), self.find(v) + if pu == pv: + return False + if self.Size[pu] >= self.Size[pv]: + self.Size[pu] += self.Size[pv] + self.Parent[pv] = pu + else: + self.Size[pv] += self.Size[pu] + self.Parent[pu] = pv + return True + +class Solution: + def minScore(self, n: int, roads: list[list[int]]) -> int: + dsu = DSU(n) + for src, dst, _ in roads: + dsu.union(src, dst) + + res = float("inf") + root = dsu.find(1) + for src, dst, dist in roads: + if dsu.find(src) == root: + res = min(res, dist) + + return res +``` + +```java +class DSU { + int[] parent, size; + + public DSU(int n) { + parent = new int[n + 1]; + size = new int[n + 1]; + for (int i = 0; i <= n; i++) { + parent[i] = i; + size[i] = 1; + } + } + + public int find(int node) { + if (parent[node] != node) { + parent[node] = find(parent[node]); + } + return parent[node]; + } + + public boolean union(int u, int v) { + int pu = find(u), pv = find(v); + if (pu == pv) return false; + if (size[pu] >= size[pv]) { + size[pu] += size[pv]; + parent[pv] = pu; + } else { + size[pv] += size[pu]; + parent[pu] = pv; + } + return true; + } +} + +public class Solution { + public int minScore(int n, int[][] roads) { + DSU dsu = new DSU(n); + for (int[] road : roads) { + dsu.union(road[0], road[1]); + } + + int res = Integer.MAX_VALUE; + int root = dsu.find(1); + for (int[] road : roads) { + if (dsu.find(road[0]) == root) { + res = Math.min(res, road[2]); + } + } + + return res; + } +} +``` + +```cpp +class DSU { +public: + vector parent, size; + + DSU(int n) { + parent.resize(n + 1); + size.resize(n + 1, 1); + for (int i = 0; i <= n; i++) { + parent[i] = i; + } + } + + int find(int node) { + if (parent[node] != node) { + parent[node] = find(parent[node]); + } + return parent[node]; + } + + bool unionSets(int u, int v) { + int pu = find(u), pv = find(v); + if (pu == pv) return false; + if (size[pu] >= size[pv]) { + size[pu] += size[pv]; + parent[pv] = pu; + } else { + size[pv] += size[pu]; + parent[pu] = pv; + } + return true; + } +}; + +class Solution { +public: + int minScore(int n, vector>& roads) { + DSU dsu(n); + for (auto& road : roads) { + dsu.unionSets(road[0], road[1]); + } + + int res = INT_MAX; + int root = dsu.find(1); + for (auto& road : roads) { + if (dsu.find(road[0]) == root) { + res = min(res, road[2]); + } + } + + return res; + } +}; +``` + +```javascript +class DSU { + /** + * @constructor + * @param {number} n + */ + constructor(n) { + this.parent = Array.from({ length: n + 1 }, (_, i) => i); + this.size = new Array(n + 1).fill(1); + } + + /** + * @param {number} node + * @return {number} + */ + find(node) { + if (this.parent[node] !== node) { + this.parent[node] = this.find(this.parent[node]); + } + return this.parent[node]; + } + + /** + * @param {number} u + * @param {number} u=v + * @return {boolean} + */ + union(u, v) { + let pu = this.find(u), pv = this.find(v); + if (pu === pv) return false; + if (this.size[pu] >= this.size[pv]) { + this.size[pu] += this.size[pv]; + this.parent[pv] = pu; + } else { + this.size[pv] += this.size[pu]; + this.parent[pu] = pv; + } + return true; + } +} + +class Solution { + /** + * @param {number} n + * @param {number[][]} roads + * @return {number} + */ + minScore(n, roads) { + const dsu = new DSU(n); + for (const [src, dst] of roads) { + dsu.union(src, dst); + } + + let res = Infinity; + const root = dsu.find(1); + for (const [src, dst, dist] of roads) { + if (dsu.find(src) === root) { + res = Math.min(res, dist); + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(V + (E * α(V)))$ +* Space complexity: $O(V)$ + +> Where $V$ is the number of vertices and $E$ is the number of edges in the graph. $α()$ is used for amortized complexity. \ No newline at end of file diff --git a/articles/number-of-closed-islands.md b/articles/number-of-closed-islands.md new file mode 100644 index 000000000..d3ab68940 --- /dev/null +++ b/articles/number-of-closed-islands.md @@ -0,0 +1,836 @@ +## 1. Depth First Search - I + +::tabs-start + +```python +class Solution: + def closedIsland(self, grid: List[List[int]]) -> int: + ROWS, COLS = len(grid), len(grid[0]) + directions = [[0, 1], [0, -1], [1, 0], [-1, 0]] + visit = set() + + def dfs(r, c): + if r < 0 or c < 0 or r == ROWS or c == COLS: + return 0 # False + if grid[r][c] == 1 or (r, c) in visit: + return 1 # True + + visit.add((r, c)) + res = True + for dx, dy in directions: + if not dfs(r + dx, c + dy): + res = False + return res + + res = 0 + for r in range(ROWS): + for c in range(COLS): + if not grid[r][c] and (r, c) not in visit: + res += dfs(r, c) + + return res +``` + +```java +public class Solution { + private int[][] directions = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}}; + private boolean[][] visit; + private int ROWS, COLS; + + public int closedIsland(int[][] grid) { + ROWS = grid.length; + COLS = grid[0].length; + visit = new boolean[ROWS][COLS]; + + int res = 0; + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + if (grid[r][c] == 0 && !visit[r][c]) { + if (dfs(grid, r, c)) { + res++; + } + } + } + } + return res; + } + + private boolean dfs(int[][] grid, int r, int c) { + if (r < 0 || c < 0 || r == ROWS || c == COLS) { + return false; + } + if (grid[r][c] == 1 || visit[r][c]) { + return true; + } + + visit[r][c] = true; + boolean res = true; + for (int[] d : directions) { + if (!dfs(grid, r + d[0], c + d[1])) { + res = false; + } + } + return res; + } +} +``` + +```cpp +class Solution { + int ROWS, COLS; + vector> directions; + vector> visit; + +public: + int closedIsland(vector>& grid) { + ROWS = grid.size(); + COLS = grid[0].size(); + directions = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}}; + visit.assign(ROWS, vector(COLS, false)); + + int res = 0; + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + if (grid[r][c] == 0 && !visit[r][c]) { + if (dfs(grid, r, c)) { + res++; + } + } + } + } + return res; + } + +private: + bool dfs(vector>& grid, int r, int c) { + if (r < 0 || c < 0 || r == ROWS || c == COLS) { + return false; + } + if (grid[r][c] == 1 || visit[r][c]) { + return true; + } + + visit[r][c] = true; + bool res = true; + for (auto& d : directions) { + if (!dfs(grid, r + d[0], c + d[1])) { + res = false; + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} grid + * @return {number} + */ + closedIsland(grid) { + const ROWS = grid.length, COLS = grid[0].length; + const directions = [0, 1, 0, -1, 0]; + const visit = Array.from({ length: ROWS }, () => Array(COLS).fill(false)); + + const dfs = (r, c) => { + if (r < 0 || c < 0 || r === ROWS || c === COLS) return false; + if (grid[r][c] === 1 || visit[r][c]) return true; + + visit[r][c] = true; + let res = true; + for (let d = 0; d < 4; d++) { + if (!dfs(r + directions[d], c + directions[d + 1])) { + res = false; + } + } + return res; + }; + + let res = 0; + for (let r = 0; r < ROWS; r++) { + for (let c = 0; c < COLS; c++) { + if (grid[r][c] === 0 && !visit[r][c]) { + if (dfs(r, c)) res++; + } + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n)$ +* Space complexity: $O(m * n)$ + +> Where $m$ is the number of rows and $n$ is the number of columns in the given grid. + +--- + +## 2. Depth First Search - II + +::tabs-start + +```python +class Solution: + def closedIsland(self, grid: list[list[int]]) -> int: + ROWS, COLS = len(grid), len(grid[0]) + directions = [0, 1, 0, -1, 0] + + def dfs(r, c): + if r < 0 or c < 0 or r == ROWS or c == COLS or grid[r][c] == 1: + return + grid[r][c] = 1 + for d in range(4): + dfs(r + directions[d], c + directions[d + 1]) + + for r in range(ROWS): + dfs(r, 0) + dfs(r, COLS - 1) + for c in range(COLS): + dfs(0, c) + dfs(ROWS - 1, c) + + res = 0 + for r in range(1, ROWS - 1): + for c in range(1, COLS - 1): + if grid[r][c] == 0: + dfs(r, c) + res += 1 + return res +``` + +```java +public class Solution { + private int ROWS, COLS; + private int[] directions = {0, 1, 0, -1, 0}; + + public int closedIsland(int[][] grid) { + ROWS = grid.length; + COLS = grid[0].length; + + for (int r = 0; r < ROWS; r++) { + dfs(grid, r, 0); + dfs(grid, r, COLS - 1); + } + for (int c = 0; c < COLS; c++) { + dfs(grid, 0, c); + dfs(grid, ROWS - 1, c); + } + + int res = 0; + for (int r = 1; r < ROWS - 1; r++) { + for (int c = 1; c < COLS - 1; c++) { + if (grid[r][c] == 0) { + dfs(grid, r, c); + res++; + } + } + } + return res; + } + + private void dfs(int[][] grid, int r, int c) { + if (r < 0 || c < 0 || r >= ROWS || c >= COLS || grid[r][c] == 1) { + return; + } + grid[r][c] = 1; + for (int d = 0; d < 4; d++) { + dfs(grid, r + directions[d], c + directions[d + 1]); + } + } +} +``` + +```cpp +class Solution { + const int directions[5] = {0, 1, 0, -1, 0}; + int ROWS, COLS; + +public: + int closedIsland(vector>& grid) { + ROWS = grid.size(); + COLS = grid[0].size(); + + for (int r = 0; r < ROWS; r++) { + dfs(grid, r, 0); + dfs(grid, r, COLS - 1); + } + for (int c = 0; c < COLS; c++) { + dfs(grid, 0, c); + dfs(grid, ROWS - 1, c); + } + + int res = 0; + for (int r = 1; r < ROWS - 1; r++) { + for (int c = 1; c < COLS - 1; c++) { + if (grid[r][c] == 0) { + dfs(grid, r, c); + res++; + } + } + } + return res; + } + +private: + void dfs(vector>& grid, int r, int c) { + if (r < 0 || c < 0 || r >= ROWS || c >= COLS || grid[r][c] == 1) { + return; + } + grid[r][c] = 1; + for (int d = 0; d < 4; d++) { + dfs(grid, r + directions[d], c + directions[d + 1]); + } + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} grid + * @return {number} + */ + closedIsland(grid) { + const ROWS = grid.length, COLS = grid[0].length; + const directions = [0, 1, 0, -1, 0]; + + const dfs = (r, c) => { + if (r < 0 || c < 0 || r === ROWS || c === COLS || grid[r][c] === 1) return; + grid[r][c] = 1; + for (let d = 0; d < 4; d++) { + dfs(r + directions[d], c + directions[d + 1]); + } + }; + + for (let r = 0; r < ROWS; r++) { + dfs(r, 0); + dfs(r, COLS - 1); + } + for (let c = 0; c < COLS; c++) { + dfs(0, c); + dfs(ROWS - 1, c); + } + + let res = 0; + for (let r = 1; r < ROWS - 1; r++) { + for (let c = 1; c < COLS - 1; c++) { + if (grid[r][c] === 0) { + dfs(r, c); + res++; + } + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n)$ +* Space complexity: $O(m * n)$ + +> Where $m$ is the number of rows and $n$ is the number of columns in the given grid. + +--- + +## 3. Breadth First Search + +::tabs-start + +```python +class Solution: + def closedIsland(self, grid: list[list[int]]) -> int: + ROWS, COLS = len(grid), len(grid[0]) + directions = [[0, 1], [0, -1], [1, 0], [-1, 0]] + visit = set() + res = 0 + + def bfs(r, c): + q = deque([(r, c)]) + visit.add((r, c)) + is_closed = True + + while q: + x, y = q.popleft() + for dx, dy in directions: + nx, ny = x + dx, y + dy + if nx < 0 or ny < 0 or nx >= ROWS or ny >= COLS: + is_closed = False + continue + if grid[nx][ny] == 1 or (nx, ny) in visit: + continue + visit.add((nx, ny)) + q.append((nx, ny)) + + return is_closed + + for r in range(ROWS): + for c in range(COLS): + if grid[r][c] == 0 and (r, c) not in visit: + res += bfs(r, c) + + return res +``` + +```java +public class Solution { + private int[][] directions = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}}; + private int ROWS, COLS; + private boolean[][] visit; + + public int closedIsland(int[][] grid) { + ROWS = grid.length; + COLS = grid[0].length; + visit = new boolean[ROWS][COLS]; + int res = 0; + + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + if (grid[r][c] == 0 && !visit[r][c]) { + if (bfs(grid, r, c)) res++; + } + } + } + return res; + } + + private boolean bfs(int[][] grid, int r, int c) { + Queue q = new LinkedList<>(); + q.offer(new int[]{r, c}); + visit[r][c] = true; + boolean isClosed = true; + + while (!q.isEmpty()) { + int[] cell = q.poll(); + int x = cell[0], y = cell[1]; + + for (int[] d : directions) { + int nx = x + d[0], ny = y + d[1]; + if (nx < 0 || ny < 0 || nx >= ROWS || ny >= COLS) { + isClosed = false; + continue; + } + if (grid[nx][ny] == 1 || visit[nx][ny]) continue; + visit[nx][ny] = true; + q.offer(new int[]{nx, ny}); + } + } + return isClosed; + } +} +``` + +```cpp +class Solution { + int directions[4][2] = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}}; + int ROWS, COLS; + vector> visit; + +public: + int closedIsland(vector>& grid) { + ROWS = grid.size(); + COLS = grid[0].size(); + visit.assign(ROWS, vector(COLS, false)); + int res = 0; + + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + if (grid[r][c] == 0 && !visit[r][c]) { + if (bfs(grid, r, c)) res++; + } + } + } + return res; + } + +private: + bool bfs(vector>& grid, int r, int c) { + queue> q; + q.push({r, c}); + visit[r][c] = true; + bool isClosed = true; + + while (!q.empty()) { + auto [x, y] = q.front();q.pop(); + for (auto& d : directions) { + int nx = x + d[0], ny = y + d[1]; + if (nx < 0 || ny < 0 || nx >= ROWS || ny >= COLS) { + isClosed = false; + continue; + } + if (grid[nx][ny] == 1 || visit[nx][ny]) continue; + visit[nx][ny] = true; + q.push({nx, ny}); + } + } + return isClosed; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} grid + * @return {number} + */ + closedIsland(grid) { + const ROWS = grid.length, COLS = grid[0].length; + const directions = [[0, 1], [0, -1], [1, 0], [-1, 0]]; + const visit = Array.from({ length: ROWS }, () => Array(COLS).fill(false)); + let res = 0; + + const bfs = (r, c) => { + const q = new Queue([[r, c]]); + visit[r][c] = true; + let isClosed = true; + + while (!q.isEmpty()) { + const [x, y] = q.pop(); + for (const [dx, dy] of directions) { + const nx = x + dx, ny = y + dy; + if (nx < 0 || ny < 0 || nx >= ROWS || ny >= COLS) { + isClosed = false; + continue; + } + if (grid[nx][ny] === 1 || visit[nx][ny]) continue; + visit[nx][ny] = true; + q.push([nx, ny]); + } + } + return isClosed; + }; + + for (let r = 0; r < ROWS; r++) { + for (let c = 0; c < COLS; c++) { + if (grid[r][c] === 0 && !visit[r][c]) { + if (bfs(r, c)) res++; + } + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n)$ +* Space complexity: $O(m * n)$ + +> Where $m$ is the number of rows and $n$ is the number of columns in the given grid. + +--- + +## 4. Disjoint Set Union + +::tabs-start + +```python +class DSU: + def __init__(self, n): + self.Parent = list(range(n + 1)) + self.Size = [1] * (n + 1) + + def find(self, node): + if self.Parent[node] != node: + self.Parent[node] = self.find(self.Parent[node]) + return self.Parent[node] + + def union(self, u, v): + pu, pv = self.find(u), self.find(v) + if pu == pv: + return False + if self.Size[pu] >= self.Size[pv]: + self.Size[pu] += self.Size[pv] + self.Parent[pv] = pu + else: + self.Size[pv] += self.Size[pu] + self.Parent[pu] = pv + return True + +class Solution: + def closedIsland(self, grid: List[List[int]]) -> int: + ROWS, COLS = len(grid), len(grid[0]) + N = ROWS * COLS + def index(r, c): + return r * COLS + c + + dsu = DSU(N) + directions = [0, 1, 0, -1, 0] + for r in range(ROWS): + for c in range(COLS): + if grid[r][c] == 0: + for d in range(4): + nr, nc = r + directions[d], c + directions[d + 1] + if min(nr, nc) < 0 or nr == ROWS or nc == COLS: + dsu.union(N, index(r, c)) + elif grid[nr][nc] == 0: + dsu.union(index(r, c), index(nr, nc)) + + res = 0 + rootN = dsu.find(N) + for r in range(ROWS): + for c in range(COLS): + if grid[r][c] == 1: + continue + node = index(r, c) + root = dsu.find(node) + if root == node and root != rootN: + res += 1 + return res +``` + +```java +class DSU { + int[] Parent, Size; + + public DSU(int n) { + Parent = new int[n + 1]; + Size = new int[n + 1]; + for (int i = 0; i <= n; i++) { + Parent[i] = i; + Size[i] = 1; + } + } + + public int find(int node) { + if (Parent[node] != node) { + Parent[node] = find(Parent[node]); + } + return Parent[node]; + } + + public boolean union(int u, int v) { + int pu = find(u), pv = find(v); + if (pu == pv) return false; + if (Size[pu] >= Size[pv]) { + Size[pu] += Size[pv]; + Parent[pv] = pu; + } else { + Size[pv] += Size[pu]; + Parent[pu] = pv; + } + return true; + } +} + +public class Solution { + public int closedIsland(int[][] grid) { + int ROWS = grid.length, COLS = grid[0].length; + int N = ROWS * COLS; + + DSU dsu = new DSU(N); + int[] directions = {0, 1, 0, -1, 0}; + + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + if (grid[r][c] == 0) { + for (int d = 0; d < 4; d++) { + int nr = r + directions[d], nc = c + directions[d + 1]; + if (nr < 0 || nc < 0 || nr == ROWS || nc == COLS) { + dsu.union(N, r * COLS + c); + } else if (grid[nr][nc] == 0) { + dsu.union(r * COLS + c, nr * COLS + nc); + } + } + } + } + } + + int res = 0, rootN = dsu.find(N); + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + if (grid[r][c] == 0) { + int node = r * COLS + c; + int root = dsu.find(node); + if (root == node && root != rootN) { + res++; + } + } + } + } + return res; + } +} +``` + +```cpp +class DSU { +public: + vector Parent, Size; + + DSU(int n) { + Parent.resize(n + 1); + Size.resize(n + 1, 1); + for (int i = 0; i <= n; i++) { + Parent[i] = i; + } + } + + int find(int node) { + if (Parent[node] != node) { + Parent[node] = find(Parent[node]); + } + return Parent[node]; + } + + bool unionSets(int u, int v) { + int pu = find(u), pv = find(v); + if (pu == pv) return false; + if (Size[pu] >= Size[pv]) { + Size[pu] += Size[pv]; + Parent[pv] = pu; + } else { + Size[pv] += Size[pu]; + Parent[pu] = pv; + } + return true; + } +}; + +class Solution { +public: + int closedIsland(vector>& grid) { + int ROWS = grid.size(), COLS = grid[0].size(); + int N = ROWS * COLS; + + DSU dsu(N); + int directions[5] = {0, 1, 0, -1, 0}; + + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + if (grid[r][c] == 0) { + for (int d = 0; d < 4; d++) { + int nr = r + directions[d], nc = c + directions[d + 1]; + if (nr < 0 || nc < 0 || nr == ROWS || nc == COLS) { + dsu.unionSets(N, r * COLS + c); + } else if (grid[nr][nc] == 0) { + dsu.unionSets(r * COLS + c, nr * COLS + nc); + } + } + } + } + } + + int res = 0, rootN = dsu.find(N); + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + if (grid[r][c] == 0) { + int node = r * COLS + c; + int root = dsu.find(node); + if (root == node && root != rootN) { + res++; + } + } + } + } + return res; + } +}; +``` + +```javascript +class DSU { + /** + * @constructor + * @param {number} n + */ + constructor(n) { + this.parent = Array.from({ length: n + 1 }, (_, i) => i); + this.size = new Array(n + 1).fill(1); + } + + /** + * @param {number} node + * @return {number} + */ + find(node) { + if (this.parent[node] !== node) { + this.parent[node] = this.find(this.parent[node]); + } + return this.parent[node]; + } + + /** + * @param {number} u + * @param {number} u=v + * @return {boolean} + */ + union(u, v) { + let pu = this.find(u), pv = this.find(v); + if (pu === pv) return false; + if (this.size[pu] >= this.size[pv]) { + this.size[pu] += this.size[pv]; + this.parent[pv] = pu; + } else { + this.size[pv] += this.size[pu]; + this.parent[pu] = pv; + } + return true; + } +} + +class Solution { + /** + * @param {number[][]} grid + * @return {number} + */ + closedIsland(grid) { + const ROWS = grid.length, COLS = grid[0].length; + const N = ROWS * COLS; + + const dsu = new DSU(N); + const directions = [0, 1, 0, -1, 0]; + + for (let r = 0; r < ROWS; r++) { + for (let c = 0; c < COLS; c++) { + if (grid[r][c] === 0) { + for (let d = 0; d < 4; d++) { + let nr = r + directions[d], nc = c + directions[d + 1]; + if (nr < 0 || nc < 0 || nr === ROWS || nc === COLS) { + dsu.union(N, r * COLS + c); + } else if (grid[nr][nc] === 0) { + dsu.union(r * COLS + c, nr * COLS + nc); + } + } + } + } + } + + let res = 0, rootN = dsu.find(N); + for (let r = 0; r < ROWS; r++) { + for (let c = 0; c < COLS; c++) { + if (grid[r][c] === 0) { + let node = r * COLS + c; + let root = dsu.find(node); + if (root === node && root !== rootN) { + res++; + } + } + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n)$ +* Space complexity: $O(m * n)$ + +> Where $m$ is the number of rows and $n$ is the number of columns in the given grid. \ No newline at end of file diff --git a/articles/number-of-enclaves.md b/articles/number-of-enclaves.md new file mode 100644 index 000000000..a5da06e58 --- /dev/null +++ b/articles/number-of-enclaves.md @@ -0,0 +1,607 @@ +## 1. Depth First Search + +::tabs-start + +```python +class Solution: + def numEnclaves(self, grid: List[List[int]]) -> int: + ROWS, COLS = len(grid), len(grid[0]) + direct = [[0, 1], [0, -1], [1, 0], [-1, 0]] + + # Return num of land cells + def dfs(r, c): + if (r < 0 or c < 0 or + r == ROWS or c == COLS or + not grid[r][c] or (r, c) in visit): + return 0 + visit.add((r, c)) + res = 1 + for dr, dc in direct: + res += dfs(r + dr, c + dc) + return res + + visit = set() + land, borderLand = 0, 0 + for r in range(ROWS): + for c in range(COLS): + land += grid[r][c] + if (grid[r][c] and (r, c) not in visit and + (c in [0, COLS - 1] or r in [0, ROWS - 1])): + borderLand += dfs(r, c) + + return land - borderLand +``` + +```java +public class Solution { + private int ROWS, COLS; + private boolean[][] visit; + private int[][] direct = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}}; + + public int numEnclaves(int[][] grid) { + this.ROWS = grid.length; + this.COLS = grid[0].length; + this.visit = new boolean[ROWS][COLS]; + + int land = 0, borderLand = 0; + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + land += grid[r][c]; + if (grid[r][c] == 1 && !visit[r][c] && + (r == 0 || r == ROWS - 1 || c == 0 || c == COLS - 1)) { + borderLand += dfs(r, c, grid); + } + } + } + return land - borderLand; + } + + private int dfs(int r, int c, int[][] grid) { + if (r < 0 || c < 0 || r == ROWS || c == COLS || + grid[r][c] == 0 || visit[r][c]) { + return 0; + } + visit[r][c] = true; + int res = 1; + for (int[] d : direct) { + res += dfs(r + d[0], c + d[1], grid); + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int ROWS, COLS; + vector> visit; + vector> direct = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}}; + + int numEnclaves(vector>& grid) { + this->ROWS = grid.size(); + this->COLS = grid[0].size(); + this->visit = vector>(ROWS, vector(COLS, false)); + + int land = 0, borderLand = 0; + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + land += grid[r][c]; + if (grid[r][c] == 1 && !visit[r][c] && + (r == 0 || r == ROWS - 1 || c == 0 || c == COLS - 1)) { + borderLand += dfs(r, c, grid); + } + } + } + return land - borderLand; + } + +private: + int dfs(int r, int c, vector>& grid) { + if (r < 0 || c < 0 || r == ROWS || c == COLS || + grid[r][c] == 0 || visit[r][c]) { + return 0; + } + visit[r][c] = true; + int res = 1; + for (auto& d : direct) { + res += dfs(r + d[0], c + d[1], grid); + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} grid + * @return {number} + */ + numEnclaves(grid) { + const ROWS = grid.length, COLS = grid[0].length; + const visit = Array.from({ length: ROWS }, () => + Array(COLS).fill(false) + ); + const direct = [0, 1, 0, -1, 0]; + + const dfs = (r, c) => { + if (r < 0 || c < 0 || r === ROWS || c === COLS || + grid[r][c] === 0 || visit[r][c]) { + return 0; + } + visit[r][c] = true; + let res = 1; + for (let d = 0; d < 4; d++) { + res += dfs(r + direct[d], c + direct[d + 1]); + } + return res; + }; + + let land = 0, borderLand = 0; + for (let r = 0; r < ROWS; r++) { + for (let c = 0; c < COLS; c++) { + land += grid[r][c]; + if (grid[r][c] === 1 && !visit[r][c] && + (r === 0 || r === ROWS - 1 || c === 0 || c === COLS - 1)) { + borderLand += dfs(r, c); + } + } + } + return land - borderLand; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n)$ +* Space complexity: $O(m * n)$ + +> Where $m$ is the number of rows and $n$ is the number of columns in the given grid. + +--- + +## 2. Breadth First Search + +::tabs-start + +```python +class Solution: + def numEnclaves(self, grid: list[list[int]]) -> int: + ROWS, COLS = len(grid), len(grid[0]) + direct = [[0, 1], [0, -1], [1, 0], [-1, 0]] + visit = [[False] * COLS for _ in range(ROWS)] + q = deque() + + land, borderLand = 0, 0 + for r in range(ROWS): + for c in range(COLS): + land += grid[r][c] + if (grid[r][c] == 1 and + (r in [0, ROWS - 1] or c in [0, COLS - 1]) + ): + q.append((r, c)) + visit[r][c] = True + + while q: + r, c = q.popleft() + borderLand += 1 + for dr, dc in direct: + nr, nc = r + dr, c + dc + if (0 <= nr < ROWS and 0 <= nc < COLS and + grid[nr][nc] == 1 and not visit[nr][nc] + ): + q.append((nr, nc)) + visit[nr][nc] = True + + return land - borderLand +``` + +```java +public class Solution { + public int numEnclaves(int[][] grid) { + int ROWS = grid.length, COLS = grid[0].length; + int[][] direct = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}}; + boolean[][] visit = new boolean[ROWS][COLS]; + Queue q = new LinkedList<>(); + + int land = 0, borderLand = 0; + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + land += grid[r][c]; + if (grid[r][c] == 1 && (r == 0 || r == ROWS - 1 || + c == 0 || c == COLS - 1)) { + q.offer(new int[]{r, c}); + visit[r][c] = true; + } + } + } + + while (!q.isEmpty()) { + int[] cell = q.poll(); + int r = cell[0], c = cell[1]; + borderLand++; + + for (int[] d : direct) { + int nr = r + d[0], nc = c + d[1]; + if (nr >= 0 && nc >= 0 && nr < ROWS && nc < COLS && + grid[nr][nc] == 1 && !visit[nr][nc]) { + q.offer(new int[]{nr, nc}); + visit[nr][nc] = true; + } + } + } + + return land - borderLand; + } +} +``` + +```cpp +class Solution { +public: + int ROWS, COLS; + vector> visit; + vector> direct = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}}; + + int numEnclaves(vector>& grid) { + this->ROWS = grid.size(); + this->COLS = grid[0].size(); + this->visit = vector>(ROWS, vector(COLS, false)); + + int land = 0, borderLand = 0; + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + land += grid[r][c]; + if (grid[r][c] == 1 && !visit[r][c] && + (r == 0 || r == ROWS - 1 || c == 0 || c == COLS - 1)) { + borderLand += dfs(r, c, grid); + } + } + } + return land - borderLand; + } + +private: + int dfs(int r, int c, vector>& grid) { + if (r < 0 || c < 0 || r == ROWS || c == COLS || + grid[r][c] == 0 || visit[r][c]) { + return 0; + } + visit[r][c] = true; + int res = 1; + for (auto& d : direct) { + res += dfs(r + d[0], c + d[1], grid); + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} grid + * @return {number} + */ + numEnclaves(grid) { + const ROWS = grid.length, COLS = grid[0].length; + const direct = [0, 1, 0, -1, 0]; + const visit = Array.from({ length: ROWS }, () => + Array(COLS).fill(false) + ); + const q = new Queue(); + + let land = 0, borderLand = 0; + for (let r = 0; r < ROWS; r++) { + for (let c = 0; c < COLS; c++) { + land += grid[r][c]; + if (grid[r][c] === 1 && (r === 0 || r === ROWS - 1 || + c === 0 || c === COLS - 1)) { + q.push([r, c]); + visit[r][c] = true; + } + } + } + + while (!q.isEmpty()) { + let [r, c] = q.pop(); + borderLand++; + for (let d = 0; d < 4; d++) { + let nr = r + direct[d], nc = c + direct[d + 1]; + if (nr >= 0 && nc >= 0 && nr < ROWS && nc < COLS && + grid[nr][nc] === 1 && !visit[nr][nc]) { + q.push([nr, nc]); + visit[nr][nc] = true; + } + } + } + + return land - borderLand; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n)$ +* Space complexity: $O(m * n)$ + +> Where $m$ is the number of rows and $n$ is the number of columns in the given grid. + +--- + +## 3. Disjoint Set Union + +::tabs-start + +```python +class DSU: + def __init__(self, n): + self.Parent = list(range(n + 1)) + self.Size = [1] * (n + 1) + + def find(self, node): + if self.Parent[node] != node: + self.Parent[node] = self.find(self.Parent[node]) + return self.Parent[node] + + def union(self, u, v): + pu, pv = self.find(u), self.find(v) + if pu == pv: + return False + if self.Size[pu] >= self.Size[pv]: + self.Size[pu] += self.Size[pv] + self.Parent[pv] = pu + else: + self.Size[pv] += self.Size[pu] + self.Parent[pu] = pv + return True + +class Solution: + def numEnclaves(self, grid: list[list[int]]) -> int: + ROWS, COLS = len(grid), len(grid[0]) + N = ROWS * COLS + def index(r, c): + return r * COLS + c + + dsu = DSU(N) + directions = [0, 1, 0, -1, 0] + land = 0 + for r in range(ROWS): + for c in range(COLS): + if grid[r][c] == 0: + continue + land += 1 + for d in range(4): + nr, nc = r + directions[d], c + directions[d + 1] + if 0 <= nr < ROWS and 0 <= nc < COLS: + if grid[nr][nc] == 1: + dsu.union(index(r, c), index(nr, nc)) + else: + dsu.union(N, index(r, c)) + + borderLand = dsu.Size[dsu.find(N)] + return land - borderLand + 1 +``` + +```java +class DSU { + int[] Parent, Size; + + public DSU(int n) { + Parent = new int[n + 1]; + Size = new int[n + 1]; + for (int i = 0; i <= n; i++) { + Parent[i] = i; + Size[i] = 1; + } + } + + public int find(int node) { + if (Parent[node] != node) { + Parent[node] = find(Parent[node]); + } + return Parent[node]; + } + + public boolean union(int u, int v) { + int pu = find(u), pv = find(v); + if (pu == pv) return false; + if (Size[pu] >= Size[pv]) { + Size[pu] += Size[pv]; + Parent[pv] = pu; + } else { + Size[pv] += Size[pu]; + Parent[pu] = pv; + } + return true; + } +} + +public class Solution { + public int numEnclaves(int[][] grid) { + int ROWS = grid.length, COLS = grid[0].length; + int N = ROWS * COLS; + DSU dsu = new DSU(N); + int[] directions = {0, 1, 0, -1, 0}; + int land = 0; + + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + if (grid[r][c] == 0) continue; + land++; + for (int d = 0; d < 4; d++) { + int nr = r + directions[d], nc = c + directions[d + 1]; + if (nr >= 0 && nc >= 0 && nr < ROWS && nc < COLS) { + if (grid[nr][nc] == 1) { + dsu.union(r * COLS + c, nr * COLS + nc); + } + } else { + dsu.union(N, r * COLS + c); + } + } + } + } + + int borderLand = dsu.Size[dsu.find(N)]; + return land - borderLand + 1; + } +} +``` + +```cpp +class DSU { +public: + vector Parent, Size; + + DSU(int n) { + Parent.resize(n + 1); + Size.resize(n + 1, 1); + for (int i = 0; i <= n; i++) { + Parent[i] = i; + } + } + + int find(int node) { + if (Parent[node] != node) { + Parent[node] = find(Parent[node]); + } + return Parent[node]; + } + + bool unionSets(int u, int v) { + int pu = find(u), pv = find(v); + if (pu == pv) return false; + if (Size[pu] >= Size[pv]) { + Size[pu] += Size[pv]; + Parent[pv] = pu; + } else { + Size[pv] += Size[pu]; + Parent[pu] = pv; + } + return true; + } +}; + +class Solution { +public: + int numEnclaves(vector>& grid) { + int ROWS = grid.size(), COLS = grid[0].size(); + int N = ROWS * COLS; + DSU dsu(N); + vector directions = {0, 1, 0, -1, 0}; + int land = 0; + + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + if (grid[r][c] == 0) continue; + land++; + for (int d = 0; d < 4; d++) { + int nr = r + directions[d], nc = c + directions[d + 1]; + if (nr >= 0 && nc >= 0 && nr < ROWS && nc < COLS) { + if (grid[nr][nc] == 1) { + dsu.unionSets(r * COLS + c, nr * COLS + nc); + } + } else { + dsu.unionSets(N, r * COLS + c); + } + } + } + } + + int borderLand = dsu.Size[dsu.find(N)]; + return land - borderLand + 1; + } +}; +``` + +```javascript +class DSU { + /** + * @constructor + * @param {number} n + */ + constructor(n) { + this.parent = Array.from({ length: n + 1 }, (_, i) => i); + this.size = new Array(n + 1).fill(1); + } + + /** + * @param {number} node + * @return {number} + */ + find(node) { + if (this.parent[node] !== node) { + this.parent[node] = this.find(this.parent[node]); + } + return this.parent[node]; + } + + /** + * @param {number} u + * @param {number} u=v + * @return {boolean} + */ + union(u, v) { + let pu = this.find(u), pv = this.find(v); + if (pu === pv) return false; + if (this.size[pu] >= this.size[pv]) { + this.size[pu] += this.size[pv]; + this.parent[pv] = pu; + } else { + this.size[pv] += this.size[pu]; + this.parent[pu] = pv; + } + return true; + } +} + +class Solution { + /** + * @param {number[][]} grid + * @return {number} + */ + numEnclaves(grid) { + const ROWS = grid.length, COLS = grid[0].length; + const N = ROWS * COLS; + const dsu = new DSU(N); + const directions = [0, 1, 0, -1, 0]; + let land = 0; + + for (let r = 0; r < ROWS; r++) { + for (let c = 0; c < COLS; c++) { + if (grid[r][c] === 0) continue; + land++; + for (let d = 0; d < 4; d++) { + let nr = r + directions[d], nc = c + directions[d + 1]; + if (nr >= 0 && nc >= 0 && nr < ROWS && nc < COLS) { + if (grid[nr][nc] === 1) { + dsu.union(r * COLS + c, nr * COLS + nc); + } + } else { + dsu.union(N, r * COLS + c); + } + } + } + } + + const borderLand = dsu.size[dsu.find(N)]; + return land - borderLand + 1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n)$ +* Space complexity: $O(m * n)$ + +> Where $m$ is the number of rows and $n$ is the number of columns in the given grid. \ No newline at end of file diff --git a/articles/pseudo-palindromic-paths-in-a-binary-tree.md b/articles/pseudo-palindromic-paths-in-a-binary-tree.md new file mode 100644 index 000000000..96bf58f3b --- /dev/null +++ b/articles/pseudo-palindromic-paths-in-a-binary-tree.md @@ -0,0 +1,806 @@ +## 1. Depth First Search + +::tabs-start + +```python +# 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 +class Solution: + def pseudoPalindromicPaths(self, root: Optional[TreeNode]) -> int: + count = defaultdict(int) + odd = 0 + + def dfs(cur): + nonlocal odd + if not cur: + return 0 + + count[cur.val] += 1 + odd_change = 1 if count[cur.val] % 2 == 1 else -1 + odd += odd_change + + if not cur.left and not cur.right: + res = 1 if odd <= 1 else 0 + else: + res = dfs(cur.left) + dfs(cur.right) + + odd -= odd_change + count[cur.val] -= 1 + return res + + return dfs(root) +``` + +```java +/** + * 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; + * } + * } + */ +public class Solution { + public int pseudoPalindromicPaths(TreeNode root) { + Map count = new HashMap<>(); + int[] odd = new int[1]; + + return dfs(root, count, odd); + } + + private int dfs(TreeNode cur, Map count, int[] odd) { + if (cur == null) return 0; + + count.put(cur.val, count.getOrDefault(cur.val, 0) + 1); + int odd_change = (count.get(cur.val) % 2 == 1) ? 1 : -1; + odd[0] += odd_change; + + int res; + if (cur.left == null && cur.right == null) { + res = (odd[0] <= 1) ? 1 : 0; + } else { + res = dfs(cur.left, count, odd) + dfs(cur.right, count, odd); + } + + odd[0] -= odd_change; + count.put(cur.val, count.get(cur.val) - 1); + return res; + } +} +``` + +```cpp +/** + * 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: + int pseudoPalindromicPaths(TreeNode* root) { + unordered_map count; + int odd = 0; + return dfs(root, count, odd); + } + +private: + int dfs(TreeNode* cur, unordered_map& count, int& odd) { + if (!cur) return 0; + + count[cur->val]++; + int odd_change = (count[cur->val] % 2 == 1) ? 1 : -1; + odd += odd_change; + + int res; + if (!cur->left && !cur->right) { + res = (odd <= 1) ? 1 : 0; + } else { + res = dfs(cur->left, count, odd) + dfs(cur->right, count, odd); + } + + odd -= odd_change; + count[cur->val]--; + return res; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {number} + */ + pseudoPalindromicPaths(root) { + const count = new Map(); + let odd = 0; + + const dfs = (cur) => { + if (!cur) return 0; + + count.set(cur.val, (count.get(cur.val) || 0) + 1); + let odd_change = (count.get(cur.val) % 2 === 1) ? 1 : -1; + odd += odd_change; + + let res; + if (!cur.left && !cur.right) { + res = (odd <= 1) ? 1 : 0; + } else { + res = dfs(cur.left) + dfs(cur.right); + } + + odd -= odd_change; + count.set(cur.val, count.get(cur.val) - 1); + return res; + }; + + return dfs(root); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(h)$ for recursion stack. + +> Where $n$ is the number of nodes and $h$ is the height of the given tree. + +--- + +## 2. Depth First Search (Using Array) + +::tabs-start + +```python +# 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 +class Solution: + def pseudoPalindromicPaths(self, root: Optional[TreeNode]) -> int: + count = [0] * 10 + odd = 0 + + def dfs(cur): + nonlocal odd + if not cur: + return 0 + + count[cur.val] ^= 1 + odd += 1 if count[cur.val] else -1 + + if not cur.left and not cur.right and odd <= 1: + res = 1 + else: + res = dfs(cur.left) + dfs(cur.right) + + odd -= 1 if count[cur.val] else -1 + count[cur.val] ^= 1 + + return res + + return dfs(root) +``` + +```java +/** + * 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; + * } + * } + */ +public class Solution { + public int pseudoPalindromicPaths(TreeNode root) { + int[] count = new int[10]; + return dfs(root, count, 0); + } + + private int dfs(TreeNode cur, int[] count, int odd) { + if (cur == null) return 0; + + count[cur.val] ^= 1; + odd += count[cur.val] == 1 ? 1 : -1; + + int res = (cur.left == null && cur.right == null && odd <= 1) ? 1 + : dfs(cur.left, count, odd) + dfs(cur.right, count, odd); + + odd += count[cur.val] == 1 ? 1 : -1; + count[cur.val] ^= 1; + + return res; + } +} +``` + +```cpp +/** + * 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: + int pseudoPalindromicPaths(TreeNode* root) { + int count[10] = {}; + return dfs(root, count, 0); + } + +private: + int dfs(TreeNode* cur, int count[], int odd) { + if (!cur) return 0; + + count[cur->val] ^= 1; + odd += (count[cur->val] == 1) ? 1 : -1; + + int res = (!cur->left && !cur->right && odd <= 1) ? 1 + : dfs(cur->left, count, odd) + dfs(cur->right, count, odd); + + odd += (count[cur->val] == 1) ? 1 : -1; + count[cur->val] ^= 1; + + return res; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {number} + */ + pseudoPalindromicPaths(root) { + const count = new Array(10).fill(0); + + const dfs = (cur, odd) => { + if (!cur) return 0; + + count[cur.val] ^= 1; + odd += count[cur.val] === 1 ? 1 : -1; + + let res = (!cur.left && !cur.right && odd <= 1) + ? 1 : dfs(cur.left, odd) + dfs(cur.right, odd); + + odd += count[cur.val] === 1 ? 1 : -1; + count[cur.val] ^= 1; + + return res; + }; + + return dfs(root, 0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(h)$ for recursion stack. + +> Where $n$ is the number of nodes and $h$ is the height of the given tree. + +--- + +## 3. Depth First Search (Bit Mask) + +::tabs-start + +```python +# 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 +class Solution: + def pseudoPalindromicPaths(self, root: Optional[TreeNode]) -> int: + def dfs(node, path): + if not node: + return 0 + + path ^= 1 << node.val + if not node.left and not node.right: + return 1 if (path & (path - 1)) == 0 else 0 + + return dfs(node.left, path) + dfs(node.right, path) + + return dfs(root, 0) +``` + +```java +/** + * 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; + * } + * } + */ +public class Solution { + public int pseudoPalindromicPaths(TreeNode root) { + return dfs(root, 0); + } + + private int dfs(TreeNode node, int path) { + if (node == null) return 0; + + path ^= (1 << node.val); + if (node.left == null && node.right == null) { + return (path & (path - 1)) == 0 ? 1 : 0; + } + + return dfs(node.left, path) + dfs(node.right, path); + } +} +``` + +```cpp +/** + * 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: + int pseudoPalindromicPaths(TreeNode* root) { + return dfs(root, 0); + } + +private: + int dfs(TreeNode* node, int path) { + if (!node) return 0; + + path ^= (1 << node->val); + if (!node->left && !node->right) { + return (path & (path - 1)) == 0 ? 1 : 0; + } + + return dfs(node->left, path) + dfs(node->right, path); + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {number} + */ + pseudoPalindromicPaths(root) { + const dfs = (node, path) => { + if (!node) return 0; + + path ^= (1 << node.val); + if (!node.left && !node.right) { + return (path & (path - 1)) === 0 ? 1 : 0; + } + + return dfs(node.left, path) + dfs(node.right, path); + }; + + return dfs(root, 0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(h)$ for recursion stack. + +> Where $n$ is the number of nodes and $h$ is the height of the given tree. + +--- + +## 4. Breadth First Search + +::tabs-start + +```python +# 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 +class Solution: + def pseudoPalindromicPaths(self, root: Optional[TreeNode]) -> int: + res = 0 + q = deque([(root, 0)]) + while q: + node, path = q.popleft() + path ^= 1 << node.val + + if not node.left and not node.right: + if path & (path - 1) == 0: + res += 1 + continue + + if node.left: + q.append((node.left, path)) + if node.right: + q.append((node.right, path)) + + return res +``` + +```java +/** + * 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; + * } + * } + */ +public class Solution { + public int pseudoPalindromicPaths(TreeNode root) { + int count = 0; + Queue q = new LinkedList<>(); + q.offer(new Pair(root, 0)); + while (!q.isEmpty()) { + Pair p = q.poll(); + TreeNode node = p.node; + int path = p.path ^ (1 << node.val); + + if (node.left == null && node.right == null) { + if ((path & (path - 1)) == 0) { + count++; + } + } else { + if (node.left != null) q.offer(new Pair(node.left, path)); + if (node.right != null) q.offer(new Pair(node.right, path)); + } + } + + return count; + } + + private static class Pair { + TreeNode node; + int path; + Pair(TreeNode node, int path) { + this.node = node; + this.path = path; + } + } +} +``` + +```cpp +/** + * 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: + int pseudoPalindromicPaths(TreeNode* root) { + int res = 0; + queue> q; + q.push({root, 0}); + while (!q.empty()) { + auto [node, path] = q.front();q.pop(); + path ^= (1 << node->val); + + if (!node->left && !node->right) { + if ((path & (path - 1)) == 0) res++; + continue; + } + + if (node->left) q.push({node->left, path}); + if (node->right) q.push({node->right, path}); + } + + return res; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {number} + */ + pseudoPalindromicPaths(root) { + let res = 0; + const q = new Queue([[root, 0]]); + + while (!q.isEmpty()) { + const [node, path] = q.pop(); + const newPath = path ^ (1 << node.val); + + if (!node.left && !node.right) { + if ((newPath & (newPath - 1)) === 0) res++; + continue; + } + + if (node.left) q.push([node.left, newPath]); + if (node.right) q.push([node.right, newPath]); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 5. Iterative DFS + +::tabs-start + +```python +# 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 +class Solution: + def pseudoPalindromicPaths(self, root: Optional[TreeNode]) -> int: + count = 0 + stack = [(root, 0)] + while stack: + node, path = stack.pop() + path ^= (1 << node.val) + + if not node.left and not node.right: + if path & (path - 1) == 0: + count += 1 + else: + if node.right: + stack.append((node.right, path)) + if node.left: + stack.append((node.left, path)) + + return count +``` + +```java +/** + * 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; + * } + * } + */ +public class Solution { + public int pseudoPalindromicPaths(TreeNode root) { + int count = 0; + Stack stack = new Stack<>(); + stack.push(new Pair(root, 0)); + + while (!stack.isEmpty()) { + Pair p = stack.pop(); + TreeNode node = p.node; + int path = p.path ^ (1 << node.val); + + if (node.left == null && node.right == null) { + if ((path & (path - 1)) == 0) { + count++; + } + } else { + if (node.right != null) stack.push(new Pair(node.right, path)); + if (node.left != null) stack.push(new Pair(node.left, path)); + } + } + return count; + } + + private static class Pair { + TreeNode node; + int path; + Pair(TreeNode node, int path) { + this.node = node; + this.path = path; + } + } +} +``` + +```cpp +/** + * 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: + int pseudoPalindromicPaths(TreeNode* root) { + stack> s; + s.push({root, 0}); + int count = 0; + + while (!s.empty()) { + auto [node, path] = s.top(); + s.pop(); + path ^= (1 << node->val); + + if (!node->left && !node->right) { + if ((path & (path - 1)) == 0) count++; + } else { + if (node->right) s.push({node->right, path}); + if (node->left) s.push({node->left, path}); + } + } + + return count; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {number} + */ + pseudoPalindromicPaths(root) { + let count = 0; + const stack = [[root, 0]]; + + while (stack.length) { + const [node, path] = stack.pop(); + const newPath = path ^ (1 << node.val); + + if (!node.left && !node.right) { + if ((newPath & (newPath - 1)) === 0) count++; + } else { + if (node.right) stack.push([node.right, newPath]); + if (node.left) stack.push([node.left, newPath]); + } + } + + return count; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(h)$ + +> Where $n$ is the number of nodes and $h$ is the height of the given tree. \ No newline at end of file diff --git a/articles/shortest-path-with-alternating-colors.md b/articles/shortest-path-with-alternating-colors.md new file mode 100644 index 000000000..f5d927069 --- /dev/null +++ b/articles/shortest-path-with-alternating-colors.md @@ -0,0 +1,543 @@ +## 1. Breadth First Search - I + +::tabs-start + +```python +class Solution: + def shortestAlternatingPaths(self, n: int, redEdges: list[list[int]], blueEdges: list[list[int]]) -> list[int]: + red, blue = defaultdict(list), defaultdict(list) + + for src, dst in redEdges: + red[src].append(dst) + + for src, dst in blueEdges: + blue[src].append(dst) + + answer = [-1 for _ in range(n)] + q = deque() + q.append((0, 0, None)) # [node, length, prev_edge_color] + visit = set() + visit.add((0, None)) + + while q: + node, length, edgeColor = q.popleft() + if answer[node] == -1: + answer[node] = length + + if edgeColor != "RED": + for nei in red[node]: + if (nei, "RED") not in visit: + visit.add((nei, "RED")) + q.append((nei, length + 1, "RED")) + + if edgeColor != "BLUE": + for nei in blue[node]: + if (nei, "BLUE") not in visit: + visit.add((nei, "BLUE")) + q.append((nei, length + 1, "BLUE")) + + return answer +``` + +```java +public class Solution { + public int[] shortestAlternatingPaths(int n, int[][] redEdges, int[][] blueEdges) { + List[] red = new ArrayList[n], blue = new ArrayList[n]; + for (int i = 0; i < n; i++) { + red[i] = new ArrayList<>(); + blue[i] = new ArrayList<>(); + } + for (int[] edge : redEdges) red[edge[0]].add(edge[1]); + for (int[] edge : blueEdges) blue[edge[0]].add(edge[1]); + + int[] answer = new int[n]; + Arrays.fill(answer, -1); + Queue q = new LinkedList<>(); + q.offer(new int[]{0, 0, -1}); + Set visit = new HashSet<>(); + visit.add("0,-1"); + + while (!q.isEmpty()) { + int[] nodeData = q.poll(); + int node = nodeData[0], length = nodeData[1], edgeColor = nodeData[2]; + + if (answer[node] == -1) answer[node] = length; + + if (edgeColor != 0) { + for (int nei : red[node]) { + if (visit.add(nei + ",0")) { + q.offer(new int[]{nei, length + 1, 0}); + } + } + } + if (edgeColor != 1) { + for (int nei : blue[node]) { + if (visit.add(nei + ",1")) { + q.offer(new int[]{nei, length + 1, 1}); + } + } + } + } + return answer; + } +} +``` + +```cpp +class Solution { +public: + vector shortestAlternatingPaths(int n, vector>& redEdges, vector>& blueEdges) { + vector> red(n), blue(n); + for (auto& edge : redEdges) red[edge[0]].push_back(edge[1]); + for (auto& edge : blueEdges) blue[edge[0]].push_back(edge[1]); + + vector answer(n, -1); + queue> q; + q.push({0, 0, -1}); + unordered_set visit; + visit.insert("0,-1"); + + while (!q.empty()) { + vector nodeData = q.front(); + q.pop(); + int node = nodeData[0], length = nodeData[1], edgeColor = nodeData[2]; + + if (answer[node] == -1) answer[node] = length; + + if (edgeColor != 0) { + for (int nei : red[node]) { + string key = to_string(nei) + ",0"; + if (visit.insert(key).second) { + q.push({nei, length + 1, 0}); + } + } + } + if (edgeColor != 1) { + for (int nei : blue[node]) { + string key = to_string(nei) + ",1"; + if (visit.insert(key).second) { + q.push({nei, length + 1, 1}); + } + } + } + } + return answer; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number[][]} redEdges + * @param {number[][]} blueEdges + * @return {number[]} + */ + shortestAlternatingPaths(n, redEdges, blueEdges) { + const red = Array.from({ length: n }, () => []); + const blue = Array.from({ length: n }, () => []); + + for (const [src, dst] of redEdges) red[src].push(dst); + for (const [src, dst] of blueEdges) blue[src].push(dst); + + const answer = new Array(n).fill(-1); + const q = new Queue([[0, 0, null]]); + const visit = new Set(["0,null"]); + + while (!q.isEmpty()) { + const [node, length, edgeColor] = q.pop(); + if (answer[node] === -1) answer[node] = length; + + if (edgeColor !== "RED") { + for (const nei of red[node]) { + if (!visit.has(`${nei},RED`)) { + visit.add(`${nei},RED`); + q.push([nei, length + 1, "RED"]); + } + } + } + if (edgeColor !== "BLUE") { + for (const nei of blue[node]) { + if (!visit.has(`${nei},BLUE`)) { + visit.add(`${nei},BLUE`); + q.push([nei, length + 1, "BLUE"]); + } + } + } + } + return answer; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(V + E)$ +* Space complexity: $O(V + E)$ + +> Where $V$ is the number of vertices and $E$ is the number of edges. + +--- + +## 2. Breadth First Search - II + +::tabs-start + +```python +class Solution: + def shortestAlternatingPaths(self, n: int, redEdges: list[list[int]], blueEdges: list[list[int]]) -> list[int]: + def buildGraph(edges): + adj = [[] for _ in range(n)] + for u, v in edges: + adj[u].append(v) + return adj + + red, blue = buildGraph(redEdges), buildGraph(blueEdges) + adj = [red, blue] + INF = float("inf") + dist = [[INF] * 2 for _ in range(n)] + dist[0][0] = dist[0][1] = 0 + + q = deque([(0, 0), (0, 1)]) + while q: + node, color = q.popleft() + for nei in adj[color][node]: + if dist[nei][color ^ 1] > dist[node][color] + 1: + dist[nei][color ^ 1] = dist[node][color] + 1 + q.append((nei, color ^ 1)) + + answer = [0] + [-1] * (n - 1) + for i in range(1, n): + answer[i] = min(dist[i][0], dist[i][1]) + if answer[i] == INF: + answer[i] = -1 + return answer +``` + +```java +public class Solution { + public int[] shortestAlternatingPaths(int n, int[][] redEdges, int[][] blueEdges) { + List[][] adj = new ArrayList[2][n]; + adj[0] = buildGraph(n, redEdges); + adj[1] = buildGraph(n, blueEdges); + + int INF = Integer.MAX_VALUE; + int[][] dist = new int[n][2]; + + for (int i = 0; i < n; i++) Arrays.fill(dist[i], INF); + dist[0][0] = dist[0][1] = 0; + + Queue q = new LinkedList<>(); + q.offer(new int[]{0, 0}); + q.offer(new int[]{0, 1}); + + while (!q.isEmpty()) { + int[] cur = q.poll(); + int node = cur[0], color = cur[1]; + + for (int nei : adj[color][node]) { + if (dist[nei][color ^ 1] > dist[node][color] + 1) { + dist[nei][color ^ 1] = dist[node][color] + 1; + q.offer(new int[]{nei, color ^ 1}); + } + } + } + + int[] answer = new int[n]; + for (int i = 0; i < n; i++) { + answer[i] = Math.min(dist[i][0], dist[i][1]); + if (answer[i] == INF) answer[i] = -1; + } + return answer; + } + + private List[] buildGraph(int n, int[][] edges) { + List[] adj = new ArrayList[n]; + for (int i = 0; i < n; i++) { + adj[i] = new ArrayList<>(); + } + for (int[] edge : edges) { + adj[edge[0]].add(edge[1]); + } + return adj; + } +} +``` + +```cpp +class Solution { +public: + vector shortestAlternatingPaths(int n, vector>& redEdges, vector>& blueEdges) { + vector> red = buildGraph(n, redEdges); + vector> blue = buildGraph(n, blueEdges); + vector> adj[] = {red, blue}; + + const int INF = 1e6; + vector> dist(n, vector(2, INF)); + dist[0][0] = dist[0][1] = 0; + + queue> q; + q.push({0, 0}); + q.push({0, 1}); + + while (!q.empty()) { + auto [node, color] = q.front();q.pop(); + for (int nei : adj[color][node]) { + if (dist[nei][color ^ 1] > dist[node][color] + 1) { + dist[nei][color ^ 1] = dist[node][color] + 1; + q.push({nei, color ^ 1}); + } + } + } + + vector answer(n, -1); + for (int i = 0; i < n; i++) { + answer[i] = min(dist[i][0], dist[i][1]); + if (answer[i] == INF) answer[i] = -1; + } + return answer; + } + +private: + vector> buildGraph(int n, vector>& edges) { + vector> adj(n); + for (auto& edge : edges) { + adj[edge[0]].push_back(edge[1]); + } + return adj; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number[][]} redEdges + * @param {number[][]} blueEdges + * @return {number[]} + */ + shortestAlternatingPaths(n, redEdges, blueEdges) { + const red = this.buildGraph(n, redEdges); + const blue = this.buildGraph(n, blueEdges); + const adj = [red, blue]; + const INF = 1e6; + const dist = Array.from({ length: n }, () => [INF, INF]); + dist[0][0] = dist[0][1] = 0; + + const q = new Queue([[0, 0], [0, 1]]); + while (!q.isEmpty()) { + const [node, color] = q.pop(); + for (const nei of adj[color][node]) { + if (dist[nei][color ^ 1] > dist[node][color] + 1) { + dist[nei][color ^ 1] = dist[node][color] + 1; + q.push([nei, color ^ 1]); + } + } + } + + return Array.from({ length: n }, (_, i) => { + let minDist = Math.min(dist[i][0], dist[i][1]); + return minDist === INF ? -1 : minDist; + }); + } + + /** + * @param {number} n + * @param {number[][]} edges + * @return {number[][]} + */ + buildGraph(n, edges) { + const adj = Array.from({ length: n }, () => []); + for (const [u, v] of edges) { + adj[u].push(v); + } + return adj; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(V + E)$ +* Space complexity: $O(V + E)$ + +> Where $V$ is the number of vertices and $E$ is the number of edges. + +--- + +## 3. Depth First Search + +::tabs-start + +```python +class Solution: + def shortestAlternatingPaths(self, n: int, redEdges: list[list[int]], blueEdges: list[list[int]]) -> list[int]: + def buildGraph(edges): + adj = [[] for _ in range(n)] + for u, v in edges: + adj[u].append(v) + return adj + + red, blue = buildGraph(redEdges), buildGraph(blueEdges) + adj = [red, blue] + INF = float("inf") + dist = [[INF] * 2 for _ in range(n)] + dist[0][0] = dist[0][1] = 0 + + def dfs(node, color): + for nei in adj[color][node]: + if dist[nei][color ^ 1] > dist[node][color] + 1: + dist[nei][color ^ 1] = dist[node][color] + 1 + dfs(nei, color ^ 1) + + dfs(0, 0) + dfs(0, 1) + + answer = [0] + [-1] * (n - 1) + for i in range(1, n): + answer[i] = min(dist[i][0], dist[i][1]) + if answer[i] == INF: + answer[i] = -1 + + return answer +``` + +```java +public class Solution { + public int[] shortestAlternatingPaths(int n, int[][] redEdges, int[][] blueEdges) { + List[][] adj = new ArrayList[2][n]; + adj[0] = buildGraph(n, redEdges); + adj[1] = buildGraph(n, blueEdges); + + int INF = Integer.MAX_VALUE; + int[][] dist = new int[n][2]; + for (int i = 0; i < n; i++) Arrays.fill(dist[i], INF); + dist[0][0] = dist[0][1] = 0; + + dfs(0, 0, adj, dist); + dfs(0, 1, adj, dist); + + int[] answer = new int[n]; + for (int i = 0; i < n; i++) { + answer[i] = Math.min(dist[i][0], dist[i][1]); + if (answer[i] == INF) answer[i] = -1; + } + return answer; + } + + private void dfs(int node, int color, List[][] adj, int[][] dist) { + for (int nei : adj[color][node]) { + if (dist[nei][color ^ 1] > dist[node][color] + 1) { + dist[nei][color ^ 1] = dist[node][color] + 1; + dfs(nei, color ^ 1, adj, dist); + } + } + } + + private List[] buildGraph(int n, int[][] edges) { + List[] adj = new ArrayList[n]; + for (int i = 0; i < n; i++) { + adj[i] = new ArrayList<>(); + } + for (int[] edge : edges) { + adj[edge[0]].add(edge[1]); + } + return adj; + } +} +``` + +```cpp +class Solution { +public: + vector shortestAlternatingPaths(int n, vector>& redEdges, vector>& blueEdges) { + vector> adj[2] = {buildGraph(n, redEdges), buildGraph(n, blueEdges)}; + + int INF = numeric_limits::max(); + vector> dist(n, vector(2, INF)); + dist[0][0] = dist[0][1] = 0; + + dfs(0, 0, adj, dist); + dfs(0, 1, adj, dist); + + vector answer(n, -1); + for (int i = 0; i < n; i++) { + answer[i] = min(dist[i][0], dist[i][1]); + if (answer[i] == INF) answer[i] = -1; + } + return answer; + } + +private: + void dfs(int node, int color, vector> adj[], vector>& dist) { + for (int nei : adj[color][node]) { + if (dist[nei][color ^ 1] > dist[node][color] + 1) { + dist[nei][color ^ 1] = dist[node][color] + 1; + dfs(nei, color ^ 1, adj, dist); + } + } + } + + vector> buildGraph(int n, vector>& edges) { + vector> adj(n); + for (auto& edge : edges) { + adj[edge[0]].push_back(edge[1]); + } + return adj; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number[][]} redEdges + * @param {number[][]} blueEdges + * @return {number[]} + */ + shortestAlternatingPaths(n, redEdges, blueEdges) { + const INF = Number.MAX_SAFE_INTEGER; + const adj = [Array.from({ length: n }, () => []), + Array.from({ length: n }, () => [])]; + + redEdges.forEach(([u, v]) => adj[0][u].push(v)); + blueEdges.forEach(([u, v]) => adj[1][u].push(v)); + + const dist = Array.from({ length: n }, () => [INF, INF]); + dist[0][0] = dist[0][1] = 0; + + const dfs = (node, color) => { + adj[color][node].forEach(nei => { + if (dist[nei][color ^ 1] > dist[node][color] + 1) { + dist[nei][color ^ 1] = dist[node][color] + 1; + dfs(nei, color ^ 1); + } + }); + }; + + dfs(0, 0); + dfs(0, 1); + + return dist.map(([red, blue]) => { + let res = Math.min(red, blue); + return res === INF ? -1 : res; + }); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(V + E)$ +* Space complexity: $O(V + E)$ + +> Where $V$ is the number of vertices and $E$ is the number of edges. \ No newline at end of file diff --git a/articles/smallest-string-starting-from-leaf.md b/articles/smallest-string-starting-from-leaf.md new file mode 100644 index 000000000..d3d241adb --- /dev/null +++ b/articles/smallest-string-starting-from-leaf.md @@ -0,0 +1,474 @@ +## 1. Depth First Search + +::tabs-start + +```python +# 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 +class Solution: + def smallestFromLeaf(self, root: Optional[TreeNode]) -> str: + def dfs(root, cur): + if not root: + return + + cur = chr(ord('a') + root.val) + cur + if root.left and root.right: + return min( + dfs(root.left, cur), + dfs(root.right, cur) + ) + + if root.right: + return dfs(root.right, cur) + if root.left: + return dfs(root.left, cur) + return cur + + return dfs(root, "") +``` + +```java +/** + * 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; + * } + * } + */ +public class Solution { + public String smallestFromLeaf(TreeNode root) { + return dfs(root, ""); + } + + private String dfs(TreeNode root, String cur) { + if (root == null) { + return null; + } + + cur = (char) ('a' + root.val) + cur; + if (root.left != null && root.right != null) { + return min(dfs(root.left, cur), dfs(root.right, cur)); + } + + if (root.right != null) { + return dfs(root.right, cur); + } + if (root.left != null) { + return dfs(root.left, cur); + } + return cur; + } + + private String min(String a, String b) { + if (a == null) return b; + if (b == null) return a; + return a.compareTo(b) < 0 ? a : b; + } +} +``` + +```cpp +/** + * 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: + string smallestFromLeaf(TreeNode* root) { + return dfs(root, ""); + } + +private: + string dfs(TreeNode* root, string cur) { + if (!root) return ""; + + cur = char('a' + root->val) + cur; + if (root->left && root->right) { + return min(dfs(root->left, cur), dfs(root->right, cur)); + } + + if (root->right) return dfs(root->right, cur); + if (root->left) return dfs(root->left, cur); + return cur; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {string} + */ + smallestFromLeaf(root) { + const min = (a, b) => { + if (!a) return b; + if (!b) return a; + return a < b ? a : b; + }; + + const dfs = (node, cur) => { + if (!node) return; + + cur = String.fromCharCode(97 + node.val) + cur; + + if (node.left && node.right) { + return min(dfs(node.left, cur), dfs(node.right, cur)); + } + if (node.left) return dfs(node.left, cur); + if (node.right) return dfs(node.right, cur); + return cur; + }; + + return dfs(root, ""); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n ^ 2)$ + +--- + +## 2. Breadth First Search + +::tabs-start + +```python +# 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 +class Solution: + def smallestFromLeaf(self, root: Optional[TreeNode]) -> str: + q = deque([(root, "")]) + res = None + + while q: + node, cur = q.popleft() + cur = chr(ord('a') + node.val) + cur + + if not node.left and not node.right: + res = min(res, cur) if res else cur + + if node.left: + q.append((node.left, cur)) + if node.right: + q.append((node.right, cur)) + + return res +``` + +```java +/** + * 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; + * } + * } + */ +public class Solution { + public String smallestFromLeaf(TreeNode root) { + Queue> q = new LinkedList<>(); + q.offer(new Pair<>(root, "")); + String res = null; + + while (!q.isEmpty()) { + Pair pair = q.poll(); + TreeNode node = pair.getKey(); + String cur = (char) ('a' + node.val) + pair.getValue(); + + if (node.left == null && node.right == null) { + if (res == null || cur.compareTo(res) < 0) { + res = cur; + } + } + + if (node.left != null) q.offer(new Pair<>(node.left, cur)); + if (node.right != null) q.offer(new Pair<>(node.right, cur)); + } + return res; + } +} +``` + +```cpp +/** + * 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: + string smallestFromLeaf(TreeNode* root) { + queue> q; + q.push({root, ""}); + string res; + + while (!q.empty()) { + auto [node, cur] = q.front(); + q.pop(); + cur = char('a' + node->val) + cur; + + if (!node->left && !node->right) { + if (res.empty() || cur < res) { + res = cur; + } + } + + if (node->left) q.push({node->left, cur}); + if (node->right) q.push({node->right, cur}); + } + return res; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {string} + */ + smallestFromLeaf(root) { + const q = new Queue(); + q.push([root, ""]); + let res = null; + + while (!q.isEmpty()) { + const [node, cur] = q.pop(); + const newCur = String.fromCharCode(97 + node.val) + cur; + + if (!node.left && !node.right) { + res = res === null || newCur < res ? newCur : res; + } + + if (node.left) q.push([node.left, newCur]); + if (node.right) q.push([node.right, newCur]); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n ^ 2)$ + +--- + +## 3. Iterative DFS + +::tabs-start + +```python +# 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 +class Solution: + def smallestFromLeaf(self, root: Optional[TreeNode]) -> str: + stack = [(root, "")] + res = None + + while stack: + node, cur = stack.pop() + cur = chr(ord('a') + node.val) + cur + + if not node.left and not node.right: + res = min(res, cur) if res else cur + + if node.right: + stack.append((node.right, cur)) + if node.left: + stack.append((node.left, cur)) + + return res +``` + +```java +/** + * 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; + * } + * } + */ +public class Solution { + public String smallestFromLeaf(TreeNode root) { + Stack> stack = new Stack<>(); + stack.push(new Pair<>(root, "")); + String res = null; + + while (!stack.isEmpty()) { + Pair pair = stack.pop(); + TreeNode node = pair.getKey(); + String cur = (char) ('a' + node.val) + pair.getValue(); + + if (node.left == null && node.right == null) { + if (res == null || cur.compareTo(res) < 0) { + res = cur; + } + } + + if (node.right != null) stack.push(new Pair<>(node.right, cur)); + if (node.left != null) stack.push(new Pair<>(node.left, cur)); + } + return res; + } +} +``` + +```cpp +/** + * 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: + string smallestFromLeaf(TreeNode* root) { + stack> stk; + stk.push({root, ""}); + string res; + + while (!stk.empty()) { + auto [node, cur] = stk.top();stk.pop(); + cur = char('a' + node->val) + cur; + + if (!node->left && !node->right) { + if (res.empty() || cur < res) { + res = cur; + } + } + + if (node->right) stk.push({node->right, cur}); + if (node->left) stk.push({node->left, cur}); + } + return res; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {string} + */ + smallestFromLeaf(root) { + const stack = [[root, ""]]; + let res = null; + + while (stack.length) { + const [node, cur] = stack.pop(); + const newCur = String.fromCharCode(97 + node.val) + cur; + + if (!node.left && !node.right) { + res = res === null || newCur < res ? newCur : res; + } + + if (node.right) stack.push([node.right, newCur]); + if (node.left) stack.push([node.left, newCur]); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n ^ 2)$ \ No newline at end of file diff --git a/articles/split-linked-list-in-parts.md b/articles/split-linked-list-in-parts.md new file mode 100644 index 000000000..5df42573a --- /dev/null +++ b/articles/split-linked-list-in-parts.md @@ -0,0 +1,343 @@ +## 1. Convert To Array + +::tabs-start + +```python +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, val=0, next=None): +# self.val = val +# self.next = next +class Solution: + def splitListToParts(self, head: Optional[ListNode], k: int) -> List[Optional[ListNode]]: + arr, cur = [], head + while cur: + arr.append(cur) + cur = cur.next + + N = len(arr) + base_len, remainder = N // k, N % k + + res = [None] * k + start = 0 + for i in range(k): + if start < N: + res[i] = arr[start] + tail = start + base_len - 1 + if remainder: + tail += 1 + remainder -= 1 + arr[tail].next = None + start = tail + 1 + + return res +``` + +```java +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode() {} + * ListNode(int val) { this.val = val; } + * ListNode(int val, ListNode next) { this.val = val; this.next = next; } + * } + */ +public class Solution { + public ListNode[] splitListToParts(ListNode head, int k) { + List arr = new ArrayList<>(); + for (ListNode cur = head; cur != null; cur = cur.next) { + arr.add(cur); + } + + int N = arr.size(); + int base_len = N / k, remainder = N % k; + + ListNode[] res = new ListNode[k]; + int start = 0; + for (int i = 0; i < k; i++) { + if (start < N) { + res[i] = arr.get(start); + int tail = start + base_len - 1; + if (remainder > 0) { + tail++; + remainder--; + } + arr.get(tail).next = null; + start = tail + 1; + } + } + + return res; + } +} +``` + +```cpp +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode() : val(0), next(nullptr) {} + * ListNode(int x) : val(x), next(nullptr) {} + * ListNode(int x, ListNode *next) : val(x), next(next) {} + * }; + */ +class Solution { +public: + vector splitListToParts(ListNode* head, int k) { + vector arr; + for (ListNode* cur = head; cur != nullptr; cur = cur->next) { + arr.push_back(cur); + } + + int N = arr.size(); + int base_len = N / k, remainder = N % k; + + vector res(k, nullptr); + int start = 0; + for (int i = 0; i < k; i++) { + if (start < N) { + res[i] = arr[start]; + int tail = start + base_len - 1; + if (remainder > 0) { + tail++; + remainder--; + } + arr[tail]->next = nullptr; + start = tail + 1; + } + } + + return res; + } +}; +``` + +```javascript +/** + * Definition for singly-linked list. + * class ListNode { + * constructor(val = 0, next = null) { + * this.val = val; + * this.next = next; + * } + * } + */ +class Solution { + /** + * @param {ListNode} head + * @param {number} k + * @return {ListNode[]} + */ + splitListToParts(head, k) { + let arr = []; + for (let cur = head; cur !== null; cur = cur.next) { + arr.push(cur); + } + + let N = arr.length; + let base_len = Math.floor(N / k), remainder = N % k; + + let res = new Array(k).fill(null); + let start = 0; + for (let i = 0; i < k; i++) { + if (start < N) { + res[i] = arr[start]; + let tail = start + base_len - 1; + if (remainder > 0) { + tail++; + remainder--; + } + arr[tail].next = null; + start = tail + 1; + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Iteration + +::tabs-start + +```python +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, val=0, next=None): +# self.val = val +# self.next = next +class Solution: + def splitListToParts(self, head: Optional[ListNode], k: int) -> List[Optional[ListNode]]: + length, curr = 0, head + while curr: + curr = curr.next + length += 1 + + base_len, remainder = length // k, length % k + curr, res = head, [] + + for i in range(k): + res.append(curr) + for j in range(base_len - 1 + (1 if remainder else 0)): + if not curr: + break + curr = curr.next + remainder -= 1 if remainder else 0 + if curr: + curr.next, curr = None, curr.next + + return res +``` + +```java +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode() {} + * ListNode(int val) { this.val = val; } + * ListNode(int val, ListNode next) { this.val = val; this.next = next; } + * } + */ +public class Solution { + public ListNode[] splitListToParts(ListNode head, int k) { + int length = 0; + ListNode curr = head; + while (curr != null) { + curr = curr.next; + length++; + } + + int baseLen = length / k, remainder = length % k; + ListNode[] res = new ListNode[k]; + curr = head; + + for (int i = 0; i < k; i++) { + res[i] = curr; + for (int j = 0; j < baseLen - 1 + (remainder > 0 ? 1 : 0); j++) { + if (curr == null) break; + curr = curr.next; + } + if (curr != null) { + ListNode temp = curr.next; + curr.next = null; + curr = temp; + } + remainder--; + } + return res; + } +} +``` + +```cpp +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode() : val(0), next(nullptr) {} + * ListNode(int x) : val(x), next(nullptr) {} + * ListNode(int x, ListNode *next) : val(x), next(next) {} + * }; + */ +class Solution { +public: + vector splitListToParts(ListNode* head, int k) { + int length = 0; + ListNode* curr = head; + while (curr) { + curr = curr->next; + length++; + } + + int baseLen = length / k, remainder = length % k; + vector res(k, nullptr); + curr = head; + + for (int i = 0; i < k; i++) { + res[i] = curr; + for (int j = 0; j < baseLen - 1 + (remainder > 0 ? 1 : 0); j++) { + if (!curr) break; + curr = curr->next; + } + if (curr) { + ListNode* temp = curr->next; + curr->next = nullptr; + curr = temp; + } + remainder--; + } + return res; + } +}; +``` + +```javascript +/** + * Definition for singly-linked list. + * class ListNode { + * constructor(val = 0, next = null) { + * this.val = val; + * this.next = next; + * } + * } + */ +class Solution { + /** + * @param {ListNode} head + * @param {number} k + * @return {ListNode[]} + */ + splitListToParts(head, k) { + let length = 0, curr = head; + while (curr) { + curr = curr.next; + length++; + } + + let baseLen = Math.floor(length / k), remainder = length % k; + let res = new Array(k).fill(null); + curr = head; + + for (let i = 0; i < k; i++) { + res[i] = curr; + for (let j = 0; j < baseLen - 1 + (remainder > 0 ? 1 : 0); j++) { + if (!curr) break; + curr = curr.next; + } + if (curr) { + let temp = curr.next; + curr.next = null; + curr = temp; + } + remainder--; + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: + * $O(1)$ extra space. + * $O(n)$ space for the output array. \ No newline at end of file diff --git a/articles/sum-root-to-leaf-numbers.md b/articles/sum-root-to-leaf-numbers.md new file mode 100644 index 000000000..02bfd196e --- /dev/null +++ b/articles/sum-root-to-leaf-numbers.md @@ -0,0 +1,652 @@ +## 1. Depth First Search + +::tabs-start + +```python +# 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 +class Solution: + def sumNumbers(self, root: TreeNode) -> int: + def dfs(cur, num): + if not cur: + return 0 + + num = num * 10 + cur.val + if not cur.left and not cur.right: + return num + return dfs(cur.left, num) + dfs(cur.right, num) + + return dfs(root, 0) +``` + +```java +/** + * 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; + * } + * } + */ +public class Solution { + public int sumNumbers(TreeNode root) { + return dfs(root, 0); + } + + private int dfs(TreeNode cur, int num) { + if (cur == null) return 0; + + num = num * 10 + cur.val; + if (cur.left == null && cur.right == null) return num; + + return dfs(cur.left, num) + dfs(cur.right, num); + } +} +``` + +```cpp +/** + * 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: + int sumNumbers(TreeNode* root) { + return dfs(root, 0); + } + +private: + int dfs(TreeNode* cur, int num) { + if (!cur) return 0; + + num = num * 10 + cur->val; + if (!cur->left && !cur->right) return num; + + return dfs(cur->left, num) + dfs(cur->right, num); + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {number} + */ + sumNumbers(root) { + const dfs = (cur, num) => { + if (!cur) return 0; + + num = num * 10 + cur.val; + if (!cur.left && !cur.right) return num; + + return dfs(cur.left, num) + dfs(cur.right, num); + }; + + return dfs(root, 0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(h)$ for recursion stack. + +> Where $n$ is the number of nodes and $h$ is the height of the given tree. + +--- + +## 2. Breadth First Search + +::tabs-start + +```python +# 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 +class Solution: + def sumNumbers(self, root: TreeNode) -> int: + res = 0 + q = deque([(root, 0)]) + while q: + cur, num = q.popleft() + num = num * 10 + cur.val + if not cur.left and not cur.right: + res += num + continue + + if cur.left: + q.append((cur.left, num)) + if cur.right: + q.append((cur.right, num)) + + return res +``` + +```java +/** + * 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; + * } + * } + */ +public class Solution { + public int sumNumbers(TreeNode root) { + int res = 0; + Queue> q = new LinkedList<>(); + q.offer(new Pair<>(root, 0)); + while (!q.isEmpty()) { + Pair p = q.poll(); + TreeNode cur = p.getKey(); + int num = p.getValue() * 10 + cur.val; + if (cur.left == null && cur.right == null) { + res += num; + continue; + } + + if (cur.left != null) q.offer(new Pair<>(cur.left, num)); + if (cur.right != null) q.offer(new Pair<>(cur.right, num)); + } + + return res; + } +} +``` + +```cpp +/** + * 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: + int sumNumbers(TreeNode* root) { + int res = 0; + queue> q; + q.push({root, 0}); + while (!q.empty()) { + auto [cur, num] = q.front();q.pop(); + num = num * 10 + cur->val; + if (!cur->left && !cur->right) { + res += num; + continue; + } + + if (cur->left) q.push({cur->left, num}); + if (cur->right) q.push({cur->right, num}); + } + + return res; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {number} + */ + sumNumbers(root) { + let res = 0; + const q = new Queue([[root, 0]]); + while (!q.isEmpty()) { + const [cur, num] = q.pop(); + const newNum = num * 10 + cur.val; + if (!cur.left && !cur.right) { + res += newNum; + continue; + } + + if (cur.left) q.push([cur.left, newNum]); + if (cur.right) q.push([cur.right, newNum]); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Iterative DFS + +::tabs-start + +```python +# 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 +class Solution: + def sumNumbers(self, root: Optional[TreeNode]) -> int: + res = 0 + stack = [] + cur, num = root, 0 + + while cur or stack: + if cur: + num = num * 10 + cur.val + if not cur.left and not cur.right: + res += num + + stack.append((cur.right, num)) + cur = cur.left + else: + cur, num = stack.pop() + + return res +``` + +```java +/** + * 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; + * } + * } + */ +public class Solution { + public int sumNumbers(TreeNode root) { + int res = 0, num = 0; + Stack> stack = new Stack<>(); + TreeNode cur = root; + + while (cur != null || !stack.isEmpty()) { + if (cur != null) { + num = num * 10 + cur.val; + if (cur.left == null && cur.right == null) + res += num; + + stack.push(new Pair<>(cur.right, num)); + cur = cur.left; + } else { + Pair p = stack.pop(); + cur = p.getKey(); + num = p.getValue(); + } + } + return res; + } +} +``` + +```cpp +/** + * 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: + int sumNumbers(TreeNode* root) { + int res = 0; + stack> st; + TreeNode* cur = root; + int num = 0; + + while (cur || !st.empty()) { + if (cur) { + num = num * 10 + cur->val; + if (!cur->left && !cur->right) + res += num; + + st.push({cur->right, num}); + cur = cur->left; + } else { + cur = st.top().first; + num = st.top().second; + st.pop(); + } + } + return res; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {number} + */ + sumNumbers(root) { + let res = 0, num = 0; + let stack = []; + let cur = root; + + while (cur || stack.length) { + if (cur) { + num = num * 10 + cur.val; + if (!cur.left && !cur.right) + res += num; + + stack.push([cur.right, num]); + cur = cur.left; + } else { + [cur, num] = stack.pop(); + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(h)$ + +> Where $n$ is the number of nodes and $h$ is the height of the given tree. + +--- + +## 4. Morris Travrsal + +::tabs-start + +```python +# 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 +class Solution: + def sumNumbers(self, root: Optional[TreeNode]) -> int: + res = 0 + cur = root + num = 0 + power = [1] * 10 + for i in range(1, 10): + power[i] *= power[i - 1] * 10 + + while cur: + if not cur.left: + num = num * 10 + cur.val + if not cur.right: + res += num + cur = cur.right + else: + prev = cur.left + steps = 1 + while prev.right and prev.right != cur: + prev = prev.right + steps += 1 + + if not prev.right: + prev.right = cur + num = num * 10 + cur.val + cur = cur.left + else: + prev.right = None + if not prev.left: + res += num + num //= power[steps] + cur = cur.right + + return res +``` + +```java +/** + * 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; + * } + * } + */ +public class Solution { + public int sumNumbers(TreeNode root) { + int res = 0, num = 0; + int[] power = new int[10]; + power[0] = 1; + for (int i = 1; i < 10; i++) { + power[i] = power[i - 1] * 10; + } + + TreeNode cur = root; + while (cur != null) { + if (cur.left == null) { + num = num * 10 + cur.val; + if (cur.right == null) res += num; + cur = cur.right; + } else { + TreeNode prev = cur.left; + int steps = 1; + while (prev.right != null && prev.right != cur) { + prev = prev.right; + steps++; + } + + if (prev.right == null) { + prev.right = cur; + num = num * 10 + cur.val; + cur = cur.left; + } else { + prev.right = null; + if (prev.left == null) res += num; + num /= power[steps]; + cur = cur.right; + } + } + } + return res; + } +} +``` + +```cpp +/** + * 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: + int sumNumbers(TreeNode* root) { + int res = 0, num = 0; + int power[10] = {1}; + for (int i = 1; i < 10; i++) { + power[i] = power[i - 1] * 10; + } + + TreeNode* cur = root; + while (cur) { + if (!cur->left) { + num = num * 10 + cur->val; + if (!cur->right) res += num; + cur = cur->right; + } else { + TreeNode* prev = cur->left; + int steps = 1; + while (prev->right && prev->right != cur) { + prev = prev->right; + steps++; + } + + if (!prev->right) { + prev->right = cur; + num = num * 10 + cur->val; + cur = cur->left; + } else { + prev->right = nullptr; + if (!prev->left) res += num; + num /= power[steps]; + cur = cur->right; + } + } + } + return res; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {number} + */ + sumNumbers(root) { + let res = 0, num = 0; + let power = Array(10).fill(1); + for (let i = 1; i < 10; i++) { + power[i] = power[i - 1] * 10; + } + + let cur = root; + while (cur) { + if (!cur.left) { + num = num * 10 + cur.val; + if (!cur.right) res += num; + cur = cur.right; + } else { + let prev = cur.left, steps = 1; + while (prev.right && prev.right !== cur) { + prev = prev.right; + steps++; + } + + if (!prev.right) { + prev.right = cur; + num = num * 10 + cur.val; + cur = cur.left; + } else { + prev.right = null; + if (!prev.left) res += num; + num = Math.floor(num / power[steps]); + cur = cur.right; + } + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. \ No newline at end of file diff --git a/articles/trim-a-binary-search-tree.md b/articles/trim-a-binary-search-tree.md new file mode 100644 index 000000000..6a7eb3f21 --- /dev/null +++ b/articles/trim-a-binary-search-tree.md @@ -0,0 +1,488 @@ +## 1. Depth First Search + +::tabs-start + +```python +# 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 +class Solution: + def trimBST(self, root: Optional[TreeNode], low: int, high: int) -> Optional[TreeNode]: + if not root: + return None + + if root.val > high: + return self.trimBST(root.left, low, high) + if root.val < low: + return self.trimBST(root.right, low, high) + + root.left = self.trimBST(root.left, low, high) + root.right = self.trimBST(root.right, low, high) + return root +``` + +```java +/** + * 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; + * } + * } + */ +public class Solution { + public TreeNode trimBST(TreeNode root, int low, int high) { + if (root == null) { + return null; + } + + if (root.val > high) { + return trimBST(root.left, low, high); + } + if (root.val < low) { + return trimBST(root.right, low, high); + } + + root.left = trimBST(root.left, low, high); + root.right = trimBST(root.right, low, high); + return root; + } +} +``` + +```cpp +/** + * 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* trimBST(TreeNode* root, int low, int high) { + if (!root) return nullptr; + + if (root->val > high) { + return trimBST(root->left, low, high); + } + if (root->val < low) { + return trimBST(root->right, low, high); + } + + root->left = trimBST(root->left, low, high); + root->right = trimBST(root->right, low, high); + return root; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @param {number} low + * @param {number} high + * @return {TreeNode} + */ + trimBST(root, low, high) { + if (!root) return null; + + if (root.val > high) return this.trimBST(root.left, low, high); + if (root.val < low) return this.trimBST(root.right, low, high); + + root.left = this.trimBST(root.left, low, high); + root.right = this.trimBST(root.right, low, high); + return root; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ for recursion stack. + +--- + +## 2. Iterative DFS + +::tabs-start + +```python +# 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 +class Solution: + def trimBST(self, root, low, high): + while root and (root.val < low or root.val > high): + if root.val < low: + root = root.right + else: + root = root.left + + stack = [root] + while stack: + node = stack.pop() + if not node: + continue + left_out = node.left and node.left.val < low + right_out = node.right and node.right.val > high + if left_out: + node.left = node.left.right + if right_out: + node.right = node.right.left + if left_out or right_out: + stack.append(node) + else: + if node.left: + stack.append(node.left) + if node.right: + stack.append(node.right) + return root +``` + +```java +/** + * 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; + * } + * } + */ +public class Solution { + public TreeNode trimBST(TreeNode root, int low, int high) { + while (root != null && (root.val < low || root.val > high)) { + root = (root.val < low) ? root.right : root.left; + } + + Stack stack = new Stack<>(); + stack.push(root); + + while (!stack.isEmpty()) { + TreeNode node = stack.pop(); + if (node == null) continue; + + boolean leftOut = (node.left != null && node.left.val < low); + boolean rightOut = (node.right != null && node.right.val > high); + + if (leftOut) node.left = node.left.right; + if (rightOut) node.right = node.right.left; + + if (leftOut || rightOut) { + stack.push(node); + } else { + if (node.left != null) stack.push(node.left); + if (node.right != null) stack.push(node.right); + } + } + + return root; + } +} +``` + +```cpp +/** + * 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* trimBST(TreeNode* root, int low, int high) { + while (root && (root->val < low || root->val > high)) { + root = (root->val < low) ? root->right : root->left; + } + + stack stack; + stack.push(root); + + while (!stack.empty()) { + TreeNode* node = stack.top(); + stack.pop(); + if (!node) continue; + + bool leftOut = (node->left && node->left->val < low); + bool rightOut = (node->right && node->right->val > high); + + if (leftOut) node->left = node->left->right; + if (rightOut) node->right = node->right->left; + + if (leftOut || rightOut) { + stack.push(node); + } else { + if (node->left) stack.push(node->left); + if (node->right) stack.push(node->right); + } + } + + return root; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @param {number} low + * @param {number} high + * @return {TreeNode} + */ + trimBST(root, low, high) { + while (root && (root.val < low || root.val > high)) { + root = (root.val < low) ? root.right : root.left; + } + + const stack = [root]; + + while (stack.length > 0) { + let node = stack.pop(); + if (!node) continue; + + let leftOut = node.left && node.left.val < low; + let rightOut = node.right && node.right.val > high; + + if (leftOut) node.left = node.left.right; + if (rightOut) node.right = node.right.left; + + if (leftOut || rightOut) { + stack.push(node); + } else { + if (node.left) stack.push(node.left); + if (node.right) stack.push(node.right); + } + } + + return root; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Iterative DFS (Optimal) + +::tabs-start + +```python +# 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 +class Solution: + def trimBST(self, root: Optional[TreeNode], low: int, high: int) -> Optional[TreeNode]: + while root and (root.val < low or root.val > high): + root = root.right if root.val < low else root.left + + tmpRoot = root + while root: + while root.left and root.left.val < low: + root.left = root.left.right + root = root.left + + root = tmpRoot + while root: + while root.right and root.right.val > high: + root.right = root.right.left + root = root.right + + return tmpRoot +``` + +```java +/** + * 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; + * } + * } + */ +public class Solution { + public TreeNode trimBST(TreeNode root, int low, int high) { + while (root != null && (root.val < low || root.val > high)) { + root = (root.val < low) ? root.right : root.left; + } + + TreeNode tmpRoot = root; + while (root != null) { + while (root.left != null && root.left.val < low) { + root.left = root.left.right; + } + root = root.left; + } + + root = tmpRoot; + while (root != null) { + while (root.right != null && root.right.val > high) { + root.right = root.right.left; + } + root = root.right; + } + + return tmpRoot; + } +} +``` + +```cpp +/** + * 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* trimBST(TreeNode* root, int low, int high) { + while (root && (root->val < low || root->val > high)) { + root = (root->val < low) ? root->right : root->left; + } + + TreeNode* tmpRoot = root; + while (root) { + while (root->left && root->left->val < low) { + root->left = root->left->right; + } + root = root->left; + } + + root = tmpRoot; + while (root) { + while (root->right && root->right->val > high) { + root->right = root->right->left; + } + root = root->right; + } + + return tmpRoot; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @param {number} low + * @param {number} high + * @return {TreeNode} + */ + trimBST(root, low, high) { + while (root && (root.val < low || root.val > high)) { + root = (root.val < low) ? root.right : root.left; + } + + let tmpRoot = root; + while (root) { + while (root.left && root.left.val < low) { + root.left = root.left.right; + } + root = root.left; + } + + root = tmpRoot; + while (root) { + while (root.right && root.right.val > high) { + root.right = root.right.left; + } + root = root.right; + } + + return tmpRoot; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. \ No newline at end of file diff --git a/articles/unique-binary-search-trees-ii.md b/articles/unique-binary-search-trees-ii.md new file mode 100644 index 000000000..3c3a4ccee --- /dev/null +++ b/articles/unique-binary-search-trees-ii.md @@ -0,0 +1,670 @@ +## 1. Recursion + +::tabs-start + +```python +# 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 +class Solution: + def generateTrees(self, n: int) -> List[Optional[TreeNode]]: + def generate(left, right): + if left > right: + return [None] + + res = [] + for val in range(left, right + 1): + for leftTree in generate(left, val - 1): + for rightTree in generate(val + 1, right): + root = TreeNode(val, leftTree, rightTree) + res.append(root) + return res + + return generate(1, n) +``` + +```java +/** + * 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; + * } + * } + */ +public class Solution { + public List generateTrees(int n) { + return generate(1, n); + } + + private List generate(int left, int right) { + List res = new ArrayList<>(); + if (left > right) { + res.add(null); + return res; + } + + for (int val = left; val <= right; val++) { + List leftTrees = generate(left, val - 1); + List rightTrees = generate(val + 1, right); + + for (TreeNode leftTree : leftTrees) { + for (TreeNode rightTree : rightTrees) { + res.add(new TreeNode(val, leftTree, rightTree)); + } + } + } + return res; + } +} +``` + +```cpp +/** + * 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: + vector generateTrees(int n) { + return generate(1, n); + } + +private: + vector generate(int left, int right) { + if (left > right) return {nullptr}; + + vector res; + for (int val = left; val <= right; val++) { + vector leftTrees = generate(left, val - 1); + vector rightTrees = generate(val + 1, right); + + for (auto& leftTree : leftTrees) { + for (auto& rightTree : rightTrees) { + res.push_back(new TreeNode(val, leftTree, rightTree)); + } + } + } + return res; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {number} n + * @return {TreeNode[]} + */ + generateTrees(n) { + const generate = (left, right) => { + if (left > right) return [null]; + + let res = []; + for (let val = left; val <= right; val++) { + let leftTrees = generate(left, val - 1); + let rightTrees = generate(val + 1, right); + + for (let leftTree of leftTrees) { + for (let rightTree of rightTrees) { + res.push(new TreeNode(val, leftTree, rightTree)); + } + } + } + return res; + }; + + return generate(1, n); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(\frac {4 ^ n}{\sqrt {n}})$ +* Space complexity: + * $O(n)$ space for recursion stack. + * $O(\frac {4 ^ n}{\sqrt {n}})$ space for the output. + +--- + +## 2. Dynamic Programming (Top-Down) + +::tabs-start + +```python +# 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 +class Solution: + def generateTrees(self, n: int) -> List[Optional[TreeNode]]: + dp = {} + + def generate(left, right): + if left > right: + return [None] + if (left, right) in dp: + return dp[(left, right)] + + res = [] + for val in range(left, right + 1): + for leftTree in generate(left, val - 1): + for rightTree in generate(val + 1, right): + root = TreeNode(val, leftTree, rightTree) + res.append(root) + + dp[(left, right)] = res + return res + + return generate(1, n) +``` + +```java +/** + * 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; + * } + * } + */ +public class Solution { + private List[][] dp; + + public List generateTrees(int n) { + dp = new ArrayList[n + 1][n + 1]; + return generate(1, n); + } + + private List generate(int left, int right) { + if (left > right) return Collections.singletonList(null); + if (dp[left][right] != null) return dp[left][right]; + + List res = new ArrayList<>(); + for (int val = left; val <= right; val++) { + for (TreeNode leftTree : generate(left, val - 1)) { + for (TreeNode rightTree : generate(val + 1, right)) { + res.add(new TreeNode(val, leftTree, rightTree)); + } + } + } + return dp[left][right] = res; + } +} +``` + +```cpp +/** + * 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: + vector>> dp; + + vector generateTrees(int n) { + dp.resize(n + 1, vector>(n + 1)); + return generate(1, n); + } + +private: + vector generate(int left, int right) { + if (left > right) return {nullptr}; + if (!dp[left][right].empty()) return dp[left][right]; + + vector res; + for (int val = left; val <= right; val++) { + for (auto& leftTree : generate(left, val - 1)) { + for (auto& rightTree : generate(val + 1, right)) { + res.push_back(new TreeNode(val, leftTree, rightTree)); + } + } + } + return dp[left][right] = res; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {number} n + * @return {TreeNode[]} + */ + generateTrees(n) { + const dp = Array.from({ length: n + 1 }, () => Array(n + 1).fill(null)); + + const generate = (left, right) => { + if (left > right) return [null]; + if (dp[left][right]) return dp[left][right]; + + let res = []; + for (let val = left; val <= right; val++) { + for (let leftTree of generate(left, val - 1)) { + for (let rightTree of generate(val + 1, right)) { + res.push(new TreeNode(val, leftTree, rightTree)); + } + } + } + return (dp[left][right] = res); + }; + + return generate(1, n); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(\frac {4 ^ n}{\sqrt {n}})$ +* Space complexity: + * $O(n)$ space for recursion stack. + * $O(n ^ 2)$ extra space. + * $O(\frac {4 ^ n}{\sqrt {n}})$ space for the output. + +--- + +## 3. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +# 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 +class Solution: + def generateTrees(self, n: int) -> List[Optional[TreeNode]]: + dp = [[[] for _ in range(n + 2)] for _ in range(n + 2)] + for i in range(1, n + 2): + dp[i][i - 1] = [None] + + for length in range(1, n + 1): + for left in range(1, n - length + 2): + right = left + length - 1 + dp[left][right] = [] + for val in range(left, right + 1): + for leftTree in dp[left][val - 1]: + for rightTree in dp[val + 1][right]: + root = TreeNode(val, leftTree, rightTree) + dp[left][right].append(root) + + return dp[1][n] +``` + +```java +/** + * 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; + * } + * } + */ +public class Solution { + public List generateTrees(int n) { + List[][] dp = new ArrayList[n + 2][n + 2]; + for (int i = 1; i <= n + 1; i++) { + dp[i][i - 1] = new ArrayList<>(); + dp[i][i - 1].add(null); + } + + for (int length = 1; length <= n; length++) { + for (int left = 1; left + length - 1 <= n; left++) { + int right = left + length - 1; + dp[left][right] = new ArrayList<>(); + + for (int val = left; val <= right; val++) { + for (TreeNode leftTree : dp[left][val - 1]) { + for (TreeNode rightTree : dp[val + 1][right]) { + dp[left][right].add(new TreeNode(val, leftTree, rightTree)); + } + } + } + } + } + return dp[1][n]; + } +} +``` + +```cpp +/** + * 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: + vector generateTrees(int n) { + vector>> dp(n + 2, vector>(n + 2)); + for (int i = 1; i <= n + 1; i++) { + dp[i][i - 1].push_back(nullptr); + } + + for (int length = 1; length <= n; length++) { + for (int left = 1; left + length - 1 <= n; left++) { + int right = left + length - 1; + + for (int val = left; val <= right; val++) { + for (auto& leftTree : dp[left][val - 1]) { + for (auto& rightTree : dp[val + 1][right]) { + dp[left][right].push_back(new TreeNode(val, leftTree, rightTree)); + } + } + } + } + } + return dp[1][n]; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {number} n + * @return {TreeNode[]} + */ + generateTrees(n) { + const dp = Array.from({ length: n + 2 }, () => Array(n + 2).fill(null)); + for (let i = 1; i <= n + 1; i++) { + dp[i][i - 1] = [null]; + } + + for (let length = 1; length <= n; length++) { + for (let left = 1; left + length - 1 <= n; left++) { + let right = left + length - 1; + dp[left][right] = []; + + for (let val = left; val <= right; val++) { + for (let leftTree of dp[left][val - 1]) { + for (let rightTree of dp[val + 1][right]) { + dp[left][right].push(new TreeNode(val, leftTree, rightTree)); + } + } + } + } + } + return dp[1][n]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(\frac {4 ^ n}{\sqrt {n}})$ +* Space complexity: + * $O(n ^ 2)$ extra space. + * $O(\frac {4 ^ n}{\sqrt {n}})$ space for the output. + +--- + +## 4. Dynamic Programming (Space Optimized) + +::tabs-start + +```python +# 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 +class Solution: + def generateTrees(self, n: int) -> list[Optional[TreeNode]]: + dp = [[] for _ in range(n + 1)] + dp[0] = [None] + + for length in range(1, n + 1): + dp[length] = [] + for val in range(1, length + 1): + for leftTree in dp[val - 1]: + for rightTree in dp[length - val]: + root = TreeNode(val) + root.left = leftTree + root.right = self.shift(rightTree, val) + dp[length].append(root) + + return dp[n] + + def shift(self, node: Optional[TreeNode], offset: int) -> Optional[TreeNode]: + if not node: + return None + root = TreeNode(node.val + offset) + root.left = self.shift(node.left, offset) + root.right = self.shift(node.right, offset) + return root +``` + +```java +/** + * 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; + * } + * } + */ +public class Solution { + public List generateTrees(int n) { + List[] dp = new ArrayList[n + 1]; + dp[0] = new ArrayList<>(); + dp[0].add(null); + + for (int length = 1; length <= n; length++) { + dp[length] = new ArrayList<>(); + for (int val = 1; val <= length; val++) { + for (TreeNode leftTree : dp[val - 1]) { + for (TreeNode rightTree : dp[length - val]) { + TreeNode root = new TreeNode(val); + root.left = leftTree; + root.right = shift(rightTree, val); + dp[length].add(root); + } + } + } + } + return dp[n]; + } + + private TreeNode shift(TreeNode node, int offset) { + if (node == null) return null; + TreeNode root = new TreeNode(node.val + offset); + root.left = shift(node.left, offset); + root.right = shift(node.right, offset); + return root; + } +} +``` + +```cpp +/** + * 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: + vector generateTrees(int n) { + vector> dp(n + 1); + dp[0].push_back(nullptr); + + for (int length = 1; length <= n; length++) { + for (int val = 1; val <= length; val++) { + for (auto& leftTree : dp[val - 1]) { + for (auto& rightTree : dp[length - val]) { + TreeNode* root = new TreeNode(val); + root->left = leftTree; + root->right = shift(rightTree, val); + dp[length].push_back(root); + } + } + } + } + return dp[n]; + } + +private: + TreeNode* shift(TreeNode* node, int offset) { + if (!node) return nullptr; + TreeNode* root = new TreeNode(node->val + offset); + root->left = shift(node->left, offset); + root->right = shift(node->right, offset); + return root; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {number} n + * @return {TreeNode[]} + */ + generateTrees(n) { + const shift = (node, offset) => { + if (!node) return null; + let root = new TreeNode(node.val + offset); + root.left = shift(node.left, offset); + root.right = shift(node.right, offset); + return root; + }; + + let dp = Array.from({ length: n + 1 }, () => []); + dp[0].push(null); + + for (let length = 1; length <= n; length++) { + for (let val = 1; val <= length; val++) { + for (let leftTree of dp[val - 1]) { + for (let rightTree of dp[length - val]) { + let root = new TreeNode(val); + root.left = leftTree; + root.right = shift(rightTree, val); + dp[length].push(root); + } + } + } + } + return dp[n]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(\frac {4 ^ n}{\sqrt {n}})$ +* Space complexity: + * $O(n)$ for the recursion stack. + * $O(n)$ extra space. + * $O(\frac {4 ^ n}{\sqrt {n}})$ space for the output. \ No newline at end of file diff --git a/articles/validate-binary-tree-nodes.md b/articles/validate-binary-tree-nodes.md new file mode 100644 index 000000000..fe797fbd4 --- /dev/null +++ b/articles/validate-binary-tree-nodes.md @@ -0,0 +1,668 @@ +## 1. Depth First Search + +::tabs-start + +```python +class Solution: + def validateBinaryTreeNodes(self, n: int, leftChild: List[int], rightChild: List[int]) -> bool: + hasParent = set(leftChild + rightChild) + hasParent.discard(-1) + if len(hasParent) == n: + return False + + root = -1 + for i in range(n): + if i not in hasParent: + root = i + break + + visit = set() + def dfs(i): + if i == -1: + return True + if i in visit: + return False + visit.add(i) + return dfs(leftChild[i]) and dfs(rightChild[i]) + + return dfs(root) and len(visit) == n +``` + +```java +public class Solution { + private Set visit; + + public boolean validateBinaryTreeNodes(int n, int[] leftChild, int[] rightChild) { + Set hasParent = new HashSet<>(); + for (int c : leftChild) if (c != -1) hasParent.add(c); + for (int c : rightChild) if (c != -1) hasParent.add(c); + if (hasParent.size() == n) return false; + + int root = -1; + for (int i = 0; i < n; i++) { + if (!hasParent.contains(i)) { + root = i; + break; + } + } + visit = new HashSet<>(); + return dfs(root, leftChild, rightChild) && visit.size() == n; + } + + private boolean dfs(int i, int[] leftChild, int[] rightChild) { + if (i == -1) return true; + if (visit.contains(i)) return false; + visit.add(i); + return dfs(leftChild[i], leftChild, rightChild) && + dfs(rightChild[i], leftChild, rightChild); + } +} +``` + +```cpp +class Solution { +public: + unordered_set visit; + + bool validateBinaryTreeNodes(int n, vector& leftChild, vector& rightChild) { + unordered_set hasParent; + for (int c : leftChild) if (c != -1) hasParent.insert(c); + for (int c : rightChild) if (c != -1) hasParent.insert(c); + if (hasParent.size() == n) return false; + + int root = -1; + for (int i = 0; i < n; i++) { + if (!hasParent.count(i)) { + root = i; + break; + } + } + return dfs(root, leftChild, rightChild) && visit.size() == n; + } + +private: + bool dfs(int i, vector& leftChild, vector& rightChild) { + if (i == -1) return true; + if (visit.count(i)) return false; + visit.insert(i); + return dfs(leftChild[i], leftChild, rightChild) && + dfs(rightChild[i], leftChild, rightChild); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number[]} leftChild + * @param {number[]} rightChild + * @return {boolean} + */ + validateBinaryTreeNodes(n, leftChild, rightChild) { + let hasParent = new Set([...leftChild, ...rightChild].filter(c => c !== -1)); + if (hasParent.size === n) return false; + + let root = 0; + for (let i = 0; i < n; i++) { + if (!hasParent.has(i)) { + root = i; + break; + } + } + + const visit = new Set(); + const dfs = (i) => { + if (i === -1) return true; + if (visit.has(i)) return false; + visit.add(i); + return dfs(leftChild[i]) && dfs(rightChild[i]); + }; + + return dfs(root) && visit.size === n; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Breadth First Search + +::tabs-start + +```python +class Solution: + def validateBinaryTreeNodes(self, n: int, leftChild: list[int], rightChild: list[int]) -> bool: + indegree = [0] * n + for i in range(n): + if leftChild[i] != -1: + indegree[leftChild[i]] += 1 + if indegree[leftChild[i]] > 1: + return False + if rightChild[i] != -1: + indegree[rightChild[i]] += 1 + if indegree[rightChild[i]] > 1: + return False + + root = -1 + for i in range(n): + if indegree[i] == 0: + if root != -1: + return False + root = i + + if root == -1: + return False + + count = 0 + q = deque([root]) + while q: + i = q.popleft() + count += 1 + if leftChild[i] != -1: + q.append(leftChild[i]) + if rightChild[i] != -1: + q.append(rightChild[i]) + return count == n +``` + +```java +public class Solution { + public boolean validateBinaryTreeNodes(int n, int[] leftChild, int[] rightChild) { + int[] indegree = new int[n]; + for (int i = 0; i < n; i++) { + if (leftChild[i] != -1) { + if (++indegree[leftChild[i]] > 1) return false; + } + if (rightChild[i] != -1) { + if (++indegree[rightChild[i]] > 1) return false; + } + } + + int root = -1; + for (int i = 0; i < n; i++) { + if (indegree[i] == 0) { + if (root != -1) return false; + root = i; + } + } + + if (root == -1) return false; + + int count = 0; + Queue q = new LinkedList<>(); + q.offer(root); + + while (!q.isEmpty()) { + int i = q.poll(); + count++; + if (leftChild[i] != -1) q.offer(leftChild[i]); + if (rightChild[i] != -1) q.offer(rightChild[i]); + } + return count == n; + } +} +``` + +```cpp +class Solution { +public: + bool validateBinaryTreeNodes(int n, vector& leftChild, vector& rightChild) { + vector indegree(n, 0); + for (int i = 0; i < n; i++) { + if (leftChild[i] != -1) { + if (++indegree[leftChild[i]] > 1) return false; + } + if (rightChild[i] != -1) { + if (++indegree[rightChild[i]] > 1) return false; + } + } + + int root = -1; + for (int i = 0; i < n; i++) { + if (indegree[i] == 0) { + if (root != -1) return false; + root = i; + } + } + + if (root == -1) return false; + + int count = 0; + queue q; + q.push(root); + + while (!q.empty()) { + int i = q.front();q.pop(); + count++; + if (leftChild[i] != -1) q.push(leftChild[i]); + if (rightChild[i] != -1) q.push(rightChild[i]); + } + return count == n; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number[]} leftChild + * @param {number[]} rightChild + * @return {boolean} + */ + validateBinaryTreeNodes(n, leftChild, rightChild) { + let indegree = new Array(n).fill(0); + for (let i = 0; i < n; i++) { + if (leftChild[i] !== -1) { + if (++indegree[leftChild[i]] > 1) return false; + } + if (rightChild[i] !== -1) { + if (++indegree[rightChild[i]] > 1) return false; + } + } + + let root = -1; + for (let i = 0; i < n; i++) { + if (indegree[i] === 0) { + if (root !== -1) return false; + root = i; + } + } + + if (root === -1) return false; + + let count = 0; + let q = new Queue([root]); + + while (!q.isEmpty()) { + let i = q.pop(); + count++; + if (leftChild[i] !== -1) q.push(leftChild[i]); + if (rightChild[i] !== -1) q.push(rightChild[i]); + } + return count === n; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Iterative DFS + +::tabs-start + +```python +class Solution: + def validateBinaryTreeNodes(self, n: int, leftChild: list[int], rightChild: list[int]) -> bool: + indegree = [0] * n + for i in range(n): + if leftChild[i] != -1: + indegree[leftChild[i]] += 1 + if indegree[leftChild[i]] > 1: + return False + if rightChild[i] != -1: + indegree[rightChild[i]] += 1 + if indegree[rightChild[i]] > 1: + return False + + root = next((i for i in range(n) if indegree[i] == 0), -1) + if root == -1: + return False + + count, stack = 0, [root] + while stack: + node = stack.pop() + count += 1 + if leftChild[node] != -1: + stack.append(leftChild[node]) + if rightChild[node] != -1: + stack.append(rightChild[node]) + + return count == n +``` + +```java +public class Solution { + public boolean validateBinaryTreeNodes(int n, int[] leftChild, int[] rightChild) { + int[] indegree = new int[n]; + for (int i = 0; i < n; i++) { + if (leftChild[i] != -1) { + if (++indegree[leftChild[i]] > 1) return false; + } + if (rightChild[i] != -1) { + if (++indegree[rightChild[i]] > 1) return false; + } + } + + int root = -1; + for (int i = 0; i < n; i++) { + if (indegree[i] == 0) { + if (root != -1) return false; + root = i; + } + } + + if (root == -1) return false; + + int count = 0; + Stack stack = new Stack<>(); + stack.push(root); + + while (!stack.isEmpty()) { + int node = stack.pop(); + count++; + if (leftChild[node] != -1) stack.push(leftChild[node]); + if (rightChild[node] != -1) stack.push(rightChild[node]); + } + return count == n; + } +} +``` + +```cpp +class Solution { +public: + bool validateBinaryTreeNodes(int n, vector& leftChild, vector& rightChild) { + vector indegree(n, 0); + for (int i = 0; i < n; i++) { + if (leftChild[i] != -1 && ++indegree[leftChild[i]] > 1) return false; + if (rightChild[i] != -1 && ++indegree[rightChild[i]] > 1) return false; + } + + int root = -1; + for (int i = 0; i < n; i++) { + if (indegree[i] == 0) { + if (root != -1) return false; + root = i; + } + } + + if (root == -1) return false; + + int count = 0; + stack stk; + stk.push(root); + + while (!stk.empty()) { + int node = stk.top(); stk.pop(); + count++; + if (leftChild[node] != -1) stk.push(leftChild[node]); + if (rightChild[node] != -1) stk.push(rightChild[node]); + } + return count == n; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number[]} leftChild + * @param {number[]} rightChild + * @return {boolean} + */ + validateBinaryTreeNodes(n, leftChild, rightChild) { + let indegree = new Array(n).fill(0); + for (let i = 0; i < n; i++) { + if (leftChild[i] !== -1) { + if (++indegree[leftChild[i]] > 1) return false; + } + if (rightChild[i] !== -1) { + if (++indegree[rightChild[i]] > 1) return false; + } + } + + let root = -1; + for (let i = 0; i < n; i++) { + if (indegree[i] === 0) { + if (root !== -1) return false; + root = i; + } + } + + if (root === -1) return false; + + let count = 0; + let stack = [root]; + + while (stack.length) { + let node = stack.pop(); + count++; + if (leftChild[node] !== -1) stack.push(leftChild[node]); + if (rightChild[node] !== -1) stack.push(rightChild[node]); + } + return count === n; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 4. Disjoint Set Union + +::tabs-start + +```python +class DSU: + def __init__(self, n): + self.Parent = list(range(n)) + self.Components = n + + def find(self, node): + if self.Parent[node] != node: + self.Parent[node] = self.find(self.Parent[node]) + return self.Parent[node] + + def union(self, parent, child): + parentRoot = self.find(parent) + childRoot = self.find(child) + if childRoot != child or parentRoot == childRoot: + return False + + self.Components -= 1 + self.Parent[childRoot] = parentRoot + return True + +class Solution: + def validateBinaryTreeNodes(self, n: int, leftChild: list[int], rightChild: list[int]) -> bool: + dsu = DSU(n) + + for parent in range(n): + for child in (leftChild[parent], rightChild[parent]): + if child == -1: + continue + if not dsu.union(parent, child): + return False + + return dsu.Components == 1 +``` + +```java +class DSU { + int[] Parent; + int Components; + + public DSU(int n) { + Parent = new int[n]; + for (int i = 0; i < n; i++) { + Parent[i] = i; + } + Components = n; + } + + public int find(int node) { + if (Parent[node] != node) { + Parent[node] = find(Parent[node]); + } + return Parent[node]; + } + + public boolean union(int parent, int child) { + int parentRoot = find(parent); + int childRoot = find(child); + if (childRoot != child || parentRoot == childRoot) { + return false; + } + + Components--; + Parent[childRoot] = parentRoot; + return true; + } +} + +class Solution { + public boolean validateBinaryTreeNodes(int n, int[] leftChild, int[] rightChild) { + DSU dsu = new DSU(n); + + for (int parent = 0; parent < n; parent++) { + for (int child : new int[]{leftChild[parent], rightChild[parent]}) { + if (child == -1) continue; + if (!dsu.union(parent, child)) return false; + } + } + + return dsu.Components == 1; + } +} +``` + +```cpp +class DSU { +public: + vector Parent; + int Components; + + DSU(int n) { + Parent.resize(n); + iota(Parent.begin(), Parent.end(), 0); + Components = n; + } + + int find(int node) { + if (Parent[node] != node) { + Parent[node] = find(Parent[node]); + } + return Parent[node]; + } + + bool unionSets(int parent, int child) { + int parentRoot = find(parent); + int childRoot = find(child); + if (childRoot != child || parentRoot == childRoot) { + return false; + } + + Components--; + Parent[childRoot] = parentRoot; + return true; + } +}; + +class Solution { +public: + bool validateBinaryTreeNodes(int n, vector& leftChild, vector& rightChild) { + DSU dsu(n); + + for (int parent = 0; parent < n; parent++) { + for (int child : {leftChild[parent], rightChild[parent]}) { + if (child == -1) continue; + if (!dsu.unionSets(parent, child)) return false; + } + } + + return dsu.Components == 1; + } +}; +``` + +```javascript +class DSU { + /** + * @constructor + * @param {number} n + */ + constructor(n) { + this.Parent = Array.from({ length: n }, (_, i) => i); + this.Components = n; + } + + /** + * @param {number} node + * @return {number} + */ + find(node) { + if (this.Parent[node] !== node) { + this.Parent[node] = this.find(this.Parent[node]); + } + return this.Parent[node]; + } + + /** + * @param {number} parent + * @param {number} child + * @return {boolean} + */ + union(parent, child) { + let parentRoot = this.find(parent); + let childRoot = this.find(child); + if (childRoot !== child || parentRoot === childRoot) { + return false; + } + + this.Components--; + this.Parent[childRoot] = parentRoot; + return true; + } +} + +class Solution { + /** + * @param {number} n + * @param {number[]} leftChild + * @param {number[]} rightChild + * @return {boolean} + */ + validateBinaryTreeNodes(n, leftChild, rightChild) { + let dsu = new DSU(n); + + for (let parent = 0; parent < n; parent++) { + for (let child of [leftChild[parent], rightChild[parent]]) { + if (child === -1) continue; + if (!dsu.union(parent, child)) return false; + } + } + + return dsu.Components === 1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ \ No newline at end of file From 803157ab030856606e57cc831bb080f9e96016ad Mon Sep 17 00:00:00 2001 From: neetcode-gh <77742485+neetcode-gh@users.noreply.github.com> Date: Sun, 2 Feb 2025 16:38:30 -0800 Subject: [PATCH 33/45] Update longest-common-prefix.md --- articles/longest-common-prefix.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/articles/longest-common-prefix.md b/articles/longest-common-prefix.md index 27c30160f..e5941f9a6 100644 --- a/articles/longest-common-prefix.md +++ b/articles/longest-common-prefix.md @@ -83,7 +83,7 @@ class Solution { ### Time & Space Complexity * Time complexity: $O(n * m)$ -* Space complexity: $O(n)$ +* Space complexity: $O(1)$ > Where $n$ is the length of the shortest string and $m$ is the number of strings. @@ -500,4 +500,4 @@ class Solution { * Time complexity: $O(n * m)$ * Space complexity: $O(n)$ -> Where $n$ is the length of the shortest string and $m$ is the number of strings. \ No newline at end of file +> Where $n$ is the length of the shortest string and $m$ is the number of strings. From b0e7b545b75f7fe0904b3447c49291fa97775f4d Mon Sep 17 00:00:00 2001 From: Sri Hari <94112314+Srihari2222@users.noreply.github.com> Date: Fri, 14 Feb 2025 19:04:57 +0530 Subject: [PATCH 34/45] Sri Hari: Batch-5/Neetcode-ALL/Added-articles (#3841) * Batch-5/Neetcode-ALL/Added-articles * Batch-5/Neetcode-ALL/Added-articles * Batch-5/Neetcode-ALL/Added-articles * Batch-5/Neetcode-ALL/Added-articles * Batch-5/Neetcode-ALL/Added-articles * Batch-5/Neetcode-ALL/Added-articles * Batch-5/Neetcode-ALL/Added-articles * Batch-5/Neetcode-ALL/Added-articles * Batch-5/Neetcode-ALL/Added-articles * Batch-5/Neetcode-ALL/Added-articles * Batch-5/Neetcode-ALL/Added-articles * Batch-5/Neetcode-ALL/Added-articles * Batch-5/Neetcode-ALL/Added-articles --- articles/all-possible-full-binary-trees.md | 693 +++++++++++ articles/binary-search-tree-iterator.md | 699 +++++++++++ articles/concatenated-words.md | 587 +++++++++ ...t-all-valid-pickup-and-delivery-options.md | 668 +++++++++++ articles/data-stream-as-disjoint-intervals.md | 450 +++++++ articles/design-browser-history.md | 640 ++++++++++ articles/detonate-the-maximum-bombs.md | 522 ++++++++ articles/distribute-coins-in-binary-tree.md | 723 +++++++++++ .../eliminate-maximum-number-of-monsters.md | 298 +++++ articles/find-all-people-with-secret.md | 834 +++++++++++++ .../find-the-maximum-sum-of-node-values.md | 637 ++++++++++ articles/find-the-safest-path-in-a-grid.md | 1054 +++++++++++++++++ articles/find-unique-binary-string.md | 685 +++++++++++ articles/furthest-building-you-can-reach.md | 723 +++++++++++ articles/integer-to-roman.md | 195 +++ articles/is-graph-bipartite.md | 611 ++++++++++ articles/knight-dialer.md | 946 +++++++++++++++ ...largest-color-value-in-a-directed-graph.md | 589 +++++++++ articles/largest-divisible-subset.md | 738 ++++++++++++ .../largest-submatrix-with-rearrangements.md | 522 ++++++++ articles/maximize-score-after-n-operations.md | 629 ++++++++++ ...lement-after-decreasing-and-rearranging.md | 158 +++ ...catenated-string-with-unique-characters.md | 857 ++++++++++++++ articles/maximum-length-of-pair-chain.md | 556 +++++++++ ...aximum-nesting-depth-of-the-parentheses.md | 305 +++++ ...aximum-points-you-can-obtain-from-cards.md | 462 ++++++++ articles/maximum-score-of-a-good-subarray.md | 729 ++++++++++++ .../maximum-score-words-formed-by-letters.md | 676 +++++++++++ articles/minimize-maximum-of-array.md | 210 ++++ ...ns-to-make-character-frequencies-unique.md | 348 ++++++ ...imum-number-of-arrows-to-burst-balloons.md | 183 +++ ...minimum-number-of-days-to-eat-n-oranges.md | 354 ++++++ ...m-number-of-vertices-to-reach-all-nodes.md | 465 ++++++++ ...ne-bit-operations-to-make-integers-zero.md | 313 +++++ articles/number-of-good-paths.md | 861 ++++++++++++++ articles/number-of-laser-beams-in-a-bank.md | 110 ++ ...umber-of-ways-to-divide-a-long-corridor.md | 608 ++++++++++ articles/operations-on-tree.md | 806 +++++++++++++ articles/parallel-courses-iii.md | 481 ++++++++ articles/partition-array-for-maximum-sum.md | 446 +++++++ articles/path-with-maximum-gold.md | 508 ++++++++ articles/path-with-maximum-probability.md | 632 ++++++++++ ...es-if-both-neighbors-are-the-same-color.md | 322 +++++ articles/remove-covered-intervals.md | 275 +++++ ...f-edges-to-keep-graph-fully-traversable.md | 274 +++++ articles/reveal-cards-in-increasing-order.md | 406 +++++++ articles/rotate-list.md | 486 ++++++++ articles/score-after-flipping-matrix.md | 253 ++++ ...ring-into-descending-consecutive-values.md | 570 +++++++++ articles/the-number-of-beautiful-subsets.md | 739 ++++++++++++ articles/two-city-scheduling.md | 683 +++++++++++ ...between-two-points-containing-no-points.md | 212 ++++ 52 files changed, 27731 insertions(+) create mode 100644 articles/all-possible-full-binary-trees.md create mode 100644 articles/binary-search-tree-iterator.md create mode 100644 articles/concatenated-words.md create mode 100644 articles/count-all-valid-pickup-and-delivery-options.md create mode 100644 articles/data-stream-as-disjoint-intervals.md create mode 100644 articles/design-browser-history.md create mode 100644 articles/detonate-the-maximum-bombs.md create mode 100644 articles/distribute-coins-in-binary-tree.md create mode 100644 articles/eliminate-maximum-number-of-monsters.md create mode 100644 articles/find-all-people-with-secret.md create mode 100644 articles/find-the-maximum-sum-of-node-values.md create mode 100644 articles/find-the-safest-path-in-a-grid.md create mode 100644 articles/find-unique-binary-string.md create mode 100644 articles/furthest-building-you-can-reach.md create mode 100644 articles/integer-to-roman.md create mode 100644 articles/is-graph-bipartite.md create mode 100644 articles/knight-dialer.md create mode 100644 articles/largest-color-value-in-a-directed-graph.md create mode 100644 articles/largest-divisible-subset.md create mode 100644 articles/largest-submatrix-with-rearrangements.md create mode 100644 articles/maximize-score-after-n-operations.md create mode 100644 articles/maximum-element-after-decreasing-and-rearranging.md create mode 100644 articles/maximum-length-of-a-concatenated-string-with-unique-characters.md create mode 100644 articles/maximum-length-of-pair-chain.md create mode 100644 articles/maximum-nesting-depth-of-the-parentheses.md create mode 100644 articles/maximum-points-you-can-obtain-from-cards.md create mode 100644 articles/maximum-score-of-a-good-subarray.md create mode 100644 articles/maximum-score-words-formed-by-letters.md create mode 100644 articles/minimize-maximum-of-array.md create mode 100644 articles/minimum-deletions-to-make-character-frequencies-unique.md create mode 100644 articles/minimum-number-of-arrows-to-burst-balloons.md create mode 100644 articles/minimum-number-of-days-to-eat-n-oranges.md create mode 100644 articles/minimum-number-of-vertices-to-reach-all-nodes.md create mode 100644 articles/minimum-one-bit-operations-to-make-integers-zero.md create mode 100644 articles/number-of-good-paths.md create mode 100644 articles/number-of-laser-beams-in-a-bank.md create mode 100644 articles/number-of-ways-to-divide-a-long-corridor.md create mode 100644 articles/operations-on-tree.md create mode 100644 articles/parallel-courses-iii.md create mode 100644 articles/partition-array-for-maximum-sum.md create mode 100644 articles/path-with-maximum-gold.md create mode 100644 articles/path-with-maximum-probability.md create mode 100644 articles/remove-colored-pieces-if-both-neighbors-are-the-same-color.md create mode 100644 articles/remove-covered-intervals.md create mode 100644 articles/remove-max-number-of-edges-to-keep-graph-fully-traversable.md create mode 100644 articles/reveal-cards-in-increasing-order.md create mode 100644 articles/rotate-list.md create mode 100644 articles/score-after-flipping-matrix.md create mode 100644 articles/splitting-a-string-into-descending-consecutive-values.md create mode 100644 articles/the-number-of-beautiful-subsets.md create mode 100644 articles/two-city-scheduling.md create mode 100644 articles/widest-vertical-area-between-two-points-containing-no-points.md diff --git a/articles/all-possible-full-binary-trees.md b/articles/all-possible-full-binary-trees.md new file mode 100644 index 000000000..1e75b0293 --- /dev/null +++ b/articles/all-possible-full-binary-trees.md @@ -0,0 +1,693 @@ +## 1. Recursion + +::tabs-start + +```python +# 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 +class Solution: + def allPossibleFBT(self, n: int) -> List[Optional[TreeNode]]: + def backtrack(n): + if n == 0: + return [] + if n == 1: + return [TreeNode(0)] + + res = [] + for l in range(n): + r = n - 1 - l + leftTrees, rightTrees = backtrack(l), backtrack(r) + + for t1 in leftTrees: + for t2 in rightTrees: + res.append(TreeNode(0, t1, t2)) + return res + + return backtrack(n) +``` + +```java +/** + * 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; + * } + * } + */ +public class Solution { + public List allPossibleFBT(int n) { + return backtrack(n); + } + + private List backtrack(int n) { + if (n == 0) { + return new ArrayList<>(); + } + if (n == 1) { + return Arrays.asList(new TreeNode(0)); + } + + List res = new ArrayList<>(); + for (int l = 0; l < n; l++) { + int r = n - 1 - l; + List leftTrees = backtrack(l); + List rightTrees = backtrack(r); + + for (TreeNode t1 : leftTrees) { + for (TreeNode t2 : rightTrees) { + res.add(new TreeNode(0, t1, t2)); + } + } + } + return res; + } +} +``` + +```cpp +/** + * 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: + vector allPossibleFBT(int n) { + return backtrack(n); + } + +private: + vector backtrack(int n) { + if (n == 0) { + return {}; + } + if (n == 1) { + return {new TreeNode(0)}; + } + + vector res; + for (int l = 0; l < n; l++) { + int r = n - 1 - l; + vector leftTrees = backtrack(l); + vector rightTrees = backtrack(r); + + for (auto& t1 : leftTrees) { + for (auto& t2 : rightTrees) { + res.push_back(new TreeNode(0, t1, t2)); + } + } + } + return res; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {number} n + * @return {TreeNode[]} + */ + allPossibleFBT(n) { + const backtrack = (n) => { + if (n === 0) { + return []; + } + if (n === 1) { + return [new TreeNode(0)]; + } + + let res = []; + for (let l = 0; l < n; l++) { + let r = n - 1 - l; + let leftTrees = backtrack(l); + let rightTrees = backtrack(r); + + for (let t1 of leftTrees) { + for (let t2 of rightTrees) { + res.push(new TreeNode(0, t1, t2)); + } + } + } + return res; + }; + + return backtrack(n); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(2 ^ n)$ +* Space complexity: $O(n * 2 ^ n)$ + +--- + +## 2. Recursion (Optimal) + +::tabs-start + +```python +# 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 +class Solution: + def allPossibleFBT(self, n: int) -> List[Optional[TreeNode]]: + if n % 2 == 0: + return [] + if n == 1: + return [TreeNode(0)] + + res = [] + for left in range(1, n, 2): + leftSubTree = self.allPossibleFBT(left) + rightSubTree = self.allPossibleFBT(n - 1 - left) + for l in leftSubTree: + for r in rightSubTree: + root = TreeNode(0, l, r) + res.append(root) + return res +``` + +```java +/** + * 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; + * } + * } + */ +public class Solution { + public List allPossibleFBT(int n) { + if (n % 2 == 0) { + return new ArrayList<>(); + } + if (n == 1) { + return Arrays.asList(new TreeNode(0)); + } + + List res = new ArrayList<>(); + for (int left = 1; left < n; left += 2) { + List leftSubTree = allPossibleFBT(left); + List rightSubTree = allPossibleFBT(n - 1 - left); + for (TreeNode l : leftSubTree) { + for (TreeNode r : rightSubTree) { + TreeNode root = new TreeNode(0, l, r); + res.add(root); + } + } + } + return res; + } +} +``` + +```cpp +/** + * 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: + vector allPossibleFBT(int n) { + if (n % 2 == 0) { + return {}; + } + if (n == 1) { + return {new TreeNode(0)}; + } + + vector res; + for (int left = 1; left < n; left += 2) { + vector leftSubTree = allPossibleFBT(left); + vector rightSubTree = allPossibleFBT(n - 1 - left); + for (auto& l : leftSubTree) { + for (auto& r : rightSubTree) { + TreeNode* root = new TreeNode(0, l, r); + res.push_back(root); + } + } + } + return res; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {number} n + * @return {TreeNode[]} + */ + allPossibleFBT(n) { + if (n % 2 === 0) { + return []; + } + if (n === 1) { + return [new TreeNode(0)]; + } + + let res = []; + for (let left = 1; left < n; left += 2) { + let leftSubTree = this.allPossibleFBT(left); + let rightSubTree = this.allPossibleFBT(n - 1 - left); + for (let l of leftSubTree) { + for (let r of rightSubTree) { + let root = new TreeNode(0, l, r); + res.push(root); + } + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(2 ^ n)$ +* Space complexity: $O(n * 2 ^ n)$ + +--- + +## 3. Dynamic Programming (Top-Down) + +::tabs-start + +```python +# 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 +class Solution: + def allPossibleFBT(self, n: int) -> List[Optional[TreeNode]]: + dp = {} + + def dfs(n): + if n % 2 == 0: + return [] + if n == 1: + return [TreeNode(0)] + if n in dp: + return dp[n] + + res = [] + for left in range(1, n, 2): + leftSubTree = dfs(left) + rightSubTree = dfs(n - 1 - left) + for l in leftSubTree: + for r in rightSubTree: + res.append(TreeNode(0, l, r)) + + dp[n] = res + return res + + return dfs(n) +``` + +```java +/** + * 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; + * } + * } + */ +public class Solution { + private List[] dp; + + public List allPossibleFBT(int n) { + dp = new ArrayList[n + 1]; + return dfs(n); + } + + private List dfs(int n) { + if (n % 2 == 0) { + return new ArrayList<>(); + } + if (n == 1) { + return Arrays.asList(new TreeNode(0)); + } + if (dp[n] != null) { + return dp[n]; + } + + List res = new ArrayList<>(); + for (int left = 1; left < n; left += 2) { + List leftSubTree = dfs(left); + List rightSubTree = dfs(n - 1 - left); + for (TreeNode l : leftSubTree) { + for (TreeNode r : rightSubTree) { + res.add(new TreeNode(0, l, r)); + } + } + } + + return dp[n] = res; + } +} +``` + +```cpp +/** + * 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 { +private: + vector> dp; + +public: + vector allPossibleFBT(int n) { + dp.resize(n + 1); + return dfs(n); + } + + vector dfs(int n) { + if (n % 2 == 0) { + return {}; + } + if (n == 1) { + return {new TreeNode(0)}; + } + if (!dp[n].empty()) { + return dp[n]; + } + + vector res; + for (int left = 1; left < n; left += 2) { + vector leftSubTree = dfs(left); + vector rightSubTree = dfs(n - 1 - left); + for (auto& l : leftSubTree) { + for (auto& r : rightSubTree) { + res.push_back(new TreeNode(0, l, r)); + } + } + } + + return dp[n] = res; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {number} n + * @return {TreeNode[]} + */ + allPossibleFBT(n) { + let dp = new Array(n + 1); + + const dfs = (n) => { + if (n % 2 === 0) { + return []; + } + if (n === 1) { + return [new TreeNode(0)]; + } + if (dp[n]) { + return dp[n]; + } + + let res = []; + for (let left = 1; left < n; left += 2) { + let leftSubTree = dfs(left); + let rightSubTree = dfs(n - 1 - left); + for (let t1 of leftSubTree) { + for (let t2 of rightSubTree) { + res.push(new TreeNode(0, t1, t2)); + } + } + } + + return (dp[n] = res); + }; + + return dfs(n); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(2 ^ n)$ +* Space complexity: $O(n * 2 ^ n)$ + +--- + +## 4. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +# 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 +class Solution: + def allPossibleFBT(self, n: int) -> List[Optional[TreeNode]]: + if n % 2 == 0: + return [] + + dp = [[] for _ in range(n + 1)] + dp[1] = [TreeNode(0)] + + for nodes in range(3, n + 1, 2): + res = [] + for left in range(1, nodes, 2): + right = nodes - 1 - left + for t1 in dp[left]: + for t2 in dp[right]: + res.append(TreeNode(0, t1, t2)) + dp[nodes] = res + + return dp[n] +``` + +```java +/** + * 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; + * } + * } + */ +public class Solution { + public List allPossibleFBT(int n) { + if (n % 2 == 0) { + return new ArrayList<>(); + } + + List[] dp = new ArrayList[n + 1]; + for (int i = 0; i <= n; i++) { + dp[i] = new ArrayList<>(); + } + dp[1].add(new TreeNode(0)); + + for (int nodes = 3; nodes <= n; nodes += 2) { + List res = new ArrayList<>(); + for (int left = 1; left < nodes; left += 2) { + int right = nodes - 1 - left; + for (TreeNode t1 : dp[left]) { + for (TreeNode t2 : dp[right]) { + res.add(new TreeNode(0, t1, t2)); + } + } + } + dp[nodes] = res; + } + + return dp[n]; + } +} +``` + +```cpp +/** + * 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: + vector allPossibleFBT(int n) { + if (n % 2 == 0) { + return {}; + } + + vector> dp(n + 1); + dp[1].push_back(new TreeNode(0)); + + for (int nodes = 3; nodes <= n; nodes += 2) { + vector res; + for (int left = 1; left < nodes; left += 2) { + int right = nodes - 1 - left; + for (auto& t1 : dp[left]) { + for (auto& t2 : dp[right]) { + res.push_back(new TreeNode(0, t1, t2)); + } + } + } + dp[nodes] = res; + } + + return dp[n]; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {number} n + * @return {TreeNode[]} + */ + allPossibleFBT(n) { + if (n % 2 === 0) { + return []; + } + + let dp = Array.from({ length: n + 1 }, () => []); + dp[1] = [new TreeNode(0)]; + + for (let nodes = 3; nodes <= n; nodes += 2) { + let res = []; + for (let left = 1; left < nodes; left += 2) { + let right = nodes - 1 - left; + for (let t1 of dp[left]) { + for (let t2 of dp[right]) { + res.push(new TreeNode(0, t1, t2)); + } + } + } + dp[nodes] = res; + } + + return dp[n]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(2 ^ n)$ +* Space complexity: $O(n * 2 ^ n)$ \ No newline at end of file diff --git a/articles/binary-search-tree-iterator.md b/articles/binary-search-tree-iterator.md new file mode 100644 index 000000000..873698c70 --- /dev/null +++ b/articles/binary-search-tree-iterator.md @@ -0,0 +1,699 @@ +## 1. Flattening the BST (DFS) + +::tabs-start + +```python +# 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 +class BSTIterator: + + def __init__(self, root: Optional[TreeNode]): + self.arr = [] + self.itr = 0 + + def dfs(node): + if not node: + return + dfs(node.left) + self.arr.append(node.val) + dfs(node.right) + + dfs(root) + + def next(self) -> int: + val = self.arr[self.itr] + self.itr += 1 + return val + + def hasNext(self) -> bool: + return self.itr < len(self.arr) +``` + +```java +/** + * 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; + * } + * } + */ +public class BSTIterator { + private List arr; + private int itr; + + public BSTIterator(TreeNode root) { + arr = new ArrayList<>(); + itr = 0; + dfs(root); + } + + private void dfs(TreeNode node) { + if (node == null) { + return; + } + dfs(node.left); + arr.add(node.val); + dfs(node.right); + } + + public int next() { + return arr.get(itr++); + } + + public boolean hasNext() { + return itr < arr.size(); + } +} +``` + +```cpp +/** + * 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 BSTIterator { +private: + vector arr; + int itr; + + void dfs(TreeNode* node) { + if (!node) { + return; + } + dfs(node->left); + arr.push_back(node->val); + dfs(node->right); + } + +public: + BSTIterator(TreeNode* root) { + itr = 0; + dfs(root); + } + + int next() { + return arr[itr++]; + } + + bool hasNext() { + return itr < arr.size(); + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class BSTIterator { + /** + * @constructor + * @param {TreeNode} root + */ + constructor(root) { + this.arr = []; + this.itr = 0; + + const dfs = (node) => { + if (!node) { + return; + } + dfs(node.left); + this.arr.push(node.val); + dfs(node.right); + }; + + dfs(root); + } + + /** + * @return {number} + */ + next() { + return this.arr[this.itr++]; + } + + /** + * @return {boolean} + */ + hasNext() { + return this.itr < this.arr.length; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: + * $O(n)$ time for initialization. + * $O(n)$ time for each $next()$ and $hasNext()$ function calls. +* Space complexity: $O(n)$ + +--- + +## 2. Flatten the BST (Iterative DFS) + +::tabs-start + +```python +# 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 +class BSTIterator: + + def __init__(self, root: Optional[TreeNode]): + self.arr = [] + self.itr = 0 + + stack = [] + while root or stack: + while root: + stack.append(root) + root = root.left + root = stack.pop() + self.arr.append(root.val) + root = root.right + + def next(self) -> int: + val = self.arr[self.itr] + self.itr += 1 + return val + + def hasNext(self) -> bool: + return self.itr < len(self.arr) +``` + +```java +/** + * 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; + * } + * } + */ +public class BSTIterator { + private List arr; + private int itr; + + public BSTIterator(TreeNode root) { + arr = new ArrayList<>(); + itr = 0; + Stack stack = new Stack<>(); + while (root != null || !stack.isEmpty()) { + while (root != null) { + stack.push(root); + root = root.left; + } + root = stack.pop(); + arr.add(root.val); + root = root.right; + } + } + + public int next() { + return arr.get(itr++); + } + + public boolean hasNext() { + return itr < arr.size(); + } +} +``` + +```cpp +/** + * 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 BSTIterator { +private: + vector arr; + int itr; + +public: + BSTIterator(TreeNode* root) { + itr = 0; + stack stack; + while (root || !stack.empty()) { + while (root) { + stack.push(root); + root = root->left; + } + root = stack.top(); + stack.pop(); + arr.push_back(root->val); + root = root->right; + } + } + + int next() { + return arr[itr++]; + } + + bool hasNext() { + return itr < arr.size(); + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class BSTIterator { + /** + * @constructor + * @param {TreeNode} root + */ + constructor(root) { + this.arr = []; + this.itr = 0; + + let stack = []; + while (root || stack.length) { + while (root) { + stack.push(root); + root = root.left; + } + root = stack.pop(); + this.arr.push(root.val); + root = root.right; + } + } + + /** + * @return {number} + */ + next() { + return this.arr[this.itr++]; + } + + /** + * @return {boolean} + */ + hasNext() { + return this.itr < this.arr.length; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: + * $O(n)$ time for initialization. + * $O(n)$ time for each $next()$ and $hasNext()$ function calls. +* Space complexity: $O(n)$ + +--- + +## 3. Iterative DFS - I + +::tabs-start + +```python +# 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 +class BSTIterator: + + def __init__(self, root: Optional[TreeNode]): + self.stack = [] + while root: + self.stack.append(root) + root = root.left + + def next(self) -> int: + res = self.stack.pop() + cur = res.right + while cur: + self.stack.append(cur) + cur = cur.left + return res.val + + def hasNext(self) -> bool: + return bool(self.stack) +``` + +```java +/** + * 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; + * } + * } + */ +public class BSTIterator { + private Stack stack; + + public BSTIterator(TreeNode root) { + stack = new Stack<>(); + while (root != null) { + stack.push(root); + root = root.left; + } + } + + public int next() { + TreeNode res = stack.pop(); + TreeNode cur = res.right; + while (cur != null) { + stack.push(cur); + cur = cur.left; + } + return res.val; + } + + public boolean hasNext() { + return !stack.isEmpty(); + } +} +``` + +```cpp +/** + * 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 BSTIterator { +private: + stack stack; + +public: + BSTIterator(TreeNode* root) { + while (root) { + stack.push(root); + root = root->left; + } + } + + int next() { + TreeNode* res = stack.top(); + stack.pop(); + TreeNode* cur = res->right; + while (cur) { + stack.push(cur); + cur = cur->left; + } + return res->val; + } + + bool hasNext() { + return !stack.empty(); + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class BSTIterator { + /** + * @constructor + * @param {TreeNode} root + */ + constructor(root) { + this.stack = []; + while (root) { + this.stack.push(root); + root = root.left; + } + } + + /** + * @return {number} + */ + next() { + let res = this.stack.pop(); + let cur = res.right; + while (cur) { + this.stack.push(cur); + cur = cur.left; + } + return res.val; + } + + /** + * @return {boolean} + */ + hasNext() { + return this.stack.length > 0; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(1)$ in average for each function call. +* Space complexity: $O(h)$ + +> Where $n$ is the number of nodes and $h$ is the height of the given tree. + +--- + +## 4. Iterative DFS - II + +::tabs-start + +```python +# 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 +class BSTIterator: + + def __init__(self, root: Optional[TreeNode]): + self.cur = root + self.stack = [] + + def next(self) -> int: + while self.cur: + self.stack.append(self.cur) + self.cur = self.cur.left + + node = self.stack.pop() + self.cur = node.right + return node.val + + def hasNext(self) -> bool: + return bool(self.cur) or bool(self.stack) +``` + +```java +/** + * 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; + * } + * } + */ +public class BSTIterator { + private TreeNode cur; + private Stack stack; + + public BSTIterator(TreeNode root) { + cur = root; + stack = new Stack<>(); + } + + public int next() { + while (cur != null) { + stack.push(cur); + cur = cur.left; + } + + TreeNode node = stack.pop(); + cur = node.right; + return node.val; + } + + public boolean hasNext() { + return cur != null || !stack.isEmpty(); + } +} +``` + +```cpp +/** + * 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 BSTIterator { +private: + TreeNode* cur; + stack stack; + +public: + BSTIterator(TreeNode* root) { + cur = root; + } + + int next() { + while (cur) { + stack.push(cur); + cur = cur->left; + } + + TreeNode* node = stack.top(); + stack.pop(); + cur = node->right; + return node->val; + } + + bool hasNext() { + return cur || !stack.empty(); + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class BSTIterator { + /** + * @constructor + * @param {TreeNode} root + */ + constructor(root) { + this.cur = root; + this.stack = []; + } + + /** + * @return {number} + */ + next() { + while (this.cur) { + this.stack.push(this.cur); + this.cur = this.cur.left; + } + + let node = this.stack.pop(); + this.cur = node.right; + return node.val; + } + + /** + * @return {boolean} + */ + hasNext() { + return this.cur !== null || this.stack.length > 0; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(1)$ in average for each function call. +* Space complexity: $O(h)$ + +> Where $n$ is the number of nodes and $h$ is the height of the given tree. \ No newline at end of file diff --git a/articles/concatenated-words.md b/articles/concatenated-words.md new file mode 100644 index 000000000..0d7eaf0d4 --- /dev/null +++ b/articles/concatenated-words.md @@ -0,0 +1,587 @@ +## 1. Brute Force (Backtracking) + +::tabs-start + +```python +class Solution: + def findAllConcatenatedWordsInADict(self, words: List[str]) -> List[str]: + n = len(words) + res = [] + wordSet = set(words) + maxLen = 0 + for w in words: + maxLen = max(maxLen, len(w)) + + def dfs(concatWord, totLen): + if len(concatWord) > 1: + word = "".join(concatWord) + if word in wordSet: + res.append(word) + wordSet.remove(word) + + for i in range(len(words)): + if totLen + len(words[i]) > maxLen: + continue + concatWord.append(words[i]) + dfs(concatWord, totLen + len(words[i])) + concatWord.pop() + + dfs([], 0) + return res +``` + +```java +public class Solution { + private Set wordSet; + private int maxLen; + private List res; + private String[] words; + + public List findAllConcatenatedWordsInADict(String[] words) { + this.words = words; + this.wordSet = new HashSet<>(Arrays.asList(words)); + this.maxLen = 0; + this.res = new ArrayList<>(); + + for (String w : words) { + maxLen = Math.max(maxLen, w.length()); + } + + dfs(new ArrayList<>(), 0); + return res; + } + + private void dfs(List concatWord, int totLen) { + if (concatWord.size() > 1) { + String word = String.join("", concatWord); + if (wordSet.contains(word)) { + res.add(word); + wordSet.remove(word); + } + } + + for (String word : words) { + if (totLen + word.length() > maxLen) continue; + concatWord.add(word); + dfs(concatWord, totLen + word.length()); + concatWord.remove(concatWord.size() - 1); + } + } +} +``` + +```cpp +class Solution { +private: + unordered_set wordSet; + int maxLen; + vector res; + vector words; + +public: + vector findAllConcatenatedWordsInADict(vector& words) { + this->words = words; + wordSet = unordered_set(words.begin(), words.end()); + maxLen = 0; + res.clear(); + + for (const string& w : words) { + maxLen = max(maxLen, (int)w.length()); + } + + vector concatWord; + dfs(concatWord, 0); + return res; + } + +private: + void dfs(vector& concatWord, int totLen) { + if (concatWord.size() > 1) { + string word = accumulate(concatWord.begin(), concatWord.end(), string("")); + if (wordSet.count(word)) { + res.push_back(word); + wordSet.erase(word); + } + } + + for (const string& word : words) { + if (totLen + word.size() > maxLen) continue; + concatWord.push_back(word); + dfs(concatWord, totLen + word.length()); + concatWord.pop_back(); + } + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} words + * @return {string[]} + */ + findAllConcatenatedWordsInADict(words) { + let n = words.length; + let res = []; + let wordSet = new Set(words); + let maxLen = 0; + + for (let w of words) { + maxLen = Math.max(maxLen, w.length); + } + + const dfs = (concatWord, totLen) => { + if (concatWord.length > 1) { + let word = concatWord.join(""); + if (wordSet.has(word)) { + res.push(word); + wordSet.delete(word); + } + } + + for (let i = 0; i < words.length; i++) { + if (totLen + words[i].length > maxLen) continue; + concatWord.push(words[i]); + dfs(concatWord, totLen + words[i].length); + concatWord.pop(); + } + }; + + dfs([], 0); + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n ^ n)$ +* Space complexity: $O(m * n)$ + +> Where $n$ is the size of the string array $words$ and $m$ is the length of the longest word in the array. + +--- + +## 2. Recursion + +::tabs-start + +```python +class Solution: + def findAllConcatenatedWordsInADict(self, words: List[str]) -> List[str]: + wordSet = set(words) + + def dfs(word): + for i in range(1, len(word)): + prefix, suffix = word[:i], word[i:] + if ((prefix in wordSet and suffix in wordSet) or + (prefix in wordSet and dfs(suffix)) + ): + return True + return False + + res = [] + for w in words: + if dfs(w): + res.append(w) + return res +``` + +```java +public class Solution { + public List findAllConcatenatedWordsInADict(String[] words) { + Set wordSet = new HashSet<>(Arrays.asList(words)); + List res = new ArrayList<>(); + + for (String w : words) { + if (dfs(w, wordSet)) { + res.add(w); + } + } + return res; + } + + private boolean dfs(String word, Set wordSet) { + for (int i = 1; i < word.length(); i++) { + String prefix = word.substring(0, i); + String suffix = word.substring(i); + + if ((wordSet.contains(prefix) && wordSet.contains(suffix)) || + (wordSet.contains(prefix) && dfs(suffix, wordSet))) { + return true; + } + } + return false; + } +} +``` + +```cpp +class Solution { +public: + vector findAllConcatenatedWordsInADict(vector& words) { + unordered_set wordSet(words.begin(), words.end()); + vector res; + + for (const string& w : words) { + if (dfs(w, wordSet)) { + res.push_back(w); + } + } + return res; + } + +private: + bool dfs(const string& word, unordered_set& wordSet) { + for (int i = 1; i < word.size(); i++) { + string prefix = word.substr(0, i); + string suffix = word.substr(i); + + if ((wordSet.count(prefix) && wordSet.count(suffix)) || + (wordSet.count(prefix) && dfs(suffix, wordSet))) { + return true; + } + } + return false; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} words + * @return {string[]} + */ + findAllConcatenatedWordsInADict(words) { + const wordSet = new Set(words); + + const dfs = (word) => { + for (let i = 1; i < word.length; i++) { + const prefix = word.substring(0, i); + const suffix = word.substring(i); + + if ((wordSet.has(prefix) && wordSet.has(suffix)) || + (wordSet.has(prefix) && dfs(suffix))) { + return true; + } + } + return false; + }; + + const res = []; + for (let w of words) { + if (dfs(w)) { + res.push(w); + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * m ^ 4)$ +* Space complexity: $O(n * m)$ + +> Where $n$ is the size of the string array $words$ and $m$ is the length of the longest word in the array. + +--- + +## 3. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def findAllConcatenatedWordsInADict(self, words: List[str]) -> List[str]: + wordSet = set(words) + dp = {} + + def dfs(word): + if word in dp: + return dp[word] + + for i in range(1, len(word)): + prefix, suffix = word[:i], word[i:] + if ((prefix in wordSet and suffix in wordSet) or + (prefix in wordSet and dfs(suffix)) + ): + dp[word] = True + return True + dp[word] = False + return False + + res = [] + for w in words: + if dfs(w): + res.append(w) + return res +``` + +```java +public class Solution { + private Map dp; + + public List findAllConcatenatedWordsInADict(String[] words) { + Set wordSet = new HashSet<>(Arrays.asList(words)); + List res = new ArrayList<>(); + dp = new HashMap<>(); + + for (String w : words) { + if (dfs(w, wordSet)) { + res.add(w); + } + } + return res; + } + + private boolean dfs(String word, Set wordSet) { + if (dp.containsKey(word)) { + return dp.get(word); + } + + for (int i = 1; i < word.length(); i++) { + String prefix = word.substring(0, i); + String suffix = word.substring(i); + + if ((wordSet.contains(prefix) && wordSet.contains(suffix)) || + (wordSet.contains(prefix) && dfs(suffix, wordSet))) { + dp.put(word, true); + return true; + } + } + dp.put(word, false); + return false; + } +} +``` + +```cpp +class Solution { + unordered_map dp; + +public: + vector findAllConcatenatedWordsInADict(vector& words) { + unordered_set wordSet(words.begin(), words.end()); + vector res; + + for (const string& w : words) { + if (dfs(w, wordSet)) { + res.push_back(w); + } + } + return res; + } + +private: + bool dfs(const string& word, unordered_set& wordSet) { + if (dp.count(word)) { + return dp[word]; + } + + for (int i = 1; i < word.size(); i++) { + string prefix = word.substr(0, i); + string suffix = word.substr(i); + + if ((wordSet.count(prefix) && wordSet.count(suffix)) || + (wordSet.count(prefix) && dfs(suffix, wordSet))) { + dp[word] = true; + return true; + } + } + dp[word] = false; + return false; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} words + * @return {string[]} + */ + findAllConcatenatedWordsInADict(words) { + const wordSet = new Set(words); + const dp = new Map(); + + const dfs = (word) => { + if (dp.has(word)) { + return dp.get(word); + } + + for (let i = 1; i < word.length; i++) { + const prefix = word.substring(0, i); + const suffix = word.substring(i); + + if ((wordSet.has(prefix) && wordSet.has(suffix)) || + (wordSet.has(prefix) && dfs(suffix))) { + dp.set(word, true); + return true; + } + } + dp.set(word, false); + return false; + }; + + const res = []; + for (let w of words) { + if (dfs(w)) { + res.push(w); + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * m ^ 3)$ +* Space complexity: $O(n * m)$ + +> Where $n$ is the size of the string array $words$ and $m$ is the length of the longest word in the array. + +--- + +## 4. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def findAllConcatenatedWordsInADict(self, words: List[str]) -> List[str]: + wordSet = set(words) + res = [] + + for word in words: + m = len(word) + + dp = [False] * (m + 1) + dp[0] = True + + for i in range(1, m + 1): + for j in range(i): + if j == 0 and i == m: + continue + if dp[j] and word[j:i] in wordSet: + dp[i] = True + break + + if dp[m]: + res.append(word) + + return res +``` + +```java +public class Solution { + public List findAllConcatenatedWordsInADict(String[] words) { + Set wordSet = new HashSet<>(Arrays.asList(words)); + List res = new ArrayList<>(); + + for (String word : words) { + int m = word.length(); + boolean[] dp = new boolean[m + 1]; + dp[0] = true; + + for (int i = 1; i <= m; i++) { + for (int j = 0; j < i; j++) { + if (j == 0 && i == m) continue; + if (dp[j] && wordSet.contains(word.substring(j, i))) { + dp[i] = true; + break; + } + } + } + + if (dp[m]) { + res.add(word); + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + vector findAllConcatenatedWordsInADict(vector& words) { + unordered_set wordSet(words.begin(), words.end()); + vector res; + + for (string& word : words) { + int m = word.length(); + vector dp(m + 1, false); + dp[0] = true; + + for (int i = 1; i <= m; i++) { + for (int j = 0; j < i; j++) { + if (j == 0 && i == m) continue; + if (dp[j] && wordSet.count(word.substr(j, i - j))) { + dp[i] = true; + break; + } + } + } + + if (dp[m]) { + res.push_back(word); + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} words + * @return {string[]} + */ + findAllConcatenatedWordsInADict(words) { + const wordSet = new Set(words); + const res = []; + + for (const word of words) { + const m = word.length; + const dp = new Array(m + 1).fill(false); + dp[0] = true; + + for (let i = 1; i <= m; i++) { + for (let j = 0; j < i; j++) { + if (j === 0 && i === m) continue; + if (dp[j] && wordSet.has(word.substring(j, i))) { + dp[i] = true; + break; + } + } + } + + if (dp[m]) { + res.push(word); + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * m ^ 3)$ +* Space complexity: $O(n * m)$ + +> Where $n$ is the size of the string array $words$ and $m$ is the length of the longest word in the array. \ No newline at end of file diff --git a/articles/count-all-valid-pickup-and-delivery-options.md b/articles/count-all-valid-pickup-and-delivery-options.md new file mode 100644 index 000000000..78b58d069 --- /dev/null +++ b/articles/count-all-valid-pickup-and-delivery-options.md @@ -0,0 +1,668 @@ +## 1. Recursion + +::tabs-start + +```python +class Solution: + def countOrders(self, n: int) -> int: + MOD = 1000000007 + + def dfs(picked, delivered): + if picked == n and delivered == n: + return 1 + + res = 0 + if picked < n: + res = (res + (n - picked) * dfs(picked + 1, delivered)) % MOD + if delivered < picked: + res = (res + (picked - delivered) * dfs(picked, delivered + 1)) % MOD + + return res + + return dfs(0, 0) +``` + +```java +public class Solution { + private static final int MOD = 1_000_000_007; + + public int countOrders(int n) { + return dfs(0, 0, n); + } + + private int dfs(int picked, int delivered, int n) { + if (picked == n && delivered == n) { + return 1; + } + + long res = 0; + if (picked < n) { + res = ((res + (n - picked) * 1L * dfs(picked + 1, delivered, n)) % MOD); + } + if (delivered < picked) { + res = ((res + (picked - delivered) * 1L * dfs(picked, delivered + 1, n)) % MOD); + } + + return (int) res; + } +} +``` + +```cpp +class Solution { +public: + static const int MOD = 1'000'000'007; + + int countOrders(int n) { + return dfs(0, 0, n); + } + +private: + int dfs(int picked, int delivered, int n) { + if (picked == n && delivered == n) { + return 1; + } + + int res = 0; + if (picked < n) { + res = (res + (n - picked) * 1LL * dfs(picked + 1, delivered, n)) % MOD; + } + if (delivered < picked) { + res = (res + (picked - delivered) * 1LL * dfs(picked, delivered + 1, n)) % MOD; + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {number} + */ + countOrders(n) { + const MOD = 1_000_000_007; + + const dfs = (picked, delivered) => { + if (picked === n && delivered === n) { + return 1; + } + + let res = 0; + if (picked < n) { + res = (res + (n - picked) * dfs(picked + 1, delivered)) % MOD; + } + if (delivered < picked) { + res = (res + (picked - delivered) * dfs(picked, delivered + 1)) % MOD; + } + + return res; + }; + + return dfs(0, 0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(2 ^ n)$ +* Space complexity: $O(n)$ for recursion stack. + +--- + +## 2. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def countOrders(self, n: int) -> int: + MOD = 1000000007 + dp = [[-1] * (n + 1) for _ in range(n + 1)] + dp[n][n] = 1 + + def dfs(picked, delivered): + if dp[picked][delivered] != -1: + return dp[picked][delivered] + + res = 0 + if picked < n: + res = (res + (n - picked) * dfs(picked + 1, delivered)) % MOD + if delivered < picked: + res = (res + (picked - delivered) * dfs(picked, delivered + 1)) % MOD + + dp[picked][delivered] = res + return res + + return dfs(0, 0) +``` + +```java +public class Solution { + private static final int MOD = 1_000_000_007; + private int[][] dp; + + public int countOrders(int n) { + dp = new int[n + 1][n + 1]; + for (int i = 0; i <= n; i++) { + for (int j = 0; j <= n; j++) { + dp[i][j] = -1; + } + } + dp[n][n] = 1; + return dfs(0, 0, n); + } + + private int dfs(int picked, int delivered, int n) { + if (dp[picked][delivered] != -1) { + return dp[picked][delivered]; + } + + long res = 0; + if (picked < n) { + res = ((res + (n - picked) * 1L * dfs(picked + 1, delivered, n)) % MOD); + } + if (delivered < picked) { + res = ((res + (picked - delivered) * 1L * dfs(picked, delivered + 1, n)) % MOD); + } + + return dp[picked][delivered] = (int)res; + } +} +``` + +```cpp +class Solution { +public: + static const int MOD = 1'000'000'007; + vector> dp; + + int countOrders(int n) { + dp.assign(n + 1, vector(n + 1, -1)); + dp[n][n] = 1; + return dfs(0, 0, n); + } + +private: + int dfs(int picked, int delivered, int n) { + if (dp[picked][delivered] != -1) { + return dp[picked][delivered]; + } + + int res = 0; + if (picked < n) { + res = (res + (n - picked) * 1LL * dfs(picked + 1, delivered, n)) % MOD; + } + if (delivered < picked) { + res = (res + (picked - delivered) * 1LL * dfs(picked, delivered + 1, n)) % MOD; + } + + return dp[picked][delivered] = res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {number} + */ + countOrders(n) { + const MOD = 1_000_000_007; + const dp = Array.from({ length: n + 1 }, () => Array(n + 1).fill(-1)); + + const dfs = (picked, delivered) => { + if (picked === n && delivered === n) { + return 1; + } + if (dp[picked][delivered] !== -1) { + return dp[picked][delivered]; + } + + let res = 0; + if (picked < n) { + res = (res + (n - picked) * dfs(picked + 1, delivered)) % MOD; + } + if (delivered < picked) { + res = (res + (picked - delivered) * dfs(picked, delivered + 1)) % MOD; + } + + dp[picked][delivered] = res; + return res; + }; + + return dfs(0, 0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n ^ 2)$ + +--- + +## 3. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def countOrders(self, n: int) -> int: + MOD = 1000000007 + dp = [[0] * (n + 1) for _ in range(n + 1)] + dp[0][0] = 1 + + for picked in range(n + 1): + for delivered in range(n + 1): + if picked < n: + dp[picked + 1][delivered] = ( + (dp[picked + 1][delivered] + + (n - picked) * dp[picked][delivered]) % MOD + ) + + if delivered < picked: + dp[picked][delivered + 1] = ( + (dp[picked][delivered + 1] + + (picked - delivered) * dp[picked][delivered]) % MOD + ) + + return dp[n][n] +``` + +```java +public class Solution { + public int countOrders(int n) { + final int MOD = 1_000_000_007; + int[][] dp = new int[n + 1][n + 1]; + dp[0][0] = 1; + + for (int picked = 0; picked <= n; picked++) { + for (int delivered = 0; delivered <= n; delivered++) { + if (picked < n) { + dp[picked + 1][delivered] = (int) ((dp[picked + 1][delivered] + + (n - picked) * 1L * dp[picked][delivered]) % MOD); + } + if (delivered < picked) { + dp[picked][delivered + 1] = (int) ((dp[picked][delivered + 1] + + (picked - delivered) * 1L * dp[picked][delivered]) % MOD); + } + } + } + + return dp[n][n]; + } +} +``` + +```cpp +class Solution { +public: + int countOrders(int n) { + const int MOD = 1'000'000'007; + vector> dp(n + 1, vector(n + 1, 0)); + dp[0][0] = 1; + + for (int picked = 0; picked <= n; picked++) { + for (int delivered = 0; delivered <= n; delivered++) { + if (picked < n) { + dp[picked + 1][delivered] = (dp[picked + 1][delivered] + + (n - picked) * 1LL * dp[picked][delivered]) % MOD; + } + if (delivered < picked) { + dp[picked][delivered + 1] = (dp[picked][delivered + 1] + + (picked - delivered) * 1LL * dp[picked][delivered]) % MOD; + } + } + } + + return dp[n][n]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {number} + */ + countOrders(n) { + const MOD = 1_000_000_007; + const dp = Array.from({ length: n + 1 }, () => Array(n + 1).fill(0)); + dp[0][0] = 1; + + for (let picked = 0; picked <= n; picked++) { + for (let delivered = 0; delivered <= n; delivered++) { + if (picked < n) { + dp[picked + 1][delivered] = ( + (dp[picked + 1][delivered] + + (n - picked) * dp[picked][delivered]) % MOD + ); + } + + if (delivered < picked) { + dp[picked][delivered + 1] = ( + (dp[picked][delivered + 1] + + (picked - delivered) * dp[picked][delivered]) % MOD + ); + } + } + } + + return dp[n][n]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n ^ 2)$ + +--- + +## 4. Dynamic Programming (Space Optimized) + +::tabs-start + +```python +class Solution: + def countOrders(self, n: int) -> int: + MOD = 1000000007 + dp = [0] * (n + 1) + dp[0] = 1 + + for picked in range(n + 1): + for delivered in range(picked): + dp[delivered + 1] = ( + (dp[delivered + 1] + + (picked - delivered) * dp[delivered]) % MOD + ) + + if picked < n: + next_dp = [0] * (n + 1) + for delivered in range(picked + 1): + next_dp[delivered] = ( + (next_dp[delivered] + + (n - picked) * dp[delivered]) % MOD + ) + dp = next_dp + + return dp[n] +``` + +```java +public class Solution { + public int countOrders(int n) { + int MOD = 1000000007; + int[] dp = new int[n + 1]; + dp[0] = 1; + + for (int picked = 0; picked <= n; picked++) { + for (int delivered = 0; delivered < picked; delivered++) { + dp[delivered + 1] = (int)((dp[delivered + 1] + + (picked - delivered) * 1L * dp[delivered]) % MOD); + } + + if (picked < n) { + int[] next_dp = new int[n + 1]; + for (int delivered = 0; delivered <= picked; delivered++) { + next_dp[delivered] = (int)((next_dp[delivered] + + (n - picked) * 1L *dp[delivered]) % MOD); + } + dp = next_dp; + } + } + + return dp[n]; + } +} +``` + +```cpp +class Solution { +public: + int countOrders(int n) { + const int MOD = 1000000007; + vector dp(n + 1); + dp[0] = 1; + + for (int picked = 0; picked <= n; picked++) { + for (int delivered = 0; delivered < picked; delivered++) { + dp[delivered + 1] = (int)((dp[delivered + 1] + + (picked - delivered) * 1LL * dp[delivered]) % MOD); + } + if (picked < n) { + vector next_dp(n + 1); + for (int delivered = 0; delivered <= picked; delivered++) { + next_dp[delivered] = (int)((next_dp[delivered] + + (n - picked) * 1LL * dp[delivered]) % MOD); + } + dp = next_dp; + } + } + + return dp[n]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {number} + */ + countOrders(n) { + const MOD = 1000000007; + let dp = new Array(n + 1).fill(0); + dp[0] = 1; + + for (let picked = 0; picked <= n; picked++) { + for (let delivered = 0; delivered < picked; delivered++) { + dp[delivered + 1] = ( + (dp[delivered + 1] + + (picked - delivered) * dp[delivered]) % MOD + ); + } + + if (picked < n) { + let next_dp = new Array(n + 1).fill(0); + for (let delivered = 0; delivered <= picked; delivered++) { + next_dp[delivered] = ( + (next_dp[delivered] + + (n - picked) * dp[delivered]) % MOD + ); + } + dp = next_dp; + } + } + + return dp[n]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n)$ + +--- + +## 5. Combinatorics + +::tabs-start + +```python +class Solution: + def countOrders(self, n: int) -> int: + MOD = 1000000007 + slots, res = 2 * n, 1 + while slots > 0: + valid_choices = slots * (slots - 1) // 2 + res = (res * valid_choices) % MOD + slots -= 2 + return res +``` + +```java +public class Solution { + public int countOrders(int n) { + int MOD = 1000000007; + long slots = 2 * n, res = 1; + + while (slots > 0) { + long validChoices = slots * (slots - 1) / 2; + res = (res * validChoices) % MOD; + slots -= 2; + } + return (int) res; + } +} +``` + +```cpp +class Solution { +public: + int countOrders(int n) { + const int MOD = 1000000007; + long long slots = 2 * n, res = 1; + + while (slots > 0) { + long long validChoices = slots * (slots - 1) / 2; + res = (res * validChoices) % MOD; + slots -= 2; + } + return (int) res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {number} + */ + countOrders(n) { + const MOD = 1000000007; + let slots = 2 * n, res = 1; + + while (slots > 0) { + let validChoices = (slots * (slots - 1)) / 2; + res = (res * validChoices) % MOD; + slots -= 2; + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ + +--- + +## 6. Probability + +::tabs-start + +```python +class Solution: + def countOrders(self, n: int) -> int: + MOD = 1000000007 + res = 1 + + for slot in range(1, 2 * n + 1): + res *= slot + if slot % 2 == 0: + res >>= 1 + res %= MOD + + return res +``` + +```java +public class Solution { + public int countOrders(int n) { + int MOD = 1000000007; + long res = 1; + + for (int slot = 1; slot <= 2 * n; slot++) { + res *= slot; + if (slot % 2 == 0) { + res >>= 1; + } + res %= MOD; + } + return (int) res; + } +} +``` + +```cpp +class Solution { +public: + int countOrders(int n) { + const int MOD = 1000000007; + long long res = 1; + + for (int slot = 1; slot <= 2 * n; slot++) { + res *= slot; + if (slot % 2 == 0) { + res >>= 1; + } + res %= MOD; + } + return (int) res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {number} + */ + countOrders(n) { + const MOD = BigInt(1000000007); + let res = BigInt(1); + + for (let slot = 1; slot <= 2 * n; slot++) { + res *= BigInt(slot); + if (slot % 2 === 0) { + res /= BigInt(2); + } + res %= MOD; + } + return Number(res); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/data-stream-as-disjoint-intervals.md b/articles/data-stream-as-disjoint-intervals.md new file mode 100644 index 000000000..a39dc70f3 --- /dev/null +++ b/articles/data-stream-as-disjoint-intervals.md @@ -0,0 +1,450 @@ +## 1. Brute Force (Sorting) + +::tabs-start + +```python +class SummaryRanges: + + def __init__(self): + self.arr = [] + + def addNum(self, value: int) -> None: + self.arr.append(value) + + def getIntervals(self) -> List[List[int]]: + if not self.arr: + return [] + + self.arr.sort() + n = len(self.arr) + start = self.arr[0] + res = [] + for i in range(1, n): + if self.arr[i] - self.arr[i - 1] > 1: + res.append([start, self.arr[i - 1]]) + start = self.arr[i] + + res.append([start, self.arr[n - 1]]) + return res +``` + +```java +public class SummaryRanges { + private List arr; + + public SummaryRanges() { + arr = new ArrayList<>(); + } + + public void addNum(int value) { + arr.add(value); + } + + public List getIntervals() { + List res = new ArrayList<>(); + if (arr.isEmpty()) return res; + + Collections.sort(arr); + int start = arr.get(0); + for (int i = 1; i < arr.size(); i++) { + if (arr.get(i) - arr.get(i - 1) > 1) { + res.add(new int[]{start, arr.get(i - 1)}); + start = arr.get(i); + } + } + res.add(new int[]{start, arr.get(arr.size() - 1)}); + return res; + } +} +``` + +```cpp +class SummaryRanges { +private: + vector arr; + +public: + SummaryRanges() {} + + void addNum(int value) { + arr.push_back(value); + } + + vector> getIntervals() { + vector> res; + if (arr.empty()) return res; + + sort(arr.begin(), arr.end()); + int start = arr[0]; + for (int i = 1; i < arr.size(); i++) { + if (arr[i] - arr[i - 1] > 1) { + res.push_back({start, arr[i - 1]}); + start = arr[i]; + } + } + res.push_back({start, arr.back()}); + return res; + } +}; +``` + +```javascript +class SummaryRanges { + constructor() { + this.arr = []; + } + + /** + * @param {number} value + * @return {void} + */ + addNum(value) { + this.arr.push(value); + } + + /** + * @return {number[][]} + */ + getIntervals() { + if (this.arr.length === 0) return []; + + this.arr.sort((a, b) => a - b); + let start = this.arr[0]; + let res = []; + + for (let i = 1; i < this.arr.length; i++) { + if (this.arr[i] - this.arr[i - 1] > 1) { + res.push([start, this.arr[i - 1]]); + start = this.arr[i]; + } + } + res.push([start, this.arr[this.arr.length - 1]]); + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: + * $O(1)$ time for initialization. + * $O(1)$ time for each $addNum()$ function call. + * $O(n \log n)$ time for each $getIntervals()$ function call. +* Space complexity: $O(n)$ + +--- + +## 2. Hash Set + Sorting + +::tabs-start + +```python +class SummaryRanges: + + def __init__(self): + self.arr = set() + + def addNum(self, value: int) -> None: + self.arr.add(value) + + def getIntervals(self) -> List[List[int]]: + if not self.arr: + return [] + + lst = sorted(list(self.arr)) + n = len(lst) + start = lst[0] + res = [] + for i in range(1, n): + if lst[i] - lst[i - 1] > 1: + res.append([start, lst[i - 1]]) + start = lst[i] + + res.append([start, lst[n - 1]]) + return res +``` + +```java +public class SummaryRanges { + private Set arr; + + public SummaryRanges() { + arr = new TreeSet<>(); + } + + public void addNum(int value) { + arr.add(value); + } + + public List getIntervals() { + List res = new ArrayList<>(); + if (arr.isEmpty()) return res; + + List lst = new ArrayList<>(arr); + int start = lst.get(0); + for (int i = 1; i < lst.size(); i++) { + if (lst.get(i) - lst.get(i - 1) > 1) { + res.add(new int[]{start, lst.get(i - 1)}); + start = lst.get(i); + } + } + res.add(new int[]{start, lst.get(lst.size() - 1)}); + return res; + } +} +``` + +```cpp +class SummaryRanges { +private: + set arr; + +public: + SummaryRanges() {} + + void addNum(int value) { + arr.insert(value); + } + + vector> getIntervals() { + vector> res; + if (arr.empty()) return res; + + vector lst(arr.begin(), arr.end()); + int start = lst[0]; + + for (int i = 1; i < lst.size(); i++) { + if (lst[i] - lst[i - 1] > 1) { + res.push_back({start, lst[i - 1]}); + start = lst[i]; + } + } + res.push_back({start, lst.back()}); + return res; + } +}; +``` + +```javascript +class SummaryRanges { + constructor() { + this.arr = new Set(); + } + + /** + * @param {number} value + * @return {number[][]} + */ + addNum(value) { + this.arr.add(value); + } + + /** + * @return {number[][]} + */ + getIntervals() { + if (this.arr.size === 0) return []; + + let lst = Array.from(this.arr).sort((a, b) => a - b); + let start = lst[0]; + let res = []; + + for (let i = 1; i < lst.length; i++) { + if (lst[i] - lst[i - 1] > 1) { + res.push([start, lst[i - 1]]); + start = lst[i]; + } + } + res.push([start, lst[lst.length - 1]]); + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: + * $O(1)$ time for initialization. + * $O(1)$ time for each $addNum()$ function call. + * $O(n \log n)$ time for each $getIntervals()$ function call. +* Space complexity: $O(n)$ + +--- + +## 3. Ordered Map + +::tabs-start + +```python +from sortedcontainers import SortedDict + +class SummaryRanges: + def __init__(self): + self.treeMap = SortedDict() + + def addNum(self, value: int) -> None: + self.treeMap[value] = True + + def getIntervals(self) -> List[List[int]]: + res = [] + for n in self.treeMap: + if res and res[-1][1] + 1 == n: + res[-1][1] = n + else: + res.append([n, n]) + return res +``` + +```java +public class SummaryRanges { + private TreeMap treeMap; + + public SummaryRanges() { + treeMap = new TreeMap<>(); + } + + public void addNum(int value) { + treeMap.put(value, true); + } + + public List getIntervals() { + List res = new ArrayList<>(); + for (int n : treeMap.keySet()) { + if (!res.isEmpty() && res.get(res.size() - 1)[1] + 1 == n) { + res.get(res.size() - 1)[1] = n; + } else { + res.add(new int[]{n, n}); + } + } + return res; + } +} +``` + +```cpp +class SummaryRanges { +private: + map treeMap; + +public: + SummaryRanges() {} + + void addNum(int value) { + treeMap[value] = true; + } + + vector> getIntervals() { + vector> res; + for (auto& [n, _] : treeMap) { + if (!res.empty() && res.back()[1] + 1 == n) { + res.back()[1] = n; + } else { + res.push_back({n, n}); + } + } + return res; + } +}; +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: + * $O(1)$ time for initialization. + * $O(\log n)$ time for each $addNum()$ function call. + * $O(n)$ time for each $getIntervals()$ function call. +* Space complexity: $O(n)$ + +--- + +## 4. Ordered Set + +::tabs-start + +```python +from sortedcontainers import SortedSet + +class SummaryRanges: + def __init__(self): + self.orderedSet = SortedSet() + + def addNum(self, value: int) -> None: + self.orderedSet.add(value) + + def getIntervals(self) -> List[List[int]]: + res = [] + for n in self.orderedSet: + if res and res[-1][1] + 1 == n: + res[-1][1] = n + else: + res.append([n, n]) + return res +``` + +```java +public class SummaryRanges { + private TreeSet orderedSet; + + public SummaryRanges() { + orderedSet = new TreeSet<>(); + } + + public void addNum(int value) { + orderedSet.add(value); + } + + public List getIntervals() { + List res = new ArrayList<>(); + for (int n : orderedSet) { + if (!res.isEmpty() && res.get(res.size() - 1)[1] + 1 == n) { + res.get(res.size() - 1)[1] = n; + } else { + res.add(new int[]{n, n}); + } + } + return res; + } +} +``` + +```cpp +class SummaryRanges { +private: + set orderedSet; + +public: + SummaryRanges() {} + + void addNum(int value) { + orderedSet.insert(value); + } + + vector> getIntervals() { + vector> res; + for (int n : orderedSet) { + if (!res.empty() && res.back()[1] + 1 == n) { + res.back()[1] = n; + } else { + res.push_back({n, n}); + } + } + return res; + } +}; +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: + * $O(1)$ time for initialization. + * $O(\log n)$ time for each $addNum()$ function call. + * $O(n)$ time for each $getIntervals()$ function call. +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/design-browser-history.md b/articles/design-browser-history.md new file mode 100644 index 000000000..a557f9151 --- /dev/null +++ b/articles/design-browser-history.md @@ -0,0 +1,640 @@ +## 1. Two Stacks + +::tabs-start + +```python +class BrowserHistory: + + def __init__(self, homepage: str): + self.back_history = [homepage] + self.front_history = [] + + def visit(self, url: str) -> None: + self.back_history.append(url) + self.front_history = [] + + def back(self, steps: int) -> str: + while steps and len(self.back_history) > 1: + self.front_history.append(self.back_history.pop()) + steps -= 1 + return self.back_history[-1] + + def forward(self, steps: int) -> str: + while steps and self.front_history: + self.back_history.append(self.front_history.pop()) + steps -= 1 + return self.back_history[-1] +``` + +```java +public class BrowserHistory { + private Stack backHistory; + private Stack frontHistory; + + public BrowserHistory(String homepage) { + backHistory = new Stack<>(); + frontHistory = new Stack<>(); + backHistory.push(homepage); + } + + public void visit(String url) { + backHistory.push(url); + frontHistory = new Stack<>(); + } + + public String back(int steps) { + while (steps > 0 && backHistory.size() > 1) { + frontHistory.push(backHistory.pop()); + steps--; + } + return backHistory.peek(); + } + + public String forward(int steps) { + while (steps > 0 && !frontHistory.isEmpty()) { + backHistory.push(frontHistory.pop()); + steps--; + } + return backHistory.peek(); + } +} +``` + +```cpp +class BrowserHistory { +private: + stack backHistory, frontHistory; + +public: + BrowserHistory(string homepage) { + backHistory.push(homepage); + } + + void visit(string url) { + backHistory.push(url); + frontHistory = stack(); + } + + string back(int steps) { + while (steps-- && backHistory.size() > 1) { + frontHistory.push(backHistory.top()); + backHistory.pop(); + } + return backHistory.top(); + } + + string forward(int steps) { + while (steps-- && !frontHistory.empty()) { + backHistory.push(frontHistory.top()); + frontHistory.pop(); + } + return backHistory.top(); + } +}; +``` + +```javascript +class BrowserHistory { + /** + * @constructor + * @param {string} homepage + */ + constructor(homepage) { + this.backHistory = [homepage]; + this.frontHistory = []; + } + + /** + * @param {string} url + * @return {void} + */ + visit(url) { + this.backHistory.push(url); + this.frontHistory = []; + } + + /** + * @param {number} steps + * @return {string} + */ + back(steps) { + while (steps-- > 0 && this.backHistory.length > 1) { + this.frontHistory.push(this.backHistory.pop()); + } + return this.backHistory[this.backHistory.length - 1]; + } + + /** + * @param {number} steps + * @return {string} + */ + forward(steps) { + while (steps-- > 0 && this.frontHistory.length > 0) { + this.backHistory.push(this.frontHistory.pop()); + } + return this.backHistory[this.backHistory.length - 1]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: + * $O(1)$ time for initialization. + * $O(1)$ time for each $visit()$ function call. + * $O(min(n, steps))$ time for each $back()$ and $forward()$ function calls. +* Space complexity: $O(m * n)$ + +> Where $n$ is the number of visited urls, $m$ is the average length of each url, and $steps$ is the number of steps we go forward or back. + +--- + +## 2. Dynamic Array + +::tabs-start + +```python +class BrowserHistory: + + def __init__(self, homepage: str): + self.history = [homepage] + self.cur = 0 + + def visit(self, url: str) -> None: + self.cur += 1 + self.history = self.history[:self.cur] + self.history.append(url) + + def back(self, steps: int) -> str: + self.cur = max(0, self.cur - steps) + return self.history[self.cur] + + def forward(self, steps: int) -> str: + self.cur = min(len(self.history) - 1, self.cur + steps) + return self.history[self.cur] +``` + +```java +public class BrowserHistory { + private List history; + private int cur; + + public BrowserHistory(String homepage) { + history = new ArrayList<>(); + history.add(homepage); + cur = 0; + } + + public void visit(String url) { + cur++; + history = history.subList(0, cur); + history.add(url); + } + + public String back(int steps) { + cur = Math.max(0, cur - steps); + return history.get(cur); + } + + public String forward(int steps) { + cur = Math.min(history.size() - 1, cur + steps); + return history.get(cur); + } +} +``` + +```cpp +class BrowserHistory { +private: + vector history; + int cur; + +public: + BrowserHistory(string homepage) { + history.push_back(homepage); + cur = 0; + } + + void visit(string url) { + cur++; + history.resize(cur); + history.push_back(url); + } + + string back(int steps) { + cur = max(0, cur - steps); + return history[cur]; + } + + string forward(int steps) { + cur = min((int)history.size() - 1, cur + steps); + return history[cur]; + } +}; +``` + +```javascript +class BrowserHistory { + /** + * @constructor + * @param {string} homepage + */ + constructor(homepage) { + this.history = [homepage]; + this.cur = 0; + } + + /** + * @param {string} url + * @return {void} + */ + visit(url) { + this.cur++; + this.history = this.history.slice(0, this.cur); + this.history.push(url); + } + + /** + * @param {number} steps + * @return {string} + */ + back(steps) { + this.cur = Math.max(0, this.cur - steps); + return this.history[this.cur]; + } + + /** + * @param {number} steps + * @return {string} + */ + forward(steps) { + this.cur = Math.min(this.history.length - 1, this.cur + steps); + return this.history[this.cur]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: + * $O(1)$ time for initialization. + * $O(n)$ time for each $visit()$ function call. + * $O(1)$ time for each $back()$ and $forward()$ function calls. +* Space complexity: $O(m * n)$ + +> Where $n$ is the number of visited urls and $m$ is the average length of each url. + +--- + +## 3. Dynamic Array (Optimal) + +::tabs-start + +```python +class BrowserHistory: + + def __init__(self, homepage: str): + self.history = [homepage] + self.cur = 0 + self.n = 1 + + def visit(self, url: str) -> None: + self.cur += 1 + if self.cur == len(self.history): + self.history.append(url) + self.n += 1 + else: + self.history[self.cur] = url + self.n = self.cur + 1 + + def back(self, steps: int) -> str: + self.cur = max(0, self.cur - steps) + return self.history[self.cur] + + def forward(self, steps: int) -> str: + self.cur = min(self.n - 1, self.cur + steps) + return self.history[self.cur] +``` + +```java +public class BrowserHistory { + private List history; + private int cur; + private int n; + + public BrowserHistory(String homepage) { + history = new ArrayList<>(); + history.add(homepage); + cur = 0; + n = 1; + } + + public void visit(String url) { + cur++; + if (cur == history.size()) { + history.add(url); + n++; + } else { + history.set(cur, url); + n = cur + 1; + } + } + + public String back(int steps) { + cur = Math.max(0, cur - steps); + return history.get(cur); + } + + public String forward(int steps) { + cur = Math.min(n - 1, cur + steps); + return history.get(cur); + } +} +``` + +```cpp +class BrowserHistory { +private: + vector history; + int cur, n; + +public: + BrowserHistory(string homepage) { + history.push_back(homepage); + cur = 0; + n = 1; + } + + void visit(string url) { + cur++; + if (cur == history.size()) { + history.push_back(url); + n++; + } else { + history[cur] = url; + n = cur + 1; + } + } + + string back(int steps) { + cur = max(0, cur - steps); + return history[cur]; + } + + string forward(int steps) { + cur = min(n - 1, cur + steps); + return history[cur]; + } +}; +``` + +```javascript +class BrowserHistory { + /** + * @constructor + * @param {string} homepage + */ + constructor(homepage) { + this.history = [homepage]; + this.cur = 0; + this.n = 1; + } + + /** + * @param {string} url + * @return {void} + */ + visit(url) { + this.cur++; + if (this.cur === this.history.length) { + this.history.push(url); + this.n++; + } else { + this.history[this.cur] = url; + this.n = this.cur + 1; + } + } + + /** + * @param {number} steps + * @return {string} + */ + back(steps) { + this.cur = Math.max(0, this.cur - steps); + return this.history[this.cur]; + } + + /** + * @param {number} steps + * @return {string} + */ + forward(steps) { + this.cur = Math.min(this.n - 1, this.cur + steps); + return this.history[this.cur]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: + * $O(1)$ time for initialization. + * $O(1)$ time for each $visit()$ function call. + * $O(1)$ time for each $back()$ and $forward()$ function calls. +* Space complexity: $O(m * n)$ + +> Where $n$ is the number of visited urls and $m$ is the average length of each url. + +--- + +## 4. Doubly Linked List + +::tabs-start + +```python +class ListNode: + def __init__(self, val, prev=None, next=None): + self.val = val + self.prev = prev + self.next = next + +class BrowserHistory: + + def __init__(self, homepage: str): + self.cur = ListNode(homepage) + + def visit(self, url: str) -> None: + self.cur.next = ListNode(url, self.cur) + self.cur = self.cur.next + + def back(self, steps: int) -> str: + while self.cur.prev and steps > 0: + self.cur = self.cur.prev + steps -= 1 + return self.cur.val + + def forward(self, steps: int) -> str: + while self.cur.next and steps > 0: + self.cur = self.cur.next + steps -= 1 + return self.cur.val +``` + +```java +class ListNode { + String val; + ListNode prev, next; + + public ListNode(String val, ListNode prev, ListNode next) { + this.val = val; + this.prev = prev; + this.next = next; + } + + public ListNode(String val) { + this(val, null, null); + } +} + +public class BrowserHistory { + private ListNode cur; + + public BrowserHistory(String homepage) { + cur = new ListNode(homepage); + } + + public void visit(String url) { + cur.next = new ListNode(url, cur, null); + cur = cur.next; + } + + public String back(int steps) { + while (cur.prev != null && steps > 0) { + cur = cur.prev; + steps--; + } + return cur.val; + } + + public String forward(int steps) { + while (cur.next != null && steps > 0) { + cur = cur.next; + steps--; + } + return cur.val; + } +} +``` + +```cpp +class BrowserHistory { + struct ListNode { + public: + string val; + ListNode* prev; + ListNode* next; + + ListNode(string val, ListNode* prev = nullptr, ListNode* next = nullptr) + : val(val), prev(prev), next(next) {} + }; + + ListNode* cur; + +public: + BrowserHistory(string homepage) { + cur = new ListNode(homepage); + } + + void visit(string url) { + cur->next = new ListNode(url, cur, nullptr); + cur = cur->next; + } + + string back(int steps) { + while (cur->prev != nullptr && steps > 0) { + cur = cur->prev; + steps--; + } + return cur->val; + } + + string forward(int steps) { + while (cur->next != nullptr && steps > 0) { + cur = cur->next; + steps--; + } + return cur->val; + } +}; +``` + +```javascript +class ListNode { + constructor(val, prev = null, next = null) { + this.val = val; + this.prev = prev; + this.next = next; + } +} + +class BrowserHistory { + /** + * @constructor + * @param {string} homepage + */ + constructor(homepage) { + this.cur = new ListNode(homepage); + } + + /** + * @param {string} url + * @return {void} + */ + visit(url) { + this.cur.next = new ListNode(url, this.cur, null); + this.cur = this.cur.next; + } + + /** + * @param {number} steps + * @return {string} + */ + back(steps) { + while (this.cur.prev !== null && steps > 0) { + this.cur = this.cur.prev; + steps--; + } + return this.cur.val; + } + + /** + * @param {number} steps + * @return {string} + */ + forward(steps) { + while (this.cur.next !== null && steps > 0) { + this.cur = this.cur.next; + steps--; + } + return this.cur.val; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: + * $O(1)$ time for initialization. + * $O(1)$ time for each $visit()$ function call. + * $O(min(n, steps))$ time for each $back()$ and $forward()$ function calls. +* Space complexity: $O(m * n)$ + +> Where $n$ is the number of visited urls, $m$ is the average length of each url, and $steps$ is the number of steps we go forward or back. \ No newline at end of file diff --git a/articles/detonate-the-maximum-bombs.md b/articles/detonate-the-maximum-bombs.md new file mode 100644 index 000000000..bc1a75fc9 --- /dev/null +++ b/articles/detonate-the-maximum-bombs.md @@ -0,0 +1,522 @@ +## 1. Depth First Search + +::tabs-start + +```python +class Solution: + def maximumDetonation(self, bombs: list[list[int]]) -> int: + adj = [[] for _ in range(len(bombs))] + + for i in range(len(bombs)): + x1, y1, r1 = bombs[i] + for j in range(i + 1, len(bombs)): + x2, y2, r2 = bombs[j] + d = (x1 - x2) ** 2 + (y1 - y2) ** 2 + + if d <= r1 ** 2: + adj[i].append(j) + if d <= r2 ** 2: + adj[j].append(i) + + def dfs(i, visit): + if i in visit: + return 0 + visit.add(i) + for nei in adj[i]: + dfs(nei, visit) + return len(visit) + + res = 0 + for i in range(len(bombs)): + res = max(res, dfs(i, set())) + return res +``` + +```java +public class Solution { + public int maximumDetonation(int[][] bombs) { + int n = bombs.length; + List[] adj = new ArrayList[n]; + for (int i = 0; i < n; i++) { + adj[i] = new ArrayList<>(); + } + + for (int i = 0; i < n; i++) { + for (int j = i + 1; j < n; j++) { + int x1 = bombs[i][0], y1 = bombs[i][1], r1 = bombs[i][2]; + int x2 = bombs[j][0], y2 = bombs[j][1], r2 = bombs[j][2]; + long d = (long) (x1 - x2) * (x1 - x2) + (long) (y1 - y2) * (y1 - y2); + + if (d <= (long) r1 * r1) { + adj[i].add(j); + } + if (d <= (long) r2 * r2) { + adj[j].add(i); + } + } + } + + int res = 0; + for (int i = 0; i < n; i++) { + res = Math.max(res, dfs(i, new HashSet<>(), adj)); + } + return res; + } + + private int dfs(int i, Set visit, List[] adj) { + if (!visit.add(i)) return 0; + for (int nei : adj[i]) { + dfs(nei, visit, adj); + } + return visit.size(); + } +} +``` + +```cpp +class Solution { +public: + int maximumDetonation(vector>& bombs) { + int n = bombs.size(); + vector> adj(n); + + for (int i = 0; i < n; i++) { + for (int j = i + 1; j < n; j++) { + long long x1 = bombs[i][0], y1 = bombs[i][1], r1 = bombs[i][2]; + long long x2 = bombs[j][0], y2 = bombs[j][1], r2 = bombs[j][2]; + long long d = (x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2); + + if (d <= r1 * r1) { + adj[i].push_back(j); + } + if (d <= r2 * r2) { + adj[j].push_back(i); + } + } + } + + int res = 0; + for (int i = 0; i < n; i++) { + unordered_set visit; + res = max(res, dfs(i, visit, adj)); + } + return res; + } + +private: + int dfs(int i, unordered_set& visit, vector>& adj) { + if (!visit.insert(i).second) return 0; + for (int nei : adj[i]) { + dfs(nei, visit, adj); + } + return visit.size(); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} bombs + * @return {number} + */ + maximumDetonation(bombs) { + let n = bombs.length; + let adj = Array.from({ length: n }, () => []); + + for (let i = 0; i < n; i++) { + for (let j = i + 1; j < n; j++) { + let [x1, y1, r1] = bombs[i]; + let [x2, y2, r2] = bombs[j]; + let d = (x1 - x2) ** 2 + (y1 - y2) ** 2; + + if (d <= r1 ** 2) adj[i].push(j); + if (d <= r2 ** 2) adj[j].push(i); + } + } + + const dfs = (i, visit) => { + if (visit.has(i)) return 0; + visit.add(i); + for (let nei of adj[i]) { + dfs(nei, visit); + } + return visit.size; + }; + + let res = 0; + for (let i = 0; i < n; i++) { + res = Math.max(res, dfs(i, new Set())); + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 3)$ +* Space complexity: $O(n ^ 2)$ + +--- + +## 2. Breadth First Search + +::tabs-start + +```python +class Solution: + def maximumDetonation(self, bombs: list[list[int]]) -> int: + n = len(bombs) + adj = [[] for _ in range(n)] + + for i in range(n): + x1, y1, r1 = bombs[i] + for j in range(i + 1, n): + x2, y2, r2 = bombs[j] + d = (x1 - x2) ** 2 + (y1 - y2) ** 2 + + if d <= r1 ** 2: + adj[i].append(j) + if d <= r2 ** 2: + adj[j].append(i) + + res = 0 + for i in range(n): + q = deque([i]) + visit = [False] * n + visit[i] = True + count = 1 + while q: + node = q.popleft() + for nei in adj[node]: + if not visit[nei]: + visit[nei] = True + count += 1 + q.append(nei) + res = max(res, count) + return res +``` + +```java +public class Solution { + public int maximumDetonation(int[][] bombs) { + int n = bombs.length; + List[] adj = new ArrayList[n]; + for (int i = 0; i < n; i++) { + adj[i] = new ArrayList<>(); + } + + for (int i = 0; i < n; i++) { + int x1 = bombs[i][0], y1 = bombs[i][1], r1 = bombs[i][2]; + for (int j = i + 1; j < n; j++) { + int x2 = bombs[j][0], y2 = bombs[j][1], r2 = bombs[j][2]; + long d = (long) (x1 - x2) * (x1 - x2) + (long) (y1 - y2) * (y1 - y2); + + if (d <= (long) r1 * r1) adj[i].add(j); + if (d <= (long) r2 * r2) adj[j].add(i); + } + } + + int res = 0; + for (int i = 0; i < n; i++) { + Queue q = new LinkedList<>(); + boolean[] visit = new boolean[n]; + q.offer(i); + visit[i] = true; + int count = 1; + + while (!q.isEmpty()) { + int node = q.poll(); + for (int nei : adj[node]) { + if (!visit[nei]) { + visit[nei] = true; + count++; + q.offer(nei); + } + } + } + res = Math.max(res, count); + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int maximumDetonation(vector>& bombs) { + int n = bombs.size(); + vector> adj(n); + + for (int i = 0; i < n; i++) { + int x1 = bombs[i][0], y1 = bombs[i][1], r1 = bombs[i][2]; + for (int j = i + 1; j < n; j++) { + int x2 = bombs[j][0], y2 = bombs[j][1], r2 = bombs[j][2]; + long long d = (x1 - x2) * 1LL * (x1 - x2) + (y1 - y2) * 1LL * (y1 - y2); + + if (d <= (long long) r1 * r1) adj[i].push_back(j); + if (d <= (long long) r2 * r2) adj[j].push_back(i); + } + } + + int res = 0; + for (int i = 0; i < n; i++) { + queue q; + vector visit(n, false); + q.push(i); + visit[i] = true; + int count = 1; + + while (!q.empty()) { + int node = q.front();q.pop(); + for (int& nei : adj[node]) { + if (!visit[nei]) { + visit[nei] = true; + count++; + q.push(nei); + } + } + } + res = max(res, count); + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} bombs + * @return {number} + */ + maximumDetonation(bombs) { + let n = bombs.length; + let adj = Array.from({ length: n }, () => []); + + for (let i = 0; i < n; i++) { + let [x1, y1, r1] = bombs[i]; + for (let j = i + 1; j < n; j++) { + let [x2, y2, r2] = bombs[j]; + let d = (x1 - x2) ** 2 + (y1 - y2) ** 2; + + if (d <= r1 ** 2) adj[i].push(j); + if (d <= r2 ** 2) adj[j].push(i); + } + } + + let res = 0; + for (let i = 0; i < n; i++) { + let q = new Queue([i]); + let visit = new Array(n).fill(false); + visit[i] = true; + let count = 1; + + while (!q.isEmpty()) { + let node = q.pop(); + for (let nei of adj[node]) { + if (!visit[nei]) { + visit[nei] = true; + count++; + q.push(nei); + } + } + } + res = Math.max(res, count); + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 3)$ +* Space complexity: $O(n ^ 2)$ + +--- + +## 3. Iterative DFS + +::tabs-start + +```python +class Solution: + def maximumDetonation(self, bombs: list[list[int]]) -> int: + n = len(bombs) + adj = [[] for _ in range(n)] + + for i in range(n): + x1, y1, r1 = bombs[i] + for j in range(i + 1, n): + x2, y2, r2 = bombs[j] + d = (x1 - x2) ** 2 + (y1 - y2) ** 2 + + if d <= r1 ** 2: + adj[i].append(j) + if d <= r2 ** 2: + adj[j].append(i) + + res = 0 + for i in range(n): + stack = [i] + visit = [False] * n + visit[i] = True + count = 1 + + while stack: + node = stack.pop() + for nei in adj[node]: + if not visit[nei]: + visit[nei] = True + count += 1 + stack.append(nei) + res = max(res, count) + return res +``` + +```java +public class Solution { + public int maximumDetonation(int[][] bombs) { + int n = bombs.length; + List[] adj = new ArrayList[n]; + for (int i = 0; i < n; i++) { + adj[i] = new ArrayList<>(); + } + + for (int i = 0; i < n; i++) { + int x1 = bombs[i][0], y1 = bombs[i][1], r1 = bombs[i][2]; + for (int j = i + 1; j < n; j++) { + int x2 = bombs[j][0], y2 = bombs[j][1], r2 = bombs[j][2]; + long d = (long) (x1 - x2) * (x1 - x2) + (long) (y1 - y2) * (y1 - y2); + + if (d <= (long) r1 * r1) adj[i].add(j); + if (d <= (long) r2 * r2) adj[j].add(i); + } + } + + int res = 0; + for (int i = 0; i < n; i++) { + Stack stack = new Stack<>(); + boolean[] visit = new boolean[n]; + stack.push(i); + visit[i] = true; + int count = 1; + + while (!stack.isEmpty()) { + int node = stack.pop(); + for (int nei : adj[node]) { + if (!visit[nei]) { + visit[nei] = true; + count++; + stack.push(nei); + } + } + } + res = Math.max(res, count); + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int maximumDetonation(vector>& bombs) { + int n = bombs.size(); + vector> adj(n); + + for (int i = 0; i < n; i++) { + int x1 = bombs[i][0], y1 = bombs[i][1], r1 = bombs[i][2]; + for (int j = i + 1; j < n; j++) { + int x2 = bombs[j][0], y2 = bombs[j][1], r2 = bombs[j][2]; + long long d = (long long)(x1 - x2) * (x1 - x2) + (long long)(y1 - y2) * (y1 - y2); + + if (d <= (long long) r1 * r1) adj[i].push_back(j); + if (d <= (long long) r2 * r2) adj[j].push_back(i); + } + } + + int res = 0; + for (int i = 0; i < n; i++) { + stack stk; + vector visit(n, false); + stk.push(i); + visit[i] = true; + int count = 1; + + while (!stk.empty()) { + int node = stk.top();stk.pop(); + for (int& nei : adj[node]) { + if (!visit[nei]) { + visit[nei] = true; + count++; + stk.push(nei); + } + } + } + res = max(res, count); + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} bombs + * @return {number} + */ + maximumDetonation(bombs) { + let n = bombs.length; + let adj = Array.from({ length: n }, () => []); + + for (let i = 0; i < n; i++) { + let [x1, y1, r1] = bombs[i]; + for (let j = i + 1; j < n; j++) { + let [x2, y2, r2] = bombs[j]; + let d = (x1 - x2) ** 2 + (y1 - y2) ** 2; + + if (d <= r1 ** 2) adj[i].push(j); + if (d <= r2 ** 2) adj[j].push(i); + } + } + + let res = 0; + for (let i = 0; i < n; i++) { + let stack = [i]; + let visit = new Array(n).fill(false); + visit[i] = true; + let count = 1; + + while (stack.length) { + let node = stack.pop(); + for (let nei of adj[node]) { + if (!visit[nei]) { + visit[nei] = true; + count++; + stack.push(nei); + } + } + } + res = Math.max(res, count); + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 3)$ +* Space complexity: $O(n ^ 2)$ \ No newline at end of file diff --git a/articles/distribute-coins-in-binary-tree.md b/articles/distribute-coins-in-binary-tree.md new file mode 100644 index 000000000..4e26d25e9 --- /dev/null +++ b/articles/distribute-coins-in-binary-tree.md @@ -0,0 +1,723 @@ +## 1. Depth First Search + +::tabs-start + +```python +# 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 +class Solution: + def distributeCoins(self, root: Optional[TreeNode]) -> int: + self.res = 0 + + def dfs(cur): + if not cur: + return [0, 0] # [size, coins] + + l_size, l_coins = dfs(cur.left) + r_size, r_coins = dfs(cur.right) + + size = 1 + l_size + r_size + coins = cur.val + l_coins + r_coins + self.res += abs(size - coins) + + return [size, coins] + + dfs(root) + return self.res +``` + +```java +/** + * 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; + * } + * } + */ +public class Solution { + private int res; + + public int distributeCoins(TreeNode root) { + res = 0; + dfs(root); + return res; + } + + private int[] dfs(TreeNode cur) { + if (cur == null) { + return new int[]{0, 0}; // [size, coins] + } + + int[] left = dfs(cur.left); + int[] right = dfs(cur.right); + + int size = 1 + left[0] + right[0]; + int coins = cur.val + left[1] + right[1]; + res += Math.abs(size - coins); + + return new int[]{size, coins}; + } +} +``` + +```cpp +/** + * 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 { +private: + int res; + + vector dfs(TreeNode* cur) { + if (!cur) { + return {0, 0}; // [size, coins] + } + + vector left = dfs(cur->left); + vector right = dfs(cur->right); + + int size = 1 + left[0] + right[0]; + int coins = cur->val + left[1] + right[1]; + res += abs(size - coins); + + return {size, coins}; + } + +public: + int distributeCoins(TreeNode* root) { + res = 0; + dfs(root); + return res; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {number} + */ + distributeCoins(root) { + let res = 0; + + const dfs = (cur) => { + if (!cur) { + return [0, 0]; // [size, coins] + } + + let [lSize, lCoins] = dfs(cur.left); + let [rSize, rCoins] = dfs(cur.right); + + let size = 1 + lSize + rSize; + let coins = cur.val + lCoins + rCoins; + res += Math.abs(size - coins); + + return [size, coins]; + }; + + dfs(root); + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ for recursion stack. + +--- + +## 2. Depth First Search (Optimal) + +::tabs-start + +```python +# 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 +class Solution: + def distributeCoins(self, root: Optional[TreeNode]) -> int: + self.res = 0 + + def dfs(cur): + if not cur: + return 0 # extra_coins + + l_extra = dfs(cur.left) + r_extra = dfs(cur.right) + + extra_coins = cur.val - 1 + l_extra + r_extra + self.res += abs(extra_coins) + return extra_coins + + dfs(root) + return self.res +``` + +```java +/** + * 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; + * } + * } + */ +public class Solution { + private int res; + + public int distributeCoins(TreeNode root) { + res = 0; + dfs(root); + return res; + } + + private int dfs(TreeNode cur) { + if (cur == null) { + return 0; // extra_coins + } + + int lExtra = dfs(cur.left); + int rExtra = dfs(cur.right); + + int extraCoins = cur.val - 1 + lExtra + rExtra; + res += Math.abs(extraCoins); + return extraCoins; + } +} +``` + +```cpp +/** + * 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 { +private: + int res; + + int dfs(TreeNode* cur) { + if (!cur) { + return 0; // extra_coins + } + + int lExtra = dfs(cur->left); + int rExtra = dfs(cur->right); + + int extraCoins = cur->val - 1 + lExtra + rExtra; + res += abs(extraCoins); + return extraCoins; + } + +public: + int distributeCoins(TreeNode* root) { + res = 0; + dfs(root); + return res; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {number} + */ + distributeCoins(root) { + let res = 0; + + const dfs = (cur) => { + if (!cur) { + return 0; // extra_coins + } + + let lExtra = dfs(cur.left); + let rExtra = dfs(cur.right); + + let extraCoins = cur.val - 1 + lExtra + rExtra; + res += Math.abs(extraCoins); + return extraCoins; + }; + + dfs(root); + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ fo recursion stack. + +--- + +## 3. Breadth First Search + +::tabs-start + +```python +# 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 +class Solution: + def distributeCoins(self, root: Optional[TreeNode]) -> int: + res = 0 + q = deque([root]) + parent_map = {} + + nodes = [] + while q: + node = q.popleft() + nodes.append(node) + if node.left: + parent_map[node.left] = node + q.append(node.left) + if node.right: + parent_map[node.right] = node + q.append(node.right) + + while nodes: + node = nodes.pop() + if node in parent_map: + parent = parent_map[node] + parent.val += node.val - 1 + res += abs(node.val - 1) + + return res +``` + +```java +/** + * 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; + * } + * } + */ +public class Solution { + public int distributeCoins(TreeNode root) { + int res = 0; + Queue q = new LinkedList<>(); + Map parentMap = new HashMap<>(); + List nodes = new ArrayList<>(); + + q.offer(root); + while (!q.isEmpty()) { + TreeNode node = q.poll(); + nodes.add(node); + if (node.left != null) { + parentMap.put(node.left, node); + q.offer(node.left); + } + if (node.right != null) { + parentMap.put(node.right, node); + q.offer(node.right); + } + } + + for (int i = nodes.size() - 1; i >= 0; i--) { + TreeNode node = nodes.get(i); + if (parentMap.containsKey(node)) { + TreeNode parent = parentMap.get(node); + parent.val += node.val - 1; + res += Math.abs(node.val - 1); + } + } + + return res; + } +} +``` + +```cpp +/** + * 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: + int distributeCoins(TreeNode* root) { + int res = 0; + queue q; + unordered_map parentMap; + vector nodes; + + q.push(root); + while (!q.empty()) { + TreeNode* node = q.front(); + q.pop(); + nodes.push_back(node); + if (node->left) { + parentMap[node->left] = node; + q.push(node->left); + } + if (node->right) { + parentMap[node->right] = node; + q.push(node->right); + } + } + + for (int i = nodes.size() - 1; i >= 0; i--) { + TreeNode* node = nodes[i]; + if (parentMap.count(node)) { + TreeNode* parent = parentMap[node]; + parent->val += node->val - 1; + res += abs(node->val - 1); + } + } + + return res; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {number} + */ + distributeCoins(root) { + let res = 0; + let q = new Queue(); + let parentMap = new Map(); + let nodes = []; + + q.push(root); + while (!q.isEmpty()) { + let node = q.pop(); + nodes.push(node); + if (node.left) { + parentMap.set(node.left, node); + q.push(node.left); + } + if (node.right) { + parentMap.set(node.right, node); + q.push(node.right); + } + } + + for (let i = nodes.length - 1; i >= 0; i--) { + let node = nodes[i]; + if (parentMap.has(node)) { + let parent = parentMap.get(node); + parent.val += node.val - 1; + res += Math.abs(node.val - 1); + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 4. Iterative DFS + +::tabs-start + +```python +# 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 +class Solution: + def distributeCoins(self, root: Optional[TreeNode]) -> int: + stack = [root] + res = 0 + visit = set() + + while stack: + node = stack.pop() + + if node not in visit: + stack.append(node) + visit.add(node) + + if node.right: + stack.append(node.right) + if node.left: + stack.append(node.left) + else: + if node.left: + node.val += node.left.val + if node.right: + node.val += node.right.val + + node.val -= 1 + res += abs(node.val) + visit.remove(node) + + return res +``` + +```java +/** + * 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; + * } + * } + */ +public class Solution { + public int distributeCoins(TreeNode root) { + Stack stack = new Stack<>(); + Set visit = new HashSet<>(); + stack.push(root); + int res = 0; + + while (!stack.isEmpty()) { + TreeNode node = stack.pop(); + + if (!visit.contains(node)) { + stack.push(node); + visit.add(node); + + if (node.right != null) { + stack.push(node.right); + } + if (node.left != null) { + stack.push(node.left); + } + } else { + if (node.left != null) { + node.val += node.left.val; + } + if (node.right != null) { + node.val += node.right.val; + } + + node.val -= 1; + res += Math.abs(node.val); + visit.remove(node); + } + } + + return res; + } +} +``` + +```cpp +/** + * 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: + int distributeCoins(TreeNode* root) { + stack stack; + unordered_set visit; + stack.push(root); + int res = 0; + + while (!stack.empty()) { + TreeNode* node = stack.top(); + stack.pop(); + + if (visit.find(node) == visit.end()) { + stack.push(node); + visit.insert(node); + + if (node->right) { + stack.push(node->right); + } + if (node->left) { + stack.push(node->left); + } + } else { + if (node->left) { + node->val += node->left->val; + } + if (node->right) { + node->val += node->right->val; + } + + visit.erase(node); + node->val -= 1; + res += abs(node->val); + } + } + + return res; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {number} + */ + distributeCoins(root) { + let stack = [root]; + let visit = new Set(); + let res = 0; + + while (stack.length) { + let node = stack.pop(); + + if (!visit.has(node)) { + stack.push(node); + visit.add(node); + + if (node.right) { + stack.push(node.right); + } + if (node.left) { + stack.push(node.left); + } + } else { + if (node.left) { + node.val += node.left.val; + } + if (node.right) { + node.val += node.right.val; + } + + visit.delete(node); + node.val -= 1; + res += Math.abs(node.val); + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/eliminate-maximum-number-of-monsters.md b/articles/eliminate-maximum-number-of-monsters.md new file mode 100644 index 000000000..551c82473 --- /dev/null +++ b/articles/eliminate-maximum-number-of-monsters.md @@ -0,0 +1,298 @@ +## 1. Sorting + +::tabs-start + +```python +class Solution: + def eliminateMaximum(self, dist: List[int], speed: List[int]) -> int: + minReach = [math.ceil(d / s) for d, s in zip(dist, speed)] + minReach.sort() + + res = 0 + for minute in range(len(minReach)): + if minute >= minReach[minute]: + return res + res += 1 + + return res +``` + +```java +public class Solution { + public int eliminateMaximum(int[] dist, int[] speed) { + int n = dist.length; + int[] minReach = new int[n]; + + for (int i = 0; i < n; i++) { + minReach[i] = (int) Math.ceil((double) dist[i] / speed[i]); + } + + Arrays.sort(minReach); + + int res = 0; + for (int minute = 0; minute < n; minute++) { + if (minute >= minReach[minute]) { + return res; + } + res++; + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int eliminateMaximum(vector& dist, vector& speed) { + int n = dist.size(); + vector minReach(n); + + for (int i = 0; i < n; i++) { + minReach[i] = ceil((double)dist[i] / speed[i]); + } + + sort(minReach.begin(), minReach.end()); + + int res = 0; + for (int minute = 0; minute < n; minute++) { + if (minute >= minReach[minute]) { + return res; + } + res++; + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} dist + * @param {number[]} speed + * @return {number} + */ + eliminateMaximum(dist, speed) { + let n = dist.length; + let minReach = new Array(n); + + for (let i = 0; i < n; i++) { + minReach[i] = Math.ceil(dist[i] / speed[i]); + } + + minReach.sort((a, b) => a - b); + + let res = 0; + for (let minute = 0; minute < n; minute++) { + if (minute >= minReach[minute]) { + return res; + } + res++; + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Sorting (Overwrting Input Array) + +::tabs-start + +```python +class Solution: + def eliminateMaximum(self, dist: List[int], speed: List[int]) -> int: + for i in range(len(dist)): + dist[i] = math.ceil(dist[i] / speed[i]) + + dist.sort() + for minute in range(len(dist)): + if minute >= dist[minute]: + return minute + + return len(dist) +``` + +```java +public class Solution { + public int eliminateMaximum(int[] dist, int[] speed) { + int n = dist.length; + for (int i = 0; i < n; i++) { + dist[i] = (int) Math.ceil((double) dist[i] / speed[i]); + } + + Arrays.sort(dist); + for (int minute = 0; minute < n; minute++) { + if (minute >= dist[minute]) { + return minute; + } + } + + return n; + } +} +``` + +```cpp +class Solution { +public: + int eliminateMaximum(vector& dist, vector& speed) { + int n = dist.size(); + for (int i = 0; i < n; i++) { + dist[i] = ceil((double)dist[i] / speed[i]); + } + + sort(dist.begin(), dist.end()); + for (int minute = 0; minute < n; minute++) { + if (minute >= dist[minute]) { + return minute; + } + } + + return n; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} dist + * @param {number[]} speed + * @return {number} + */ + eliminateMaximum(dist, speed) { + let n = dist.length; + for (let i = 0; i < n; i++) { + dist[i] = Math.ceil(dist[i] / speed[i]); + } + + dist.sort((a, b) => a - b); + for (let minute = 0; minute < n; minute++) { + if (minute >= dist[minute]) { + return minute; + } + } + + return n; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(1)$ or $O(n)$ depending on the sorting algorithm. + +--- + +## 3. Min-Heap + +::tabs-start + +```python +class Solution: + def eliminateMaximum(self, dist: List[int], speed: List[int]) -> int: + minHeap = [] + for i in range(len(dist)): + heapq.heappush(minHeap, dist[i] / speed[i]) + + res = 0 + while minHeap: + if res >= heapq.heappop(minHeap): + return res + res += 1 + + return res +``` + +```java +public class Solution { + public int eliminateMaximum(int[] dist, int[] speed) { + PriorityQueue minHeap = new PriorityQueue<>(); + for (int i = 0; i < dist.length; i++) { + minHeap.add((double) dist[i] / speed[i]); + } + + int res = 0; + while (!minHeap.isEmpty()) { + if (res >= minHeap.poll()) { + return res; + } + res++; + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int eliminateMaximum(vector& dist, vector& speed) { + priority_queue, greater> minHeap; + for (int i = 0; i < dist.size(); i++) { + minHeap.push((double)dist[i] / speed[i]); + } + + int res = 0; + while (!minHeap.empty()) { + if (res >= minHeap.top()) { + return res; + } + minHeap.pop(); + res++; + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} dist + * @param {number[]} speed + * @return {number} + */ + eliminateMaximum(dist, speed) { + const minHeap = new MinPriorityQueue(); + for (let i = 0; i < dist.length; i++) { + minHeap.enqueue(dist[i] / speed[i]); + } + + let res = 0; + while (!minHeap.isEmpty()) { + if (res >= minHeap.dequeue().element) { + return res; + } + res++; + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/find-all-people-with-secret.md b/articles/find-all-people-with-secret.md new file mode 100644 index 000000000..1d1b58e20 --- /dev/null +++ b/articles/find-all-people-with-secret.md @@ -0,0 +1,834 @@ +## 1. Depth First Search + +::tabs-start + +```python +class Solution: + def findAllPeople(self, n: int, meetings: list[list[int]], firstPerson: int) -> list[int]: + secrets = set([0, firstPerson]) # People with secret + time_map = {} # time -> adjacency list meetings + + for src, dst, t in meetings: + if t not in time_map: + time_map[t] = defaultdict(list) + time_map[t][src].append(dst) + time_map[t][dst].append(src) + + def dfs(src, adj): + if src in visit: + return + visit.add(src) + secrets.add(src) + for nei in adj[src]: + dfs(nei, adj) + + for t in sorted(time_map.keys()): + visit = set() + for src in time_map[t]: + if src in secrets: + dfs(src, time_map[t]) + + return list(secrets) +``` + +```java +public class Solution { + private Set secrets, visit; + + public List findAllPeople(int n, int[][] meetings, int firstPerson) { + secrets = new HashSet<>(); + visit = new HashSet<>(); + secrets.add(0); + secrets.add(firstPerson); + Map>> time_map = new HashMap<>(); + + for (int[] meet : meetings) { + int src = meet[0], dst = meet[1], t = meet[2]; + time_map.putIfAbsent(t, new HashMap<>()); + time_map.get(t).putIfAbsent(src, new ArrayList<>()); + time_map.get(t).putIfAbsent(dst, new ArrayList<>()); + time_map.get(t).get(src).add(dst); + time_map.get(t).get(dst).add(src); + } + + List timeKeys = new ArrayList<>(time_map.keySet()); + Collections.sort(timeKeys); + for (int t : timeKeys) { + visit = new HashSet<>(); + for (int src : time_map.get(t).keySet()) { + if (secrets.contains(src)) { + dfs(src, time_map.get(t)); + } + } + } + return new ArrayList<>(secrets); + } + + private void dfs(int src, Map> adj) { + if (!visit.add(src)) return; + secrets.add(src); + for (int nei : adj.getOrDefault(src, new ArrayList<>())) { + dfs(nei, adj); + } + } +} +``` + +```cpp +class Solution { +public: + unordered_set secrets, visit; + + vector findAllPeople(int n, vector>& meetings, int firstPerson) { + secrets = {0, firstPerson}; + unordered_map>> time_map; + + for (auto& meet : meetings) { + int src = meet[0], dst = meet[1], t = meet[2]; + time_map[t][src].push_back(dst); + time_map[t][dst].push_back(src); + } + + vector timeKeys; + for (auto& [t, _] : time_map) { + timeKeys.push_back(t); + } + sort(timeKeys.begin(), timeKeys.end()); + + for (int& t : timeKeys) { + visit.clear(); + for (auto& [src, _] : time_map[t]) { + if (secrets.count(src)) { + dfs(src, time_map[t]); + } + } + } + + return vector(secrets.begin(), secrets.end()); + } + +private: + void dfs(int src, unordered_map>& adj) { + if (!visit.insert(src).second) return; + secrets.insert(src); + for (int& nei : adj[src]) { + dfs(nei, adj); + } + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number[][]} meetings + * @param {number} firstPerson + * @return {number[]} + */ + findAllPeople(n, meetings, firstPerson) { + const secrets = new Set([0, firstPerson]); + let visit = new Set(); + let time_map = new Map(); + + for (let [src, dst, t] of meetings) { + if (!time_map.has(t)) time_map.set(t, new Map()); + if (!time_map.get(t).has(src)) time_map.get(t).set(src, []); + if (!time_map.get(t).has(dst)) time_map.get(t).set(dst, []); + time_map.get(t).get(src).push(dst); + time_map.get(t).get(dst).push(src); + } + const dfs = (src, adj) => { + if (visit.has(src)) return; + visit.add(src); + secrets.add(src); + for (let nei of (adj.get(src) || [])) { + dfs(nei, adj); + } + }; + + let timeKeys = [...time_map.keys()].sort((a, b) => a - b); + for (let t of timeKeys) { + visit.clear(); + for (let src of time_map.get(t).keys()) { + if (secrets.has(src)) { + dfs(src, time_map.get(t)); + } + } + } + + return [...secrets]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m \log m + n)$ +* Space complexity: $O(m + n)$ + +> Where $m$ is the number of meetings and $n$ is the number of people. + +--- + +## 2. Breadth First Search + +::tabs-start + +```python +class Solution: + def findAllPeople(self, n: int, meetings: list[list[int]], firstPerson: int) -> list[int]: + secrets = set([0, firstPerson]) # People with secret + time_map = {} # time -> adjacency list meetings + + for src, dst, t in meetings: + if t not in time_map: + time_map[t] = defaultdict(list) + time_map[t][src].append(dst) + time_map[t][dst].append(src) + + for t in sorted(time_map.keys()): + visit = set() + q = deque() + + for src in time_map[t]: + if src in secrets: + q.append(src) + visit.add(src) + + while q: + node = q.popleft() + secrets.add(node) + for nei in time_map[t][node]: + if nei not in visit: + visit.add(nei) + q.append(nei) + + return list(secrets) +``` + +```java +public class Solution { + public List findAllPeople(int n, int[][] meetings, int firstPerson) { + Set secrets = new HashSet<>(); + secrets.add(0); + secrets.add(firstPerson); + TreeMap>> time_map = new TreeMap<>(); + + for (int[] meet : meetings) { + int src = meet[0], dst = meet[1], t = meet[2]; + time_map.putIfAbsent(t, new HashMap<>()); + time_map.get(t).putIfAbsent(src, new ArrayList<>()); + time_map.get(t).putIfAbsent(dst, new ArrayList<>()); + time_map.get(t).get(src).add(dst); + time_map.get(t).get(dst).add(src); + } + + for (int t : time_map.keySet()) { + Set visit = new HashSet<>(); + Queue q = new LinkedList<>(); + + for (int src : time_map.get(t).keySet()) { + if (secrets.contains(src)) { + q.offer(src); + visit.add(src); + } + } + + while (!q.isEmpty()) { + int node = q.poll(); + secrets.add(node); + for (int nei : time_map.get(t).get(node)) { + if (!visit.contains(nei)) { + visit.add(nei); + q.offer(nei); + } + } + } + } + + return new ArrayList<>(secrets); + } +} +``` + +```cpp +class Solution { +public: + vector findAllPeople(int n, vector>& meetings, int firstPerson) { + unordered_set secrets = {0, firstPerson}; + map>> time_map; + + for (auto& meet : meetings) { + int src = meet[0], dst = meet[1], t = meet[2]; + time_map[t][src].push_back(dst); + time_map[t][dst].push_back(src); + } + + for (auto& [t, adj] : time_map) { + unordered_set visit; + queue q; + + for (auto& [src, _] : adj) { + if (secrets.count(src)) { + q.push(src); + visit.insert(src); + } + } + + while (!q.empty()) { + int node = q.front(); + q.pop(); + secrets.insert(node); + for (int nei : adj[node]) { + if (!visit.count(nei)) { + visit.insert(nei); + q.push(nei); + } + } + } + } + + return vector(secrets.begin(), secrets.end()); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number[][]} meetings + * @param {number} firstPerson + * @return {number[]} + */ + findAllPeople(n, meetings, firstPerson) { + const secrets = new Set([0, firstPerson]); + const time_map = new Map(); + + for (let [src, dst, t] of meetings) { + if (!time_map.has(t)) time_map.set(t, new Map()); + if (!time_map.get(t).has(src)) time_map.get(t).set(src, []); + if (!time_map.get(t).has(dst)) time_map.get(t).set(dst, []); + time_map.get(t).get(src).push(dst); + time_map.get(t).get(dst).push(src); + } + + for (let t of [...time_map.keys()].sort((a, b) => a - b)) { + let visit = new Set(); + const q = new Queue(); + + for (let src of time_map.get(t).keys()) { + if (secrets.has(src)) { + q.push(src); + visit.add(src); + } + } + + while (!q.isEmpty()) { + let node = q.pop(); + secrets.add(node); + for (let nei of time_map.get(t).get(node)) { + if (!visit.has(nei)) { + visit.add(nei); + q.push(nei); + } + } + } + } + + return [...secrets]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m \log m + n)$ +* Space complexity: $O(m + n)$ + +> Where $m$ is the number of meetings and $n$ is the number of people. + +--- + +## 3. Iterative DFS + +::tabs-start + +```python +class Solution: + def findAllPeople(self, n: int, meetings: list[list[int]], firstPerson: int) -> list[int]: + meetings.sort(key=lambda x: x[2]) # Sort by time + secrets = [False] * n + secrets[0] = secrets[firstPerson] = True + + for _, group in groupby(meetings, key=lambda x: x[2]): + adj = defaultdict(list) + visited = set() + + for u, v, _ in group: + adj[u].append(v) + adj[v].append(u) + if secrets[u]: + visited.add(u) + if secrets[v]: + visited.add(v) + + stack = list(visited) + while stack: + node = stack.pop() + for nei in adj[node]: + if nei not in visited: + visited.add(nei) + stack.append(nei) + secrets[nei] = True + + return [i for i in range(n) if secrets[i]] +``` + +```java +public class Solution { + public List findAllPeople(int n, int[][] meetings, int firstPerson) { + Arrays.sort(meetings, Comparator.comparingInt(a -> a[2])); + boolean[] secrets = new boolean[n]; + secrets[0] = secrets[firstPerson] = true; + + int i = 0, m = meetings.length; + while (i < m) { + int time = meetings[i][2]; + Map> adj = new HashMap<>(); + Set visited = new HashSet<>(); + + while (i < m && meetings[i][2] == time) { + int u = meetings[i][0], v = meetings[i][1]; + adj.computeIfAbsent(u, k -> new ArrayList<>()).add(v); + adj.computeIfAbsent(v, k -> new ArrayList<>()).add(u); + if (secrets[u]) visited.add(u); + if (secrets[v]) visited.add(v); + i++; + } + + Stack stack = new Stack<>(); + stack.addAll(visited); + while (!stack.isEmpty()) { + int node = stack.pop(); + for (int nei : adj.getOrDefault(node, Collections.emptyList())) { + if (!visited.contains(nei)) { + visited.add(nei); + stack.push(nei); + secrets[nei] = true; + } + } + } + } + + List res = new ArrayList<>(); + for (int j = 0; j < n; j++) { + if (secrets[j]) res.add(j); + } + return res; + } +} +``` + +```cpp +class Solution { +public: + vector findAllPeople(int n, vector>& meetings, int firstPerson) { + sort(meetings.begin(), meetings.end(), [](auto& a, auto& b) { + return a[2] < b[2]; + }); + + vector secrets(n, false); + secrets[0] = secrets[firstPerson] = true; + + int i = 0, m = meetings.size(); + while (i < m) { + int time = meetings[i][2]; + unordered_map> adj; + unordered_set visited; + + while (i < m && meetings[i][2] == time) { + int u = meetings[i][0], v = meetings[i][1]; + adj[u].push_back(v); + adj[v].push_back(u); + if (secrets[u]) visited.insert(u); + if (secrets[v]) visited.insert(v); + i++; + } + + stack stack(visited.begin(), visited.end()); + while (!stack.empty()) { + int node = stack.top(); stack.pop(); + for (int& nei : adj[node]) { + if (!visited.count(nei)) { + visited.insert(nei); + stack.push(nei); + secrets[nei] = true; + } + } + } + } + + vector res; + for (int j = 0; j < n; j++) { + if (secrets[j]) res.push_back(j); + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number[][]} meetings + * @param {number} firstPerson + * @return {number[]} + */ + findAllPeople(n, meetings, firstPerson) { + meetings.sort((a, b) => a[2] - b[2]); + const secrets = new Array(n).fill(false); + secrets[0] = secrets[firstPerson] = true; + + let i = 0, m = meetings.length; + while (i < m) { + const time = meetings[i][2]; + const adj = new Map(); + const visited = new Set(); + + while (i < m && meetings[i][2] === time) { + const [u, v] = meetings[i]; + if (!adj.has(u)) adj.set(u, []); + if (!adj.has(v)) adj.set(v, []); + adj.get(u).push(v); + adj.get(v).push(u); + if (secrets[u]) visited.add(u); + if (secrets[v]) visited.add(v); + i++; + } + + const stack = [...visited]; + while (stack.length) { + const node = stack.pop(); + for (const nei of (adj.get(node) || [])) { + if (!visited.has(nei)) { + visited.add(nei); + stack.push(nei); + secrets[nei] = true; + } + } + } + } + + let res = []; + for (let i = 0; i < n; i++) { + if (secrets[i]) res.push(i); + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m \log m + n)$ +* Space complexity: $O(m + n)$ + +> Where $m$ is the number of meetings and $n$ is the number of people. + +--- + +## 4. Disjoint Set Union + +::tabs-start + +```python +class DSU: + def __init__(self, n): + self.Parent = list(range(n + 1)) + self.Size = [1] * (n + 1) + + def find(self, node): + if self.Parent[node] != node: + self.Parent[node] = self.find(self.Parent[node]) + return self.Parent[node] + + def union(self, u, v): + pu, pv = self.find(u), self.find(v) + if pu == pv: + return False + if self.Size[pu] < self.Size[pv]: + pu, pv = pv, pu + self.Size[pu] += self.Size[pv] + self.Parent[pv] = pu + return True + + def reset(self, node): + self.Parent[node] = node + self.Size[node] = 1 + +class Solution: + def findAllPeople(self, n: int, meetings: list[list[int]], firstPerson: int) -> list[int]: + meetings.sort(key=lambda x: x[2]) # Sort by time + dsu = DSU(n) + dsu.union(0, firstPerson) + + for _, group in groupby(meetings, key=lambda x: x[2]): + group_nodes = set() + for u, v, _ in group: + dsu.union(u, v) + group_nodes.add(u) + group_nodes.add(v) + + for node in group_nodes: + if dsu.find(node) != dsu.find(0): + dsu.reset(node) + + return [i for i in range(n) if dsu.find(i) == dsu.find(0)] +``` + +```java +class DSU { + int[] Parent, Size; + + DSU(int n) { + Parent = new int[n + 1]; + Size = new int[n + 1]; + for (int i = 0; i <= n; i++) { + Parent[i] = i; + Size[i] = 1; + } + } + + int find(int node) { + if (Parent[node] != node) { + Parent[node] = find(Parent[node]); + } + return Parent[node]; + } + + void union(int u, int v) { + int pu = find(u), pv = find(v); + if (pu == pv) return; + if (Size[pu] < Size[pv]) { + int temp = pu; + pu = pv; + pv = temp; + } + Size[pu] += Size[pv]; + Parent[pv] = pu; + } + + void reset(int node) { + Parent[node] = node; + Size[node] = 1; + } +} + +public class Solution { + public List findAllPeople(int n, int[][] meetings, int firstPerson) { + Arrays.sort(meetings, Comparator.comparingInt(a -> a[2])); + DSU dsu = new DSU(n); + dsu.union(0, firstPerson); + + for (int i = 0; i < meetings.length; ) { + int time = meetings[i][2]; + Set group = new HashSet<>(); + + for (; i < meetings.length && meetings[i][2] == time; i++) { + int u = meetings[i][0], v = meetings[i][1]; + dsu.union(u, v); + group.add(u); + group.add(v); + } + + for (int node : group) { + if (dsu.find(node) != dsu.find(0)) { + dsu.reset(node); + } + } + } + + List result = new ArrayList<>(); + for (int i = 0; i < n; i++) { + if (dsu.find(i) == dsu.find(0)) result.add(i); + } + return result; + } +} +``` + +```cpp +class DSU { +public: + vector Parent, Size; + + DSU(int n) { + Parent.resize(n + 1); + Size.resize(n + 1, 1); + for (int i = 0; i <= n; i++) { + Parent[i] = i; + } + } + + int find(int node) { + if (Parent[node] != node) { + Parent[node] = find(Parent[node]); + } + return Parent[node]; + } + + void unionSets(int u, int v) { + int pu = find(u), pv = find(v); + if (pu == pv) return; + if (Size[pu] < Size[pv]) swap(pu, pv); + Size[pu] += Size[pv]; + Parent[pv] = pu; + } + + void reset(int node) { + Parent[node] = node; + Size[node] = 1; + } +}; + +class Solution { +public: + vector findAllPeople(int n, vector>& meetings, int firstPerson) { + sort(meetings.begin(), meetings.end(), [](auto &a, auto &b) { + return a[2] < b[2]; + }); + DSU dsu(n); + dsu.unionSets(0, firstPerson); + + for (int i = 0; i < meetings.size(); ) { + int time = meetings[i][2]; + unordered_set group; + + for (; i < meetings.size() && meetings[i][2] == time; i++) { + int u = meetings[i][0], v = meetings[i][1]; + dsu.unionSets(u, v); + group.insert(u); + group.insert(v); + } + + for (int node : group) { + if (dsu.find(node) != dsu.find(0)) { + dsu.reset(node); + } + } + } + + vector result; + for (int i = 0; i < n; i++) { + if (dsu.find(i) == dsu.find(0)) result.push_back(i); + } + return result; + } +}; +``` + +```javascript +class DSU { + /** + * @constructor + * @param {number} n + */ + constructor(n) { + this.Parent = Array.from({ length: n + 1 }, (_, i) => i); + this.Size = Array(n + 1).fill(1); + } + + /** + * @param {number} node + * @return {number} + */ + find(node) { + if (this.Parent[node] !== node) { + this.Parent[node] = this.find(this.Parent[node]); + } + return this.Parent[node]; + } + + /** + * @param {number} u + * @param {number} v + * @return {boolean} + */ + union(u, v) { + let pu = this.find(u); + let pv = this.find(v); + if (pu === pv) return false; + if (this.Size[pu] >= this.Size[pv]) { + this.Size[pu] += this.Size[pv]; + this.Parent[pv] = pu; + } else { + this.Size[pv] += this.Size[pu]; + this.Parent[pu] = pv; + } + return true; + } + + /** + * @param {number} node + * @return {void} + */ + reset(node) { + this.Parent[node] = node; + this.Size[node] = 1; + } +} + +class Solution { + /** + * @param {number} n + * @param {number[][]} meetings + * @param {number} firstPerson + * @return {number[]} + */ + findAllPeople(n, meetings, firstPerson) { + meetings.sort((a, b) => a[2] - b[2]); + let dsu = new DSU(n); + dsu.union(0, firstPerson); + + for (let i = 0; i < meetings.length; ) { + let time = meetings[i][2]; + let group = new Set(); + + for (; i < meetings.length && meetings[i][2] === time; i++) { + let [u, v] = meetings[i]; + dsu.union(u, v); + group.add(u); + group.add(v); + } + + group.forEach(node => { + if (dsu.find(node) !== dsu.find(0)) { + dsu.reset(node); + } + }); + } + + return [...Array(n).keys()].filter(i => dsu.find(i) === dsu.find(0)); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m \log m + (m * α(n)))$ +* Space complexity: + * $O(n)$ extra space. + * $O(m)$ space depending on the sorting algorithm. + +> Where $m$ is the number of meetings and $n$ is the number of people. \ No newline at end of file diff --git a/articles/find-the-maximum-sum-of-node-values.md b/articles/find-the-maximum-sum-of-node-values.md new file mode 100644 index 000000000..d48258892 --- /dev/null +++ b/articles/find-the-maximum-sum-of-node-values.md @@ -0,0 +1,637 @@ +## 1. Depth First Search + +::tabs-start + +```python +class Solution: + def maximumValueSum(self, nums: List[int], k: int, edges: List[List[int]]) -> int: + adj = [[] for _ in range(len(nums))] + for u, v in edges: + adj[u].append(v) + adj[v].append(u) + + def dfs(node, par): + res = [nums[node], nums[node] ^ k] + for child in adj[node]: + if child == par: + continue + + cur = dfs(child, node) + tmp = [] + tmp.append(max(res[0] + cur[0], res[1] + cur[1])) + tmp.append(max(res[1] + cur[0], res[0] + cur[1])) + res = tmp + + return res + + return dfs(0, -1)[0] +``` + +```java +public class Solution { + public long maximumValueSum(int[] nums, int k, int[][] edges) { + int n = nums.length; + List[] adj = new ArrayList[n]; + for (int i = 0; i < n; i++) adj[i] = new ArrayList<>(); + for (int[] edge : edges) { + adj[edge[0]].add(edge[1]); + adj[edge[1]].add(edge[0]); + } + + return dfs(0, -1, nums, k, adj)[0]; + } + + private long[] dfs(int node, int parent, int[] nums, int k, List[] adj) { + long[] res = { nums[node], nums[node] ^ k }; + for (int child : adj[node]) { + if (child == parent) continue; + + long[] cur = dfs(child, node, nums, k, adj); + long[] tmp = new long[2]; + tmp[0] = Math.max(res[0] + cur[0], res[1] + cur[1]); + tmp[1] = Math.max(res[1] + cur[0], res[0] + cur[1]); + res = tmp; + } + return res; + } +} +``` + +```cpp +class Solution { + vector> adj; + +public: + long long maximumValueSum(vector& nums, int k, vector>& edges) { + int n = nums.size(); + adj.resize(n); + for (const auto& edge : edges) { + adj[edge[0]].push_back(edge[1]); + adj[edge[1]].push_back(edge[0]); + } + + return dfs(0, -1, nums, k)[0]; + } + +private: + vector dfs(int node, int parent, vector& nums, int k) { + vector res = { nums[node], nums[node] ^ k }; + for (int child : adj[node]) { + if (child == parent) continue; + + vector cur = dfs(child, node, nums, k); + vector tmp(2); + tmp[0] = max(res[0] + cur[0], res[1] + cur[1]); + tmp[1] = max(res[1] + cur[0], res[0] + cur[1]); + res = tmp; + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @param {number[][]} edges + * @return {number} + */ + maximumValueSum(nums, k, edges) { + const n = nums.length; + const adj = Array.from({ length: n }, () => []); + + for (const [u, v] of edges) { + adj[u].push(v); + adj[v].push(u); + } + + const dfs = (node, parent) => { + let res = [nums[node], nums[node] ^ k]; + + for (const child of adj[node]) { + if (child === parent) continue; + + const cur = dfs(child, node); + const tmp = []; + tmp[0] = Math.max(res[0] + cur[0], res[1] + cur[1]); + tmp[1] = Math.max(res[1] + cur[0], res[0] + cur[1]); + res = tmp; + } + + return res; + }; + + return dfs(0, -1)[0]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def maximumValueSum(self, nums: List[int], k: int, edges: List[List[int]]) -> int: + dp = [[None] * 2 for _ in range(len(nums))] + [[0, float("-inf")]] + + def dfs(i, xorCnt): + if dp[i][xorCnt] is not None: + return dp[i][xorCnt] + + res = nums[i] + dfs(i + 1, xorCnt) + res = max(res, (nums[i] ^ k) + dfs(i + 1, xorCnt ^ 1)) + dp[i][xorCnt] = res + return res + + return dfs(0, 0) +``` + +```java +public class Solution { + private long[][] dp; + + public long maximumValueSum(int[] nums, int k, int[][] edges) { + int n = nums.length; + dp = new long[n + 1][2]; + for (long[] row : dp) Arrays.fill(row, Long.MIN_VALUE); + dp[n][0] = 0; + dp[n][1] = Integer.MIN_VALUE; + + return dfs(0, 0, nums, k); + } + + private long dfs(int i, int xorCnt, int[] nums, int k) { + if (dp[i][xorCnt] != Long.MIN_VALUE) { + return dp[i][xorCnt]; + } + + long res = nums[i] + dfs(i + 1, xorCnt, nums, k); + res = Math.max(res, (nums[i] ^ k) + dfs(i + 1, xorCnt ^ 1, nums, k)); + + return dp[i][xorCnt] = res; + } +} +``` + +```cpp +class Solution { + vector> dp; + +public: + long long maximumValueSum(vector& nums, int k, vector>& edges) { + int n = nums.size(); + dp.assign(n + 1, vector(2, LLONG_MIN)); + dp[n][0] = 0; + dp[n][1] = INT_MIN; + + return dfs(0, 0, nums, k); + } + +private: + long long dfs(int i, int xorCnt, vector& nums, int k) { + if (dp[i][xorCnt] != LLONG_MIN) { + return dp[i][xorCnt]; + } + + long long res = nums[i] + dfs(i + 1, xorCnt, nums, k); + res = max(res, (nums[i] ^ k) + dfs(i + 1, xorCnt ^ 1, nums, k)); + return dp[i][xorCnt] = res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @param {number[][]} edges + * @return {number} + */ + maximumValueSum(nums, k, edges) { + const n = nums.length; + const dp = Array.from({ length: n + 1 }, () => [null, null]); + dp[n][0] = 0; + dp[n][1] = -Infinity; + + const dfs = (i, xorCnt) => { + if (dp[i][xorCnt] !== null) return dp[i][xorCnt]; + + let res = nums[i] + dfs(i + 1, xorCnt); + res = Math.max(res, (nums[i] ^ k) + dfs(i + 1, xorCnt ^ 1)); + return dp[i][xorCnt] = res; + }; + + return dfs(0, 0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def maximumValueSum(self, nums: List[int], k: int, edges: List[List[int]]) -> int: + n = len(nums) + dp = [[0, 0] for _ in range(n + 1)] + dp[n][1] = float("-inf") + + for i in range(n - 1, -1, -1): + dp[i][0] = max(nums[i] + dp[i + 1][0], (nums[i] ^ k) + dp[i + 1][1]) + dp[i][1] = max(nums[i] + dp[i + 1][1], (nums[i] ^ k) + dp[i + 1][0]) + + return dp[0][0] +``` + +```java +public class Solution { + public long maximumValueSum(int[] nums, int k, int[][] edges) { + int n = nums.length; + long[][] dp = new long[n + 1][2]; + dp[n][1] = Integer.MIN_VALUE; + + for (int i = n - 1; i >= 0; i--) { + dp[i][0] = Math.max(nums[i] + dp[i + 1][0], (nums[i] ^ k) + dp[i + 1][1]); + dp[i][1] = Math.max(nums[i] + dp[i + 1][1], (nums[i] ^ k) + dp[i + 1][0]); + } + + return dp[0][0]; + } +} +``` + +```cpp +class Solution { +public: + long long maximumValueSum(vector& nums, int k, vector>& edges) { + int n = nums.size(); + vector> dp(n + 1, vector(2)); + dp[n][1] = INT_MIN; + + for (int i = n - 1; i >= 0; i--) { + dp[i][0] = max(nums[i] + dp[i + 1][0], (nums[i] ^ k) + dp[i + 1][1]); + dp[i][1] = max(nums[i] + dp[i + 1][1], (nums[i] ^ k) + dp[i + 1][0]); + } + + return dp[0][0]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @param {number[][]} edges + * @return {number} + */ + maximumValueSum(nums, k, edges) { + const n = nums.length; + const dp = Array.from({ length: n + 1 }, () => [0, 0]); + dp[n][1] = -Infinity; + + for (let i = n - 1; i >= 0; i--) { + dp[i][0] = Math.max(nums[i] + dp[i + 1][0], (nums[i] ^ k) + dp[i + 1][1]); + dp[i][1] = Math.max(nums[i] + dp[i + 1][1], (nums[i] ^ k) + dp[i + 1][0]); + } + + return dp[0][0]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 4. Dynamic Programming (Space Optimized) + +::tabs-start + +```python +class Solution: + def maximumValueSum(self, nums: List[int], k: int, edges: List[List[int]]) -> int: + dp = [0, float("-inf")] + + for i in range(len(nums) - 1, -1, -1): + next_dp = [0, 0] + next_dp[0] = max(nums[i] + dp[0], (nums[i] ^ k) + dp[1]) + next_dp[1] = max(nums[i] + dp[1], (nums[i] ^ k) + dp[0]) + dp = next_dp + + return dp[0] +``` + +```java +public class Solution { + public long maximumValueSum(int[] nums, int k, int[][] edges) { + int n = nums.length; + long[] dp = {0, Long.MIN_VALUE}; + + for (int i = n - 1; i >= 0; i--) { + long[] nextDp = new long[2]; + nextDp[0] = Math.max(nums[i] + dp[0], (nums[i] ^ k) + dp[1]); + nextDp[1] = Math.max(nums[i] + dp[1], (nums[i] ^ k) + dp[0]); + dp = nextDp; + } + + return dp[0]; + } +} +``` + +```cpp +class Solution { +public: + long long maximumValueSum(vector& nums, int k, vector>& edges) { + int n = nums.size(); + vector dp = {0, LLONG_MIN}; + + for (int i = n - 1; i >= 0; i--) { + vector nextDp(2); + nextDp[0] = max(nums[i] + dp[0], (nums[i] ^ k) + dp[1]); + nextDp[1] = max(nums[i] + dp[1], (nums[i] ^ k) + dp[0]); + dp = nextDp; + } + + return dp[0]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @param {number[][]} edges + * @return {number} + */ + maximumValueSum(nums, k, edges) { + const n = nums.length; + let dp = [0, -Infinity]; + + for (let i = n - 1; i >= 0; i--) { + let nextDp = [0, 0]; + nextDp[0] = Math.max(nums[i] + dp[0], (nums[i] ^ k) + dp[1]); + nextDp[1] = Math.max(nums[i] + dp[1], (nums[i] ^ k) + dp[0]); + dp = nextDp; + } + + return dp[0]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. + +--- + +## 5. Greedy + +::tabs-start + +```python +class Solution: + def maximumValueSum(self, nums: List[int], k: int, edges: List[List[int]]) -> int: + delta = [(num ^ k) - num for num in nums] + delta.sort(reverse=True) + res = sum(nums) + + for i in range(0, len(nums), 2): + if i == len(nums) - 1: + break + path_delta = delta[i] + delta[i + 1] + if path_delta <= 0: + break + res += path_delta + + return res +``` + +```java +public class Solution { + public long maximumValueSum(int[] nums, int k, int[][] edges) { + int n = nums.length; + int[] delta = new int[n]; + long res = 0; + for (int i = 0; i < n; i++) { + res += nums[i]; + delta[i] = (nums[i] ^ k) - nums[i]; + } + + Arrays.sort(delta); + for (int i = n - 1; i > 0; i -= 2) { + int pathDelta = delta[i] + delta[i - 1]; + if (pathDelta <= 0) { + break; + } + res += pathDelta; + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + long long maximumValueSum(vector& nums, int k, vector>& edges) { + int n = nums.size(); + vector delta(n); + long long res = 0; + for (int i = 0; i < n; i++) { + res += nums[i]; + delta[i] = (nums[i] ^ k) - nums[i]; + } + + sort(delta.rbegin(), delta.rend()); + + for (int i = 0; i + 1 < n; i += 2) { + int pathDelta = delta[i] + delta[i + 1]; + if (pathDelta <= 0) { + break; + } + res += pathDelta; + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @param {number[][]} edges + * @return {number} + */ + maximumValueSum(nums, k, edges) { + const n = nums.length; + let res = 0; + let delta = []; + for (let i = 0; i < n; i++) { + res += nums[i]; + delta.push((nums[i] ^ k) - nums[i]); + } + + delta.sort((a, b) => b - a); + for (let i = 0; i + 1 < n; i += 2) { + let pathDelta = delta[i] + delta[i + 1]; + if (pathDelta <= 0) { + break; + } + res += pathDelta; + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ + +--- + +## 6. Greedy (Optimal) + +::tabs-start + +```python +class Solution: + def maximumValueSum(self, nums: List[int], k: int, edges: List[List[int]]) -> int: + xorCnt = res = 0 + minDiff = 1 << 30 + + for num in nums: + xorNum = num ^ k + if xorNum > num: + res += xorNum + xorCnt ^= 1 + else: + res += num + minDiff = min(minDiff, abs(xorNum - num)) + + return res - xorCnt * minDiff +``` + +```java +public class Solution { + public long maximumValueSum(int[] nums, int k, int[][] edges) { + int xorCnt = 0, minDiff = 1 << 30; + long res = 0; + + for (int num : nums) { + int xorNum = num ^ k; + if (xorNum > num) { + res += xorNum; + xorCnt ^= 1; + } else { + res += num; + } + minDiff = Math.min(minDiff, Math.abs(xorNum - num)); + } + + return res - xorCnt * minDiff; + } +} +``` + +```cpp +class Solution { +public: + long long maximumValueSum(vector& nums, int k, vector>& edges) { + int xorCnt = 0, minDiff = 1 << 30; + long long res = 0; + + for (int& num : nums) { + int xorNum = num ^ k; + if (xorNum > num) { + res += xorNum; + xorCnt ^= 1; + } else { + res += num; + } + minDiff = min(minDiff, abs(xorNum - num)); + } + + return res - (xorCnt * minDiff); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @param {number[][]} edges + * @return {number} + */ + maximumValueSum(nums, k, edges) { + let xorCnt = 0, res = 0, minDiff = 1 << 30; + + for (let num of nums) { + let xorNum = num ^ k; + if (xorNum > num) { + res += xorNum; + xorCnt ^= 1; + } else { + res += num; + } + minDiff = Math.min(minDiff, Math.abs(xorNum - num)); + } + + return res - (xorCnt * minDiff); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. \ No newline at end of file diff --git a/articles/find-the-safest-path-in-a-grid.md b/articles/find-the-safest-path-in-a-grid.md new file mode 100644 index 000000000..f547fa423 --- /dev/null +++ b/articles/find-the-safest-path-in-a-grid.md @@ -0,0 +1,1054 @@ +## 1. Multi Source BFS + Dijkstra's Algorithm + +::tabs-start + +```python +class Solution: + def maximumSafenessFactor(self, grid: List[List[int]]) -> int: + N = len(grid) + + def in_bounds(r, c): + return min(r, c) >= 0 and max(r, c) < N + + def precompute(): + q = deque() + min_dist = {} + for r in range(N): + for c in range(N): + if grid[r][c]: + q.append([r, c, 0]) + min_dist[(r, c)] = 0 + while q: + r, c, dist = q.popleft() + nei = [[r+1, c], [r-1, c], [r, c+1], [r, c-1]] + for r2, c2 in nei: + if in_bounds(r2, c2) and (r2, c2) not in min_dist: + min_dist[(r2, c2)] = dist + 1 + q.append([r2, c2, dist + 1]) + return min_dist + + min_dist = precompute() + maxHeap = [(-min_dist[(0, 0)], 0, 0)] # (dist, r, c) + visit = set() + visit.add((0, 0)) + + while maxHeap: + dist, r, c = heapq.heappop(maxHeap) + dist = -dist + if (r, c) == (N-1, N-1): + return dist + nei = [[r+1, c], [r-1, c], [r, c+1], [r, c-1]] + for r2, c2 in nei: + if in_bounds(r2, c2) and (r2, c2) not in visit: + visit.add((r2, c2)) + dist2 = min(dist, min_dist[(r2, c2)]) + heapq.heappush(maxHeap, (-dist2, r2, c2)) +``` + +```java +public class Solution { + public int maximumSafenessFactor(List> grid) { + int N = grid.size(); + int[][] minDist = precompute(grid, N); + PriorityQueue maxHeap = new PriorityQueue<>((a, b) -> b[0] - a[0]); + boolean[][] visit = new boolean[N][N]; + + maxHeap.offer(new int[]{minDist[0][0], 0, 0}); + visit[0][0] = true; + + while (!maxHeap.isEmpty()) { + int[] curr = maxHeap.poll(); + int dist = curr[0], r = curr[1], c = curr[2]; + + if (r == N - 1 && c == N - 1) { + return dist; + } + + for (int[] dir : new int[][]{{1, 0}, {-1, 0}, {0, 1}, {0, -1}}) { + int r2 = r + dir[0], c2 = c + dir[1]; + if (inBounds(r2, c2, N) && !visit[r2][c2]) { + visit[r2][c2] = true; + int dist2 = Math.min(dist, minDist[r2][c2]); + maxHeap.offer(new int[]{dist2, r2, c2}); + } + } + } + return 0; + } + + private int[][] precompute(List> grid, int N) { + int[][] minDist = new int[N][N]; + for (int[] row : minDist) Arrays.fill(row, -1); + Queue q = new LinkedList<>(); + + for (int r = 0; r < N; r++) { + for (int c = 0; c < N; c++) { + if (grid.get(r).get(c) == 1) { + q.offer(new int[]{r, c, 0}); + minDist[r][c] = 0; + } + } + } + + while (!q.isEmpty()) { + int[] curr = q.poll(); + int r = curr[0], c = curr[1], dist = curr[2]; + + for (int[] dir : new int[][]{{1, 0}, {-1, 0}, {0, 1}, {0, -1}}) { + int r2 = r + dir[0], c2 = c + dir[1]; + if (inBounds(r2, c2, N) && minDist[r2][c2] == -1) { + minDist[r2][c2] = dist + 1; + q.offer(new int[]{r2, c2, dist + 1}); + } + } + } + return minDist; + } + + private boolean inBounds(int r, int c, int N) { + return r >= 0 && c >= 0 && r < N && c < N; + } +} +``` + +```cpp +class Solution { + static constexpr int directions[4][2] = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}}; + +public: + int maximumSafenessFactor(vector>& grid) { + int N = grid.size(); + vector> minDist = precompute(grid, N); + priority_queue> maxHeap; + vector> visit(N, vector(N, false)); + + maxHeap.push({minDist[0][0], 0, 0}); + visit[0][0] = true; + + while (!maxHeap.empty()) { + vector cur = maxHeap.top(); maxHeap.pop(); + int dist = cur[0], r = cur[1], c = cur[2]; + + if (r == N - 1 && c == N - 1) { + return dist; + } + + for (const auto& dir : directions) { + int r2 = r + dir[0], c2 = c + dir[1]; + if (inBounds(r2, c2, N) && !visit[r2][c2]) { + visit[r2][c2] = true; + int dist2 = min(dist, minDist[r2][c2]); + maxHeap.push({dist2, r2, c2}); + } + } + } + return 0; + } + +private: + vector> precompute(vector>& grid, int N) { + vector> minDist(N, vector(N, -1)); + queue> q; + + for (int r = 0; r < N; r++) { + for (int c = 0; c < N; c++) { + if (grid[r][c] == 1) { + q.push({r, c, 0}); + minDist[r][c] = 0; + } + } + } + + while (!q.empty()) { + vector cur = q.front(); + q.pop(); + int r = cur[0], c = cur[1], dist = cur[2]; + + for (const auto& dir : directions) { + int r2 = r + dir[0], c2 = c + dir[1]; + if (inBounds(r2, c2, N) && minDist[r2][c2] == -1) { + minDist[r2][c2] = dist + 1; + q.push({r2, c2, dist + 1}); + } + } + } + return minDist; + } + + bool inBounds(int r, int c, int N) { + return r >= 0 && c >= 0 && r < N && c < N; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} grid + * @return {number} + */ + maximumSafenessFactor(grid) { + const N = grid.length; + const directions = [[1, 0], [-1, 0], [0, 1], [0, -1]]; + + const inBounds = (r, c) => { + return r >= 0 && c >= 0 && r < N && c < N; + }; + + const precompute = () => { + const q = new Queue(); + const minDist = Array.from({ length: N }, () => Array(N).fill(-1)); + + for (let r = 0; r < N; r++) { + for (let c = 0; c < N; c++) { + if (grid[r][c] === 1) { + q.push([r, c, 0]); + minDist[r][c] = 0; + } + } + } + + while (!q.isEmpty()) { + let [r, c, dist] = q.pop(); + + for (let [dr, dc] of directions) { + let r2 = r + dr, c2 = c + dc; + if (inBounds(r2, c2) && minDist[r2][c2] === -1) { + minDist[r2][c2] = dist + 1; + q.push([r2, c2, dist + 1]); + } + } + } + return minDist; + }; + + const minDist = precompute(); + const maxHeap = new MaxPriorityQueue({ priority: x => x[0] }); + const visit = Array.from({ length: N }, () => Array(N).fill(false)); + + maxHeap.enqueue([minDist[0][0], 0, 0]); + visit[0][0] = true; + + while (!maxHeap.isEmpty()) { + let [dist, r, c] = maxHeap.dequeue().element; + + if (r === N - 1 && c === N - 1) { + return dist; + } + + for (let [dr, dc] of directions) { + let r2 = r + dr, c2 = c + dc; + if (inBounds(r2, c2) && !visit[r2][c2]) { + visit[r2][c2] = true; + let dist2 = Math.min(dist, minDist[r2][c2]); + maxHeap.enqueue([dist2, r2, c2]); + } + } + } + return 0; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2 \log n)$ +* Space complexity: $O(n ^ 2)$ + +--- + +## 2. Multi Source BFS + Dijkstra's Algorithm (Overwriting the Input) + +::tabs-start + +```python +class Solution: + def maximumSafenessFactor(self, grid): + N = len(grid) + minDist = grid + directions = [0, 1, 0, -1, 0] + + q = deque() + for r in range(N): + for c in range(N): + if grid[r][c] == 1: + q.append(r * N + c) + minDist[r][c] = 0 + else: + minDist[r][c] = -1 + + while q: + node = q.popleft() + r, c = divmod(node, N) + for i in range(4): + r2, c2 = r + directions[i], c + directions[i + 1] + if 0 <= r2 < N and 0 <= c2 < N and minDist[r2][c2] == -1: + minDist[r2][c2] = minDist[r][c] + 1 + q.append(r2 * N + c2) + + maxHeap = [(-minDist[0][0], 0)] + safeFactor = [0] * (N * N) + safeFactor[0] = minDist[0][0] + + while maxHeap: + dist, node = heapq.heappop(maxHeap) + dist = -dist + r, c = divmod(node, N) + if r == N - 1 and c == N - 1: + return dist + if safeFactor[node] > dist: + continue + + for i in range(4): + r2, c2 = r + directions[i], c + directions[i + 1] + node2 = r2 * N + c2 + if 0 <= r2 < N and 0 <= c2 < N: + dist2 = min(dist, minDist[r2][c2]) + if dist2 > safeFactor[node2]: + safeFactor[node2] = dist2 + heapq.heappush(maxHeap, (-dist2, node2)) + + return 0 +``` + +```java +public class Solution { + public int maximumSafenessFactor(List> grid) { + int N = grid.size(); + int[][] minDist = new int[N][N]; + int[] directions = {0, 1, 0, -1, 0}; + + Queue q = new LinkedList<>(); + for (int r = 0; r < N; r++) { + for (int c = 0; c < N; c++) { + if (grid.get(r).get(c) == 1) { + q.offer(r * N + c); + minDist[r][c] = 0; + } else { + minDist[r][c] = -1; + } + } + } + + while (!q.isEmpty()) { + int node = q.poll(); + int r = node / N, c = node % N; + for (int i = 0; i < 4; i++) { + int r2 = r + directions[i], c2 = c + directions[i + 1]; + if (r2 >= 0 && c2 >= 0 && r2 < N && c2 < N && minDist[r2][c2] == -1) { + minDist[r2][c2] = minDist[r][c] + 1; + q.offer(r2 * N + c2); + } + } + } + + PriorityQueue maxHeap = new PriorityQueue<>((a, b) -> Integer.compare(b[0], a[0])); + int[] safeFactor = new int[N * N]; + Arrays.fill(safeFactor, -1); + safeFactor[0] = minDist[0][0]; + maxHeap.offer(new int[]{safeFactor[0], 0}); + + while (!maxHeap.isEmpty()) { + int[] top = maxHeap.poll(); + int dist = top[0], node = top[1]; + int r = node / N, c = node % N; + if (r == N - 1 && c == N - 1) { + return dist; + } + if (safeFactor[node] > dist) { + continue; + } + + for (int i = 0; i < 4; i++) { + int r2 = r + directions[i], c2 = c + directions[i + 1]; + int node2 = r2 * N + c2; + if (r2 >= 0 && c2 >= 0 && r2 < N && c2 < N) { + int dist2 = Math.min(dist, minDist[r2][c2]); + if (dist2 > safeFactor[node2]) { + safeFactor[node2] = dist2; + maxHeap.offer(new int[]{dist2, node2}); + } + } + } + } + return 0; + } +} +``` + +```cpp +class Solution { +public: + int maximumSafenessFactor(vector>& grid) { + int N = grid.size(); + vector directions = {0, 1, 0, -1, 0}; + vector>& minDist = grid; + + queue q; + for (int r = 0; r < N; r++) { + for (int c = 0; c < N; c++) { + if (grid[r][c] == 1) { + q.push(r * N + c); + minDist[r][c] = 0; + } else { + minDist[r][c] = -1; + } + } + } + + while (!q.empty()) { + int node = q.front(); q.pop(); + int r = node / N, c = node % N; + for (int i = 0; i < 4; i++) { + int r2 = r + directions[i], c2 = c + directions[i + 1]; + if (r2 >= 0 && c2 >= 0 && r2 < N && c2 < N && minDist[r2][c2] == -1) { + minDist[r2][c2] = minDist[r][c] + 1; + q.push(r2 * N + c2); + } + } + } + + priority_queue> maxHeap; + vector safeFactor(N * N, 0); + safeFactor[0] = minDist[0][0]; + maxHeap.push({safeFactor[0], 0}); + + while (!maxHeap.empty()) { + auto [dist, node] = maxHeap.top(); maxHeap.pop(); + int r = node / N, c = node % N; + if (r == N - 1 && c == N - 1) { + return dist; + } + if (safeFactor[node] > dist) { + continue; + } + + for (int i = 0; i < 4; i++) { + int r2 = r + directions[i], c2 = c + directions[i + 1], node2 = r2 * N + c2; + if (r2 >= 0 && c2 >= 0 && r2 < N && c2 < N) { + int dist2 = min(dist, minDist[r2][c2]); + if (dist2 > safeFactor[node2]) { + safeFactor[node2] = dist2; + maxHeap.push({dist2, node2}); + } + } + } + } + return 0; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} grid + * @return {number} + */ + maximumSafenessFactor(grid) { + const N = grid.length; + const directions = [0, 1, 0, -1, 0]; + const minDist = grid; + + const q = new Queue(); + for (let r = 0; r < N; r++) { + for (let c = 0; c < N; c++) { + if (grid[r][c] === 1) { + q.push(r * N + c); + minDist[r][c] = 0; + } else { + minDist[r][c] = -1; + } + } + } + + while (!q.isEmpty()) { + let node = q.pop(); + let r = Math.floor(node / N), c = node % N; + for (let i = 0; i < 4; i++) { + let r2 = r + directions[i], c2 = c + directions[i + 1]; + if (r2 >= 0 && c2 >= 0 && r2 < N && c2 < N && minDist[r2][c2] === -1) { + minDist[r2][c2] = minDist[r][c] + 1; + q.push(r2 * N + c2); + } + } + } + + let maxHeap = new MaxPriorityQueue({ priority: x => x[0] }); + let safeFactor = new Array(N * N).fill(0); + safeFactor[0] = minDist[0][0]; + maxHeap.enqueue([safeFactor[0], 0]); + + while (!maxHeap.isEmpty()) { + let [dist, node] = maxHeap.dequeue().element; + let r = Math.floor(node / N), c = node % N; + if (r === N - 1 && c === N - 1) { + return dist; + } + if (safeFactor[node] > dist) { + continue; + } + + for (let i = 0; i < 4; i++) { + let r2 = r + directions[i], c2 = c + directions[i + 1], node2 = r2 * N + c2; + if (r2 >= 0 && c2 >= 0 && r2 < N && c2 < N) { + let dist2 = Math.min(dist, minDist[r2][c2]); + if (dist2 > safeFactor[node2]) { + safeFactor[node2] = dist2; + maxHeap.enqueue([dist2, node2]); + } + } + } + } + return 0; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2 \log n)$ +* Space complexity: $O(n ^ 2)$ + +--- + +## 3. Multi Source BFS + Binary Search + +::tabs-start + +```python +class Solution: + def maximumSafenessFactor(self, grid): + N = len(grid) + minDist = grid + directions = [0, 1, 0, -1, 0] + + q = deque() + for r in range(N): + for c in range(N): + if grid[r][c] == 1: + q.append(r * N + c) + minDist[r][c] = 0 + else: + minDist[r][c] = -1 + + while q: + node = q.popleft() + r, c = divmod(node, N) + for i in range(4): + r2, c2 = r + directions[i], c + directions[i + 1] + if 0 <= r2 < N and 0 <= c2 < N and minDist[r2][c2] == -1: + minDist[r2][c2] = minDist[r][c] + 1 + q.append(r2 * N + c2) + + def canReach(threshold): + q = deque([0]) + visited = [False] * (N * N) + visited[0] = True + + while q: + node = q.popleft() + r, c = divmod(node, N) + if r == N - 1 and c == N - 1: + return True + + for i in range(4): + r2, c2 = r + directions[i], c + directions[i + 1] + node2 = r2 * N + c2 + if (0 <= r2 < N and 0 <= c2 < N and not visited[node2] and + minDist[r2][c2] >= threshold + ): + visited[node2] = True + q.append(node2) + + return False + + l, r = 0, min(minDist[0][0], minDist[N - 1][N - 1]) + while l <= r: + mid = (l + r) // 2 + if canReach(mid): + l = mid + 1 + else: + r = mid - 1 + + return l - 1 +``` + +```java +public class Solution { + private static int[] directions = {0, 1, 0, -1, 0}; + + public int maximumSafenessFactor(List> grid) { + int N = grid.size(); + int[][] minDist = new int[N][N]; + + Queue q = new LinkedList<>(); + for (int r = 0; r < N; r++) { + for (int c = 0; c < N; c++) { + if (grid.get(r).get(c) == 1) { + q.offer(r * N + c); + minDist[r][c] = 0; + } else { + minDist[r][c] = -1; + } + } + } + + while (!q.isEmpty()) { + int node = q.poll(); + int r = node / N, c = node % N; + for (int i = 0; i < 4; i++) { + int r2 = r + directions[i], c2 = c + directions[i + 1]; + if (r2 >= 0 && c2 >= 0 && r2 < N && c2 < N && minDist[r2][c2] == -1) { + minDist[r2][c2] = minDist[r][c] + 1; + q.offer(r2 * N + c2); + } + } + } + + int l = 0, r = Math.min(minDist[0][0], minDist[N - 1][N - 1]); + while (l <= r) { + int mid = (l + r) / 2; + if (canReach(minDist, N, mid)) { + l = mid + 1; + } else { + r = mid - 1; + } + } + return l - 1; + } + + private boolean canReach(int[][] minDist, int N, int threshold) { + Queue q = new LinkedList<>(); + boolean[] visited = new boolean[N * N]; + q.offer(0); + visited[0] = true; + + while (!q.isEmpty()) { + int node = q.poll(); + int r = node / N, c = node % N; + if (r == N - 1 && c == N - 1) { + return true; + } + + for (int i = 0; i < 4; i++) { + int r2 = r + directions[i], c2 = c + directions[i + 1]; + int node2 = r2 * N + c2; + if (r2 >= 0 && c2 >= 0 && r2 < N && c2 < N && !visited[node2] && + minDist[r2][c2] >= threshold) { + visited[node2] = true; + q.offer(node2); + } + } + } + return false; + } +} +``` + +```cpp +class Solution { + static constexpr int directions[5] = {0, 1, 0, -1, 0}; + +public: + int maximumSafenessFactor(vector>& grid) { + int N = grid.size(); + vector>& minDist = grid; + + queue q; + for (int r = 0; r < N; r++) { + for (int c = 0; c < N; c++) { + if (grid[r][c] == 1) { + q.push(r * N + c); + minDist[r][c] = 0; + } else { + minDist[r][c] = -1; + } + } + } + + while (!q.empty()) { + int node = q.front(); q.pop(); + int r = node / N, c = node % N; + for (int i = 0; i < 4; i++) { + int r2 = r + directions[i], c2 = c + directions[i + 1]; + if (r2 >= 0 && c2 >= 0 && r2 < N && c2 < N && minDist[r2][c2] == -1) { + minDist[r2][c2] = minDist[r][c] + 1; + q.push(r2 * N + c2); + } + } + } + + int l = 0, r = min(minDist[0][0], minDist[N - 1][N - 1]); + while (l <= r) { + int mid = (l + r) / 2; + if (canReach(minDist, N, mid)) { + l = mid + 1; + } else { + r = mid - 1; + } + } + return l - 1; + } + +private: + bool canReach(vector>& minDist, int N, int threshold) { + queue q; + vector visited(N * N, false); + q.push(0); + visited[0] = true; + + while (!q.empty()) { + int node = q.front(); q.pop(); + int r = node / N, c = node % N; + if (r == N - 1 && c == N - 1) { + return true; + } + + for (int i = 0; i < 4; i++) { + int r2 = r + directions[i], c2 = c + directions[i + 1], node2 = r2 * N + c2; + if (r2 >= 0 && c2 >= 0 && r2 < N && c2 < N && !visited[node2] && + minDist[r2][c2] >= threshold) { + visited[node2] = true; + q.push(node2); + } + } + } + return false; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} grid + * @return {number} + */ + maximumSafenessFactor(grid) { + const N = grid.length; + const minDist = grid; + const directions = [0, 1, 0, -1, 0]; + + let q = new Queue(); + for (let r = 0; r < N; r++) { + for (let c = 0; c < N; c++) { + if (grid[r][c] === 1) { + q.push(r * N + c); + minDist[r][c] = 0; + } else { + minDist[r][c] = -1; + } + } + } + + while (!q.isEmpty()) { + let node = q.pop(); + let r = Math.floor(node / N), c = node % N; + for (let i = 0; i < 4; i++) { + let r2 = r + directions[i], c2 = c + directions[i + 1]; + if (r2 >= 0 && c2 >= 0 && r2 < N && c2 < N && minDist[r2][c2] === -1) { + minDist[r2][c2] = minDist[r][c] + 1; + q.push(r2 * N + c2); + } + } + } + + const canReach = (threshold) => { + q = new Queue([0]); + let visited = new Array(N * N).fill(false); + visited[0] = true; + + while (!q.isEmpty()) { + let node = q.pop(); + let r = Math.floor(node / N), c = node % N; + if (r === N - 1 && c === N - 1) return true; + + for (let i = 0; i < 4; i++) { + let r2 = r + directions[i], c2 = c + directions[i + 1], node2 = r2 * N + c2; + if (r2 >= 0 && c2 >= 0 && r2 < N && c2 < N && !visited[node2] && + minDist[r2][c2] >= threshold) { + visited[node2] = true; + q.push(node2); + } + } + } + return false; + }; + + let l = 0, r = Math.min(minDist[0][0], minDist[N - 1][N - 1]); + while (l <= r) { + let mid = Math.floor((l + r) / 2); + if (canReach(mid)) { + l = mid + 1; + } else { + r = mid - 1; + } + } + return l - 1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2 \log n)$ +* Space complexity: $O(n ^ 2)$ + +--- + +## 4. Breadth First Search (0-1 BFS) + +::tabs-start + +```python +class Solution: + def maximumSafenessFactor(self, grid): + N = len(grid) + minDist = grid + directions = [0, 1, 0, -1, 0] + + q = deque() + for r in range(N): + for c in range(N): + if grid[r][c] == 1: + q.append(r * N + c) + minDist[r][c] = 0 + else: + minDist[r][c] = -1 + + while q: + node = q.popleft() + r, c = divmod(node, N) + for i in range(4): + r2, c2 = r + directions[i], c + directions[i + 1] + if 0 <= r2 < N and 0 <= c2 < N and minDist[r2][c2] == -1: + minDist[r2][c2] = minDist[r][c] + 1 + q.append(r2 * N + c2) + + safeFactor = [-1] * (N * N) + res = safeFactor[0] = min(minDist[N - 1][N - 1], minDist[0][0]) + q.append(0) + + while q: + node = q.popleft() + r, c = divmod(node, N) + res = min(res, safeFactor[node]) + if r == N - 1 and c == N - 1: + break + + for i in range(4): + r2, c2 = r + directions[i], c + directions[i + 1] + node2 = r2 * N + c2 + if 0 <= r2 < N and 0 <= c2 < N and safeFactor[node2] == -1: + safeFactor[node2] = min(safeFactor[node], minDist[r2][c2]) + if safeFactor[node2] < res: + q.append(node2) + else: + q.appendleft(node2) + + return res +``` + +```java +public class Solution { + private static final int[] directions = {0, 1, 0, -1, 0}; + + public int maximumSafenessFactor(List> grid) { + int N = grid.size(); + int[][] minDist = new int[N][N]; + + Deque q = new ArrayDeque<>(); + for (int r = 0; r < N; r++) { + for (int c = 0; c < N; c++) { + if (grid.get(r).get(c) == 1) { + q.offerLast(r * N + c); + minDist[r][c] = 0; + } else { + minDist[r][c] = -1; + } + } + } + + while (!q.isEmpty()) { + int node = q.pollFirst(); + int r = node / N, c = node % N; + for (int i = 0; i < 4; i++) { + int r2 = r + directions[i], c2 = c + directions[i + 1]; + if (r2 >= 0 && c2 >= 0 && r2 < N && c2 < N && minDist[r2][c2] == -1) { + minDist[r2][c2] = minDist[r][c] + 1; + q.offerLast(r2 * N + c2); + } + } + } + + int[] safeFactor = new int[N * N]; + Arrays.fill(safeFactor, -1); + int res = safeFactor[0] = Math.min(minDist[N - 1][N - 1], minDist[0][0]); + q.offerLast(0); + + while (!q.isEmpty()) { + int node = q.pollFirst(); + int r = node / N, c = node % N; + res = Math.min(res, safeFactor[node]); + if (r == N - 1 && c == N - 1) { + break; + } + + for (int i = 0; i < 4; i++) { + int r2 = r + directions[i], c2 = c + directions[i + 1], node2 = r2 * N + c2; + if (r2 >= 0 && c2 >= 0 && r2 < N && c2 < N && safeFactor[node2] == -1) { + safeFactor[node2] = Math.min(safeFactor[node], minDist[r2][c2]); + if (safeFactor[node2] < res) { + q.offerLast(node2); + } else { + q.offerFirst(node2); + } + } + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int maximumSafenessFactor(vector>& grid) { + int N = grid.size(); + vector>& minDist = grid; + constexpr int directions[5] = {0, 1, 0, -1, 0}; + + deque q; + for (int r = 0; r < N; r++) { + for (int c = 0; c < N; c++) { + if (grid[r][c] == 1) { + q.push_back(r * N + c); + minDist[r][c] = 0; + } else { + minDist[r][c] = -1; + } + } + } + + while (!q.empty()) { + int node = q.front(); q.pop_front(); + int r = node / N, c = node % N; + for (int i = 0; i < 4; i++) { + int r2 = r + directions[i], c2 = c + directions[i + 1]; + if (r2 >= 0 && c2 >= 0 && r2 < N && c2 < N && minDist[r2][c2] == -1) { + minDist[r2][c2] = minDist[r][c] + 1; + q.push_back(r2 * N + c2); + } + } + } + + vector safeFactor(N * N, -1); + int res = safeFactor[0] = min(minDist[N - 1][N - 1], minDist[0][0]); + q.push_back(0); + + while (!q.empty()) { + int node = q.front(); q.pop_front(); + int r = node / N, c = node % N; + res = min(res, safeFactor[node]); + if (r == N - 1 && c == N - 1) { + break; + } + + for (int i = 0; i < 4; i++) { + int r2 = r + directions[i], c2 = c + directions[i + 1], node2 = r2 * N + c2; + if (r2 >= 0 && c2 >= 0 && r2 < N && c2 < N && safeFactor[node2] == -1) { + safeFactor[node2] = min(safeFactor[node], minDist[r2][c2]); + if (safeFactor[node2] < res) { + q.push_back(node2); + } else { + q.push_front(node2); + } + } + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} grid + * @return {number} + */ + maximumSafenessFactor(grid) { + const N = grid.length; + const minDist = grid; + const directions = [0, 1, 0, -1, 0]; + + const q = new Deque(); + for (let r = 0; r < N; r++) { + for (let c = 0; c < N; c++) { + if (grid[r][c] === 1) { + q.pushBack(r * N + c); + minDist[r][c] = 0; + } else { + minDist[r][c] = -1; + } + } + } + + while (!q.isEmpty()) { + let node = q.popFront(); + let r = Math.floor(node / N), c = node % N; + for (let i = 0; i < 4; i++) { + let r2 = r + directions[i], c2 = c + directions[i + 1]; + if (r2 >= 0 && c2 >= 0 && r2 < N && c2 < N && minDist[r2][c2] === -1) { + minDist[r2][c2] = minDist[r][c] + 1; + q.pushBack(r2 * N + c2); + } + } + } + + let safeFactor = new Array(N * N).fill(-1); + let res = safeFactor[0] = Math.min(minDist[N - 1][N - 1], minDist[0][0]); + q.pushBack(0); + + while (!q.isEmpty()) { + let node = q.popFront(); + let r = Math.floor(node / N), c = node % N; + res = Math.min(res, safeFactor[node]); + if (r === N - 1 && c === N - 1) { + break; + } + + for (let i = 0; i < 4; i++) { + let r2 = r + directions[i], c2 = c + directions[i + 1], node2 = r2 * N + c2; + if (r2 >= 0 && c2 >= 0 && r2 < N && c2 < N && safeFactor[node2] === -1) { + safeFactor[node2] = Math.min(safeFactor[node], minDist[r2][c2]); + if (safeFactor[node2] < res) { + q.pushBack(node2); + } else { + q.pushFront(node2); + } + } + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n ^ 2)$ \ No newline at end of file diff --git a/articles/find-unique-binary-string.md b/articles/find-unique-binary-string.md new file mode 100644 index 000000000..ea24db9f9 --- /dev/null +++ b/articles/find-unique-binary-string.md @@ -0,0 +1,685 @@ +## 1. Backtracking (Recursion) + +::tabs-start + +```python +class Solution: + def findDifferentBinaryString(self, nums: List[str]) -> str: + strSet = {s for s in nums} + + def backtrack(i, cur): + if i == len(nums): + res = "".join(cur) + return None if res in strSet else res + + res = backtrack(i + 1, cur) + if res: return res + + cur[i] = "1" + return backtrack(i + 1, cur) + + return backtrack(0, ["0" for _ in nums]) +``` + +```java +public class Solution { + public String findDifferentBinaryString(String[] nums) { + Set strSet = new HashSet<>(); + for (String s : nums) { + strSet.add(s); + } + return backtrack(0, new char[nums.length], strSet, nums.length); + } + + private String backtrack(int i, char[] cur, Set strSet, int n) { + if (i == n) { + String res = new String(cur); + return strSet.contains(res) ? null : res; + } + + cur[i] = '0'; + String res = backtrack(i + 1, cur, strSet, n); + if (res != null) return res; + + cur[i] = '1'; + return backtrack(i + 1, cur, strSet, n); + } +} +``` + +```cpp +class Solution { +public: + string findDifferentBinaryString(vector& nums) { + unordered_set strSet(nums.begin(), nums.end()); + string cur(nums.size(), '0'); + return backtrack(0, cur, strSet, nums.size()); + } + +private: + string backtrack(int i, string& cur, unordered_set& strSet, int n) { + if (i == n) { + return strSet.count(cur) ? "" : cur; + } + + string res = backtrack(i + 1, cur, strSet, n); + if (!res.empty()) return res; + + cur[i] = '1'; + return backtrack(i + 1, cur, strSet, n); + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} nums + * @return {string} + */ + findDifferentBinaryString(nums) { + const strSet = new Set(nums); + const n = nums.length; + + const backtrack = (i, cur) => { + if (i === n) { + const res = cur.join(""); + return strSet.has(res) ? null : res; + } + + let res = backtrack(i + 1, cur); + if (res) return res; + + cur[i] = "1"; + return backtrack(i + 1, cur); + }; + + return backtrack(0, Array(n).fill("0")); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n)$ + +--- + +## 2. Backtracking (Iteration) + +::tabs-start + +```python +class Solution: + def findDifferentBinaryString(self, nums: List[str]) -> str: + strSet = set(nums) + n = len(nums) + + for num in range(1 << n): + res = bin(num)[2:].zfill(n) + if res not in strSet: + return res + + return "" +``` + +```java +public class Solution { + public String findDifferentBinaryString(String[] nums) { + Set strSet = new HashSet<>(); + for (String s : nums) { + strSet.add(s); + } + int n = nums.length; + + for (int num = 0; num < (n + 1); num++) { + String res = String.format("%" + n + "s", + Integer.toBinaryString(num)).replace(' ', '0'); + if (!strSet.contains(res)) { + return res; + } + } + + return ""; + } +} +``` + +```cpp +class Solution { +public: + string findDifferentBinaryString(vector& nums) { + unordered_set strSet(nums.begin(), nums.end()); + int n = nums.size(); + + for (int num = 0; num < (n + 1); num++) { + string res = toBinaryString(num, n); + if (strSet.find(res) == strSet.end()) { + return res; + } + } + + return ""; + } + +private: + string toBinaryString(int num, int length) { + string res = ""; + for (int i = length - 1; i >= 0; i--) { + res += (num & (1 << i)) ? '1' : '0'; + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} nums + * @return {string} + */ + findDifferentBinaryString(nums) { + const strSet = new Set(nums); + const n = nums.length; + + for (let num = 0; num < (n + 1); num++) { + let res = num.toString(2).padStart(n, '0'); + if (!strSet.has(res)) { + return res; + } + } + + return ""; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n)$ + +--- + +## 3. Cantor's Diagonal Argument + +::tabs-start + +```python +class Solution: + def findDifferentBinaryString(self, nums: List[str]) -> str: + res = [] + for i in range(len(nums)): + if nums[i][i] == '0': + res.append('1') + else: + res.append('0') + return "".join(res) +``` + +```java +public class Solution { + public String findDifferentBinaryString(String[] nums) { + StringBuilder res = new StringBuilder(); + for (int i = 0; i < nums.length; i++) { + res.append(nums[i].charAt(i) == '0' ? '1' : '0'); + } + return res.toString(); + } +} +``` + +```cpp +class Solution { +public: + string findDifferentBinaryString(vector& nums) { + string res; + for (int i = 0; i < nums.size(); i++) { + res += (nums[i][i] == '0') ? '1' : '0'; + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} nums + * @return {string} + */ + findDifferentBinaryString(nums) { + let res = []; + for (let i = 0; i < nums.length; i++) { + res.push(nums[i][i] === '0' ? '1' : '0'); + } + return res.join(""); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: + * $O(1)$ extra space. + * $O(n)$ space for the output string. + +--- + +## 4. Randomization + +::tabs-start + +```python +class Solution: + def findDifferentBinaryString(self, nums: List[str]) -> str: + strSet = set(nums) + n = len(nums) + + while True: + res = "".join(random.choice("01") for _ in range(n)) + if res not in strSet: + return res +``` + +```java +public class Solution { + public String findDifferentBinaryString(String[] nums) { + Set strSet = new HashSet<>(); + for (String s : nums) { + strSet.add(s); + } + int n = nums.length; + Random random = new Random(); + + while (true) { + StringBuilder res = new StringBuilder(); + for (int i = 0; i < n; i++) { + res.append(random.nextBoolean() ? '1' : '0'); + } + String result = res.toString(); + if (!strSet.contains(result)) { + return result; + } + } + } +} +``` + +```cpp +class Solution { +public: + string findDifferentBinaryString(vector& nums) { + unordered_set strSet(nums.begin(), nums.end()); + int n = nums.size(); + + while (true) { + string res = ""; + for (int i = 0; i < n; i++) { + res += (rand() % 2) ? '1' : '0'; + } + if (strSet.find(res) == strSet.end()) { + return res; + } + } + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} nums + * @return {string} + */ + findDifferentBinaryString(nums) { + const strSet = new Set(nums); + const n = nums.length; + + while (true) { + let res = Array.from({ length: n }, () => + Math.random() < 0.5 ? '0' : '1').join(""); + if (!strSet.has(res)) { + return res; + } + } + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(∞)$ in worst case. +* Space complexity: $O(n)$ + +--- + +## 5. Trie + +::tabs-start + +```python +class Node: + def __init__(self): + self.children = [None, None] + + def contains_bit(self, bit: int) -> bool: + return self.children[bit] is not None + + def put(self, bit: int): + self.children[bit] = Node() + + def get(self, bit: int): + return self.children[bit] + +class Trie: + def __init__(self): + self.root = Node() + + def insert(self, s: str): + curr = self.root + for c in s: + bit = int(c) + if not curr.contains_bit(bit): + curr.put(bit) + curr = curr.get(bit) + + def search(self, res: str, curr) -> bool: + while curr.contains_bit(0) or curr.contains_bit(1): + if not curr.contains_bit(0): + res.append('0') + return True + if not curr.contains_bit(1): + res.append('1') + return True + + res.append('1') + curr = curr.get(1) + + return False + +class Solution: + def findDifferentBinaryString(self, nums: List[str]) -> str: + trie = Trie() + for s in nums: + trie.insert(s) + + res = [] + trie.search(res, trie.root) + + while len(res) < len(nums): + res.append('1') + + return ''.join(res) +``` + +```java +class Node { + Node[] children; + + Node() { + this.children = new Node[2]; + } + + boolean containsBit(int bit) { + return this.children[bit] != null; + } + + void put(int bit) { + this.children[bit] = new Node(); + } + + Node get(int bit) { + return this.children[bit]; + } +} + +class Trie { + Node root; + + Trie() { + this.root = new Node(); + } + + void insert(String s) { + Node curr = root; + for (char c : s.toCharArray()) { + int bit = c - '0'; + if (!curr.containsBit(bit)) { + curr.put(bit); + } + curr = curr.get(bit); + } + } + + boolean search(StringBuilder res, Node curr) { + while (curr.containsBit(0) || curr.containsBit(1)) { + if (!curr.containsBit(0)) { + res.append('0'); + return true; + } + if (!curr.containsBit(1)) { + res.append('1'); + return true; + } + + res.append('1'); + curr = curr.get(1); + } + + return false; + } +} + +public class Solution { + public String findDifferentBinaryString(String[] nums) { + Trie trie = new Trie(); + for (String s : nums) { + trie.insert(s); + } + + StringBuilder res = new StringBuilder(); + trie.search(res, trie.root); + + while (res.length() < nums.length) { + res.append('1'); + } + + return res.toString(); + } +} +``` + +```cpp +class Node { +public: + Node *children[2]; + + Node() { + this->children[0] = nullptr; + this->children[1] = nullptr; + } + + bool containsBit(int bit) { + return this->children[bit] != nullptr; + } + + void put(int bit) { + this->children[bit] = new Node(); + } + + Node* get(int bit) { + return this->children[bit]; + } +}; + +class Trie { +public: + Node* root; + + Trie() { + this->root = new Node(); + } + + void insert(const string& s) { + Node* curr = root; + for (char c : s) { + int bit = c - '0'; + if (!curr->containsBit(bit)) { + curr->put(bit); + } + curr = curr->get(bit); + } + } + + bool search(string& res, Node* curr) { + while (curr->containsBit(0) || curr->containsBit(1)) { + if (!curr->containsBit(0)) { + res += '0'; + return true; + } + if (!curr->containsBit(1)) { + res += '1'; + return true; + } + + res += '1'; + curr = curr->get(1); + } + + return false; + } +}; + +class Solution { +public: + string findDifferentBinaryString(vector& nums) { + Trie trie; + for (const string& s : nums) { + trie.insert(s); + } + + string res; + trie.search(res, trie.root); + + while (res.length() < nums.size()) { + res += '1'; + } + + return res; + } +}; +``` + +```javascript +class Node { + constructor() { + this.children = [null, null]; + } + + /** + * @param {number} bit + * @return {boolean} + */ + containsBit(bit) { + return this.children[bit] !== null; + } + + /** + * @param {number} bit + */ + put(bit) { + this.children[bit] = new Node(); + } + + /** + * @param {number} bit + * @return {Node} + */ + get(bit) { + return this.children[bit]; + } +} + +class Trie { + constructor() { + this.root = new Node(); + } + + /** + * @param {string} s + */ + insert(s) { + let curr = this.root; + for (const c of s) { + let bit = c === '1' ? 1 : 0; + if (!curr.containsBit(bit)) { + curr.put(bit); + } + curr = curr.get(bit); + } + } + + /** + * @param {string[]} res + * @param {Node} curr + * @return {boolean} + */ + search(res, curr) { + while (curr.containsBit(0) || curr.containsBit(1)) { + if (!curr.containsBit(0)) { + res.push('0'); + return true; + } + if (!curr.containsBit(1)) { + res.push('1'); + return true; + } + + res.push('1'); + curr = curr.get(1); + } + return false; + } +} + +class Solution { + /** + * @param {string[]} nums + * @return {string} + */ + findDifferentBinaryString(nums) { + let trie = new Trie(); + for (const s of nums) { + trie.insert(s); + } + + let res = []; + trie.search(res, trie.root); + + while (res.length < nums.length) { + res.push('1'); + } + + return res.join(""); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n ^ 2)$ \ No newline at end of file diff --git a/articles/furthest-building-you-can-reach.md b/articles/furthest-building-you-can-reach.md new file mode 100644 index 000000000..24ca0ef3b --- /dev/null +++ b/articles/furthest-building-you-can-reach.md @@ -0,0 +1,723 @@ +## 1. Brute Force (Greedy) + +::tabs-start + +```python +class Solution: + def furthestBuilding(self, heights: List[int], bricks: int, ladders: int) -> int: + n = len(heights) + + for i in range(1, n): + if ladders >= i: + continue + + diffs = [] + for j in range(i): + if heights[j + 1] > heights[j]: + diffs.append(heights[j + 1] - heights[j]) + + diffs.sort() + brickSum = 0 + for j in range(len(diffs) - ladders): + brickSum += diffs[j] + + if brickSum > bricks: + return i - 1 + + return n - 1 +``` + +```java +public class Solution { + public int furthestBuilding(int[] heights, int bricks, int ladders) { + int n = heights.length; + + for (int i = 1; i < n; i++) { + if (ladders >= i) { + continue; + } + + List diffs = new ArrayList<>(); + for (int j = 0; j < i; j++) { + if (heights[j + 1] > heights[j]) { + diffs.add(heights[j + 1] - heights[j]); + } + } + + Collections.sort(diffs); + long brickSum = 0; + for (int j = 0; j < diffs.size() - ladders; j++) { + brickSum += diffs.get(j); + } + + if (brickSum > bricks) { + return i - 1; + } + } + + return n - 1; + } +} +``` + +```cpp +class Solution { +public: + int furthestBuilding(vector& heights, int bricks, int ladders) { + int n = heights.size(); + + for (int i = 1; i < n; i++) { + if (ladders >= i) { + continue; + } + + vector diffs; + for (int j = 0; j < i; j++) { + if (heights[j + 1] > heights[j]) { + diffs.push_back(heights[j + 1] - heights[j]); + } + } + + sort(diffs.begin(), diffs.end()); + long long brickSum = 0; + for (int j = 0; j < int(diffs.size()) - ladders; j++) { + brickSum += diffs[j]; + } + + if (brickSum > bricks) { + return i - 1; + } + } + + return n - 1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} heights + * @param {number} bricks + * @param {number} ladders + * @return {number} + */ + furthestBuilding(heights, bricks, ladders) { + let n = heights.length; + + for (let i = 1; i < n; i++) { + if (ladders >= i) { + continue; + } + + let diffs = []; + for (let j = 0; j < i; j++) { + if (heights[j + 1] > heights[j]) { + diffs.push(heights[j + 1] - heights[j]); + } + } + + diffs.sort((a, b) => a - b); + let brickSum = 0; + for (let j = 0; j < diffs.length - ladders; j++) { + brickSum += diffs[j]; + } + + if (brickSum > bricks) { + return i - 1; + } + } + + return n - 1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2 \log n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Binary Search On Buildings + +::tabs-start + +```python +class Solution: + def furthestBuilding(self, heights: List[int], bricks: int, ladders: int) -> int: + def canReach(mid): + diffs = [] + for i in range(mid): + if heights[i + 1] > heights[i]: + diffs.append(heights[i + 1] - heights[i]) + + diffs.sort() + brickSum = 0 + for j in range(len(diffs) - ladders): + brickSum += diffs[j] + if brickSum > bricks: + return False + + return True + + l, r = ladders - 1, len(heights) - 1 + while l <= r: + mid = (l + r) // 2 + if canReach(mid): + l = mid + 1 + else: + r = mid - 1 + + return l - 1 +``` + +```java +public class Solution { + public int furthestBuilding(int[] heights, int bricks, int ladders) { + int l = ladders - 1, r = heights.length - 1; + + while (l <= r) { + int mid = (l + r) / 2; + if (canReach(heights, mid, bricks, ladders)) { + l = mid + 1; + } else { + r = mid - 1; + } + } + + return l - 1; + } + + private boolean canReach(int[] heights, int mid, int bricks, int ladders) { + List diffs = new ArrayList<>(); + + for (int i = 0; i < mid; i++) { + if (heights[i + 1] > heights[i]) { + diffs.add(heights[i + 1] - heights[i]); + } + } + + Collections.sort(diffs); + int brickSum = 0; + + for (int j = 0; j < diffs.size() - ladders; j++) { + brickSum += diffs.get(j); + if (brickSum > bricks) { + return false; + } + } + + return true; + } +} +``` + +```cpp +class Solution { +public: + int furthestBuilding(vector& heights, int bricks, int ladders) { + int l = ladders - 1, r = heights.size() - 1; + + while (l <= r) { + int mid = (l + r) / 2; + if (canReach(heights, mid, bricks, ladders)) { + l = mid + 1; + } else { + r = mid - 1; + } + } + + return l - 1; + } + +private: + bool canReach(vector& heights, int mid, int bricks, int ladders) { + vector diffs; + + for (int i = 0; i < mid; i++) { + if (heights[i + 1] > heights[i]) { + diffs.push_back(heights[i + 1] - heights[i]); + } + } + + sort(diffs.begin(), diffs.end()); + long long brickSum = 0; + + for (int j = 0; j < int(diffs.size()) - ladders; j++) { + brickSum += diffs[j]; + if (brickSum > bricks) { + return false; + } + } + + return true; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} heights + * @param {number} bricks + * @param {number} ladders + * @return {number} + */ + furthestBuilding(heights, bricks, ladders) { + let l = ladders - 1, r = heights.length - 1; + + const canReach = (mid) => { + let diffs = []; + for (let i = 0; i < mid; i++) { + if (heights[i + 1] > heights[i]) { + diffs.push(heights[i + 1] - heights[i]); + } + } + + diffs.sort((a, b) => a - b); + let brickSum = 0; + + for (let j = 0; j < diffs.length - ladders; j++) { + brickSum += diffs[j]; + if (brickSum > bricks) { + return false; + } + } + + return true; + }; + + while (l <= r) { + let mid = Math.floor((l + r) / 2); + if (canReach(mid)) { + l = mid + 1; + } else { + r = mid - 1; + } + } + + return l - 1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log ^ 2 n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Binary Search On Buildings (Optimal) + +::tabs-start + +```python +class Solution: + def furthestBuilding(self, heights: List[int], bricks: int, ladders: int) -> int: + diffs = [] + for i in range(1, len(heights)): + if heights[i] > heights[i - 1]: + diffs.append((heights[i] - heights[i - 1], i)) + + diffs.sort(reverse=True) + + def canReach(index): + useLadders = useBricks = 0 + for diff, i in diffs: + if i > index: + continue + + if useLadders < ladders: + useLadders += 1 + else: + useBricks += diff + if useBricks > bricks: + return False + return True + + l, r = 1, len(heights) - 1 + while l <= r: + mid = (l + r) >> 1 + if canReach(mid): + l = mid + 1 + else: + r = mid - 1 + return l - 1 +``` + +```java +public class Solution { + public int furthestBuilding(int[] heights, int bricks, int ladders) { + List diffs = new ArrayList<>(); + for (int i = 1; i < heights.length; i++) { + if (heights[i] > heights[i - 1]) { + diffs.add(new int[]{heights[i] - heights[i - 1], i}); + } + } + + diffs.sort((a, b) -> Integer.compare(b[0], a[0])); + + int l = 1, r = heights.length - 1; + while (l <= r) { + int mid = (l + r) >> 1; + if (canReach(diffs, mid, bricks, ladders)) { + l = mid + 1; + } else { + r = mid - 1; + } + } + + return l - 1; + } + + private boolean canReach(List diffs, int index, int bricks, int ladders) { + int useLadders = 0; + long useBricks = 0; + for (int[] diff : diffs) { + int jump = diff[0], i = diff[1]; + + if (i > index) continue; + + if (useLadders < ladders) { + useLadders++; + } else { + useBricks += jump; + if (useBricks > bricks) { + return false; + } + } + } + return true; + } +} +``` + +```cpp +class Solution { +public: + int furthestBuilding(vector& heights, int bricks, int ladders) { + vector> diffs; + for (int i = 1; i < heights.size(); i++) { + if (heights[i] > heights[i - 1]) { + diffs.emplace_back(heights[i] - heights[i - 1], i); + } + } + + sort(diffs.rbegin(), diffs.rend()); // Sort in descending order + + int l = 1, r = heights.size() - 1; + while (l <= r) { + int mid = (l + r) >> 1; + if (canReach(diffs, mid, bricks, ladders)) { + l = mid + 1; + } else { + r = mid - 1; + } + } + + return l - 1; + } + +private: + bool canReach(vector>& diffs, int index, int bricks, int ladders) { + int useLadders = 0; + long long useBricks = 0; + for (auto& diff : diffs) { + int jump = diff.first, i = diff.second; + + if (i > index) continue; + + if (useLadders < ladders) { + useLadders++; + } else { + useBricks += jump; + if (useBricks > bricks) { + return false; + } + } + } + return true; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} heights + * @param {number} bricks + * @param {number} ladders + * @return {number} + */ + furthestBuilding(heights, bricks, ladders) { + let diffs = []; + for (let i = 1; i < heights.length; i++) { + if (heights[i] > heights[i - 1]) { + diffs.push([heights[i] - heights[i - 1], i]); + } + } + + diffs.sort((a, b) => b[0] - a[0]); + + const canReach = (index) => { + let useLadders = 0, useBricks = 0; + for (let [diff, i] of diffs) { + if (i > index) continue; + + if (useLadders < ladders) { + useLadders++; + } else { + useBricks += diff; + if (useBricks > bricks) { + return false; + } + } + } + return true; + }; + + let l = 1, r = heights.length - 1; + while (l <= r) { + let mid = (l + r) >> 1; + if (canReach(mid)) { + l = mid + 1; + } else { + r = mid - 1; + } + } + + return l - 1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ + +--- + +## 4. Max-Heap + +::tabs-start + +```python +class Solution: + def furthestBuilding(self, heights: List[int], bricks: int, ladders: int) -> int: + heap = [] # Max heap of bricks used + + for i in range(len(heights) - 1): + diff = heights[i + 1] - heights[i] + if diff <= 0: + continue + + bricks -= diff + heapq.heappush(heap, -diff) + + if bricks < 0: + if ladders == 0: + return i + ladders -= 1 + bricks += -heapq.heappop(heap) + + return len(heights) - 1 +``` + +```java +public class Solution { + public int furthestBuilding(int[] heights, int bricks, int ladders) { + PriorityQueue maxHeap = new PriorityQueue<>((a, b) -> b - a); + + for (int i = 0; i < heights.length - 1; i++) { + int diff = heights[i + 1] - heights[i]; + if (diff <= 0) continue; + + bricks -= diff; + maxHeap.offer(diff); + + if (bricks < 0) { + if (ladders == 0) return i; + ladders--; + bricks += maxHeap.poll(); + } + } + + return heights.length - 1; + } +} +``` + +```cpp +class Solution { +public: + int furthestBuilding(vector& heights, int bricks, int ladders) { + priority_queue maxHeap; + + for (int i = 0; i < heights.size() - 1; i++) { + int diff = heights[i + 1] - heights[i]; + if (diff <= 0) continue; + + bricks -= diff; + maxHeap.push(diff); + + if (bricks < 0) { + if (ladders == 0) return i; + ladders--; + bricks += maxHeap.top(); + maxHeap.pop(); + } + } + + return heights.size() - 1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} heights + * @param {number} bricks + * @param {number} ladders + * @return {number} + */ + furthestBuilding(heights, bricks, ladders) { + const maxHeap = new MaxPriorityQueue(); + + for (let i = 0; i < heights.length - 1; i++) { + let diff = heights[i + 1] - heights[i]; + if (diff <= 0) continue; + + bricks -= diff; + maxHeap.enqueue(diff); + + if (bricks < 0) { + if (ladders === 0) return i; + ladders--; + bricks += maxHeap.dequeue().element; + } + } + + return heights.length - 1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ + +--- + +## 5. Min-Heap + +::tabs-start + +```python +class Solution: + def furthestBuilding(self, heights: List[int], bricks: int, ladders: int) -> int: + minHeap = [] + + for i in range(len(heights) - 1): + diff = heights[i + 1] - heights[i] + if diff <= 0: + continue + + heapq.heappush(minHeap, diff) + if len(minHeap) > ladders: + bricks -= heapq.heappop(minHeap) + if bricks < 0: + return i + + return len(heights) - 1 +``` + +```java +public class Solution { + public int furthestBuilding(int[] heights, int bricks, int ladders) { + PriorityQueue minHeap = new PriorityQueue<>(); + + for (int i = 0; i < heights.length - 1; i++) { + int diff = heights[i + 1] - heights[i]; + if (diff <= 0) continue; + + minHeap.offer(diff); + if (minHeap.size() > ladders) { + bricks -= minHeap.poll(); + if (bricks < 0) return i; + } + } + + return heights.length - 1; + } +} +``` + +```cpp +class Solution { +public: + int furthestBuilding(vector& heights, int bricks, int ladders) { + priority_queue, greater> minHeap; + + for (int i = 0; i < int(heights.size()) - 1; i++) { + int diff = heights[i + 1] - heights[i]; + if (diff <= 0) continue; + + minHeap.push(diff); + if (minHeap.size() > ladders) { + bricks -= minHeap.top(); minHeap.pop(); + if (bricks < 0) return i; + } + } + + return int(heights.size()) - 1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} heights + * @param {number} bricks + * @param {number} ladders + * @return {number} + */ + furthestBuilding(heights, bricks, ladders) { + const minHeap = new MinPriorityQueue(); + + for (let i = 0; i < heights.length - 1; i++) { + let diff = heights[i + 1] - heights[i]; + if (diff <= 0) continue; + + minHeap.enqueue(diff); + if (minHeap.size() > ladders) { + bricks -= minHeap.dequeue().element; + if (bricks < 0) return i; + } + } + + return heights.length - 1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/integer-to-roman.md b/articles/integer-to-roman.md new file mode 100644 index 000000000..6bb15bb30 --- /dev/null +++ b/articles/integer-to-roman.md @@ -0,0 +1,195 @@ +## 1. Math - I + +::tabs-start + +```python +class Solution: + def intToRoman(self, num: int) -> str: + symList = [ + ["I", 1], ["IV", 4], ["V", 5], ["IX", 9], + ["X", 10], ["XL", 40], ["L", 50], ["XC", 90], + ["C", 100], ["CD", 400], ["D", 500], ["CM", 900], + ["M", 1000] + ] + + res = "" + for sym, val in reversed(symList): + count = num // val + if count: + res += sym * count + num %= val + + return res +``` + +```java +public class Solution { + public String intToRoman(int num) { + String[][] symList = { + {"I", "1"}, {"IV", "4"}, {"V", "5"}, {"IX", "9"}, + {"X", "10"}, {"XL", "40"}, {"L", "50"}, {"XC", "90"}, + {"C", "100"}, {"CD", "400"}, {"D", "500"}, {"CM", "900"}, + {"M", "1000"} + }; + + StringBuilder res = new StringBuilder(); + for (int i = symList.length - 1; i >= 0; i--) { + String sym = symList[i][0]; + int val = Integer.parseInt(symList[i][1]); + int count = num / val; + if (count > 0) { + res.append(sym.repeat(count)); + num %= val; + } + } + + return res.toString(); + } +} +``` + +```cpp +class Solution { +public: + string intToRoman(int num) { + vector> symList = { + {"I", 1}, {"IV", 4}, {"V", 5}, {"IX", 9}, + {"X", 10}, {"XL", 40}, {"L", 50}, {"XC", 90}, + {"C", 100}, {"CD", 400}, {"D", 500}, {"CM", 900}, + {"M", 1000} + }; + + string res = ""; + for (int i = symList.size() - 1; i >= 0; i--) { + string sym = symList[i].first; + int val = symList[i].second; + int count = num / val; + if (count > 0) { + res.append(count, sym[0]); + if (sym.size() == 2) res.append(1, sym[1]); + num %= val; + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} num + * @return {string} + */ + intToRoman(num) { + const symList = [ + ["I", 1], ["IV", 4], ["V", 5], ["IX", 9], + ["X", 10], ["XL", 40], ["L", 50], ["XC", 90], + ["C", 100], ["CD", 400], ["D", 500], ["CM", 900], + ["M", 1000] + ]; + + let res = ""; + for (let i = symList.length - 1; i >= 0; i--) { + const [sym, val] = symList[i]; + let count = Math.floor(num / val); + if (count > 0) { + res += sym.repeat(count); + num %= val; + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(1)$ +* Space complexity: $O(1)$ + +--- + +## 2. Math - II + +::tabs-start + +```python +class Solution: + def intToRoman(self, num: int) -> str: + thousands = ["", "M", "MM", "MMM"] + hundreds = ["", "C", "CC", "CCC", "CD", "D", "DC", "DCC", "DCCC", "CM"] + tens = ["", "X", "XX", "XXX", "XL", "L", "LX", "LXX", "LXXX", "XC"] + ones = ["", "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX"] + + return ( + thousands[num // 1000] + + hundreds[(num % 1000) // 100] + + tens[(num % 100) // 10] + + ones[num % 10] + ) +``` + +```java +public class Solution { + public String intToRoman(int num) { + String[] thousands = {"", "M", "MM", "MMM"}; + String[] hundreds = {"", "C", "CC", "CCC", "CD", "D", "DC", "DCC", "DCCC", "CM"}; + String[] tens = {"", "X", "XX", "XXX", "XL", "L", "LX", "LXX", "LXXX", "XC"}; + String[] ones = {"", "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX"}; + + return thousands[num / 1000] + + hundreds[(num % 1000) / 100] + + tens[(num % 100) / 10] + + ones[num % 10]; + } +} +``` + +```cpp +class Solution { +public: + string intToRoman(int num) { + string thousands[] = {"", "M", "MM", "MMM"}; + string hundreds[] = {"", "C", "CC", "CCC", "CD", "D", "DC", "DCC", "DCCC", "CM"}; + string tens[] = {"", "X", "XX", "XXX", "XL", "L", "LX", "LXX", "LXXX", "XC"}; + string ones[] = {"", "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX"}; + + return thousands[num / 1000] + + hundreds[(num % 1000) / 100] + + tens[(num % 100) / 10] + + ones[num % 10]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} num + * @return {string} + */ + intToRoman(num) { + const thousands = ["", "M", "MM", "MMM"]; + const hundreds = ["", "C", "CC", "CCC", "CD", "D", "DC", "DCC", "DCCC", "CM"]; + const tens = ["", "X", "XX", "XXX", "XL", "L", "LX", "LXX", "LXXX", "XC"]; + const ones = ["", "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX"]; + + return thousands[Math.floor(num / 1000)] + + hundreds[Math.floor((num % 1000) / 100)] + + tens[Math.floor((num % 100) / 10)] + + ones[num % 10]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(1)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/is-graph-bipartite.md b/articles/is-graph-bipartite.md new file mode 100644 index 000000000..2db1c9d94 --- /dev/null +++ b/articles/is-graph-bipartite.md @@ -0,0 +1,611 @@ +## 1. Depth First Search + +::tabs-start + +```python +class Solution: + def isBipartite(self, graph: List[List[int]]) -> bool: + color = [0] * len(graph) # Map node i -> odd=1, even=-1 + + def dfs(i, c): + color[i] = c + for nei in graph[i]: + if color[nei] == c: + return False + if color[nei] == 0 and not dfs(nei, -c): + return False + return True + + for i in range(len(graph)): + if color[i] == 0 and not dfs(i, 1): + return False + return True +``` + +```java +public class Solution { + private int[] color; + + public boolean isBipartite(int[][] graph) { + int n = graph.length; + color = new int[n]; // Map node i -> odd=1, even=-1 + + for (int i = 0; i < n; i++) { + if (color[i] == 0 && !dfs(graph, i, 1)) { + return false; + } + } + return true; + } + + private boolean dfs(int[][] graph, int i, int c) { + color[i] = c; + for (int nei : graph[i]) { + if (color[nei] == c) { + return false; + } + if (color[nei] == 0 && !dfs(graph, nei, -c)) { + return false; + } + } + return true; + } +} +``` + +```cpp +class Solution { +private: + vector color; + + bool dfs(vector>& graph, int i, int c) { + color[i] = c; + for (int nei : graph[i]) { + if (color[nei] == c) { + return false; + } + if (color[nei] == 0 && !dfs(graph, nei, -c)) { + return false; + } + } + return true; + } + +public: + bool isBipartite(vector>& graph) { + int n = graph.size(); + color.assign(n, 0); // Map node i -> odd=1, even=-1 + + for (int i = 0; i < n; i++) { + if (color[i] == 0 && !dfs(graph, i, 1)) { + return false; + } + } + return true; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} graph + * @return {boolean} + */ + isBipartite(graph) { + let n = graph.length; + let color = new Array(n).fill(0); // Map node i -> odd=1, even=-1 + + const dfs = (i, c) => { + color[i] = c; + for (let nei of graph[i]) { + if (color[nei] === c) { + return false; + } + if (color[nei] === 0 && !dfs(nei, -c)) { + return false; + } + } + return true; + }; + + for (let i = 0; i < n; i++) { + if (color[i] === 0 && !dfs(i, 1)) { + return false; + } + } + return true; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(V + E)$ +* Space complexity: $O(V)$ + +> Where $V$ is the number of vertices and $E$ is the number of edges. + +--- + +## 2. Breadth First Search + +::tabs-start + +```python +class Solution: + def isBipartite(self, graph: List[List[int]]) -> bool: + color = [0] * len(graph) # Map node i -> odd=1, even=-1 + + def bfs(i): + if color[i]: + return True + q = deque([i]) + color[i] = -1 + while q: + i = q.popleft() + for nei in graph[i]: + if color[i] == color[nei]: + return False + elif not color[nei]: + q.append(nei) + color[nei] = -1 * color[i] + return True + + for i in range(len(graph)): + if not bfs(i): + return False + return True +``` + +```java +public class Solution { + public boolean isBipartite(int[][] graph) { + int n = graph.length; + int[] color = new int[n]; // Map node i -> odd=1, even=-1 + + for (int i = 0; i < n; i++) { + if (color[i] != 0) continue; + Queue q = new LinkedList<>(); + q.offer(i); + color[i] = -1; + + while (!q.isEmpty()) { + int node = q.poll(); + for (int nei : graph[node]) { + if (color[nei] == color[node]) { + return false; + } else if (color[nei] == 0) { + q.offer(nei); + color[nei] = -color[node]; + } + } + } + } + return true; + } +} +``` + +```cpp +class Solution { +public: + bool isBipartite(vector>& graph) { + int n = graph.size(); + vector color(n, 0); // Map node i -> odd=1, even=-1 + + for (int i = 0; i < n; i++) { + if (color[i] != 0) continue; + queue q; + q.push(i); + color[i] = -1; + + while (!q.empty()) { + int node = q.front(); + q.pop(); + for (int nei : graph[node]) { + if (color[nei] == color[node]) { + return false; + } else if (color[nei] == 0) { + q.push(nei); + color[nei] = -color[node]; + } + } + } + } + return true; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} graph + * @return {boolean} + */ + isBipartite(graph) { + let n = graph.length; + let color = new Array(n).fill(0); // Map node i -> odd=1, even=-1 + + for (let i = 0; i < n; i++) { + if (color[i] !== 0) continue; + let q = new Queue([i]); + color[i] = -1; + + while (!q.isEmpty()) { + let node = q.pop(); + for (let nei of graph[node]) { + if (color[nei] === color[node]) { + return false; + } else if (color[nei] === 0) { + q.push(nei); + color[nei] = -color[node]; + } + } + } + } + return true; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(V + E)$ +* Space complexity: $O(V)$ + +> Where $V$ is the number of vertices and $E$ is the number of edges. + +--- + +## 3. Iterative DFS + +::tabs-start + +```python +class Solution: + def isBipartite(self, graph: List[List[int]]) -> bool: + color = [0] * len(graph) # Map node i -> odd=1, even=-1 + stack = [] + + for i in range(len(graph)): + if color[i] != 0: + continue + color[i] = -1 + stack.append(i) + while stack: + node = stack.pop() + for nei in graph[node]: + if color[node] == color[nei]: + return False + elif not color[nei]: + stack.append(nei) + color[nei] = -1 * color[node] + + return True +``` + +```java +public class Solution { + public boolean isBipartite(int[][] graph) { + int n = graph.length; + int[] color = new int[n]; // Map node i -> odd=1, even=-1 + Stack stack = new Stack<>(); + + for (int i = 0; i < n; i++) { + if (color[i] != 0) continue; + color[i] = -1; + stack.push(i); + while (!stack.isEmpty()) { + int node = stack.pop(); + for (int nei : graph[node]) { + if (color[node] == color[nei]) return false; + if (color[nei] == 0) { + stack.push(nei); + color[nei] = -color[node]; + } + } + } + } + return true; + } +} +``` + +```cpp +class Solution { +public: + bool isBipartite(vector>& graph) { + int n = graph.size(); + vector color(n); // Map node i -> odd=1, even=-1 + stack stack; + + for (int i = 0; i < n; i++) { + if (color[i] != 0) continue; + color[i] = -1; + stack.push(i); + while (!stack.empty()) { + int node = stack.top(); + stack.pop(); + for (int nei : graph[node]) { + if (color[node] == color[nei]) return false; + if (color[nei] == 0) { + stack.push(nei); + color[nei] = -color[node]; + } + } + } + } + return true; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} graph + * @return {boolean} + */ + isBipartite(graph) { + let n = graph.length; + let color = new Array(n).fill(0); // Map node i -> odd=1, even=-1 + let stack = []; + + for (let i = 0; i < n; i++) { + if (color[i] !== 0) continue; + color[i] = -1; + stack.push(i); + while (stack.length > 0) { + let node = stack.pop(); + for (let nei of graph[node]) { + if (color[node] === color[nei]) return false; + if (color[nei] === 0) { + stack.push(nei); + color[nei] = -color[node]; + } + } + } + } + return true; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(V + E)$ +* Space complexity: $O(V)$ + +> Where $V$ is the number of vertices and $E$ is the number of edges. + +--- + +## 4. Disjoint Set Union + +::tabs-start + +```python +class DSU: + def __init__(self, n): + self.Parent = list(range(n)) + self.Size = [0] * n + + def find(self, node): + if self.Parent[node] != node: + self.Parent[node] = self.find(self.Parent[node]) + return self.Parent[node] + + def union(self, u, v): + pu, pv = self.find(u), self.find(v) + if pu == pv: + return False + if self.Size[pu] > self.Size[pv]: + self.Parent[pv] = pu + elif self.Size[pu] < self.Size[pv]: + self.Parent[pu] = pv + else: + self.Parent[pv] = pu + self.Size[pu] += 1 + return True + +class Solution: + def isBipartite(self, graph: List[List[int]]) -> bool: + n = len(graph) + dsu = DSU(n) + + for node in range(n): + for nei in graph[node]: + if dsu.find(node) == dsu.find(nei): + return False + dsu.union(graph[node][0], nei) + + return True +``` + +```java +public class DSU { + private int[] Parent, Size; + + public DSU(int n) { + Parent = new int[n]; + Size = new int[n]; + for (int i = 0; i < n; i++) { + Parent[i] = i; + Size[i] = 0; + } + } + + public int find(int node) { + if (Parent[node] != node) { + Parent[node] = find(Parent[node]); + } + return Parent[node]; + } + + public boolean union(int u, int v) { + int pu = find(u), pv = find(v); + if (pu == pv) return false; + if (Size[pu] > Size[pv]) { + Parent[pv] = pu; + } else if (Size[pu] < Size[pv]) { + Parent[pu] = pv; + } else { + Parent[pv] = pu; + Size[pu]++; + } + return true; + } +} + +class Solution { + public boolean isBipartite(int[][] graph) { + int n = graph.length; + DSU dsu = new DSU(n); + + for (int node = 0; node < n; node++) { + for (int nei : graph[node]) { + if (dsu.find(node) == dsu.find(nei)) { + return false; + } + dsu.union(graph[node][0], nei); + } + } + return true; + } +} +``` + +```cpp +class DSU { +private: + vector Parent, Size; + +public: + DSU(int n) { + Parent.resize(n); + Size.resize(n, 0); + for (int i = 0; i < n; i++) { + Parent[i] = i; + } + } + + int find(int node) { + if (Parent[node] != node) { + Parent[node] = find(Parent[node]); + } + return Parent[node]; + } + + bool unionSet(int u, int v) { + int pu = find(u), pv = find(v); + if (pu == pv) return false; + if (Size[pu] > Size[pv]) { + Parent[pv] = pu; + } else if (Size[pu] < Size[pv]) { + Parent[pu] = pv; + } else { + Parent[pv] = pu; + Size[pu]++; + } + return true; + } +}; + +class Solution { +public: + bool isBipartite(vector>& graph) { + int n = graph.size(); + DSU dsu(n); + + for (int node = 0; node < n; node++) { + for (int& nei : graph[node]) { + if (dsu.find(node) == dsu.find(nei)) { + return false; + } + dsu.unionSet(graph[node][0], nei); + } + } + return true; + } +}; +``` + +```javascript +class DSU { + /** + * @constructor + * @param {number} n + */ + constructor(n) { + this.parent = Array.from({ length: n + 1 }, (_, i) => i); + this.size = new Array(n + 1).fill(1); + } + + /** + * @param {number} node + * @return {number} + */ + find(node) { + if (this.parent[node] !== node) { + this.parent[node] = this.find(this.parent[node]); + } + return this.parent[node]; + } + + /** + * @param {number} u + * @param {number} u=v + * @return {boolean} + */ + union(u, v) { + let pu = this.find(u), pv = this.find(v); + if (pu === pv) return false; + if (this.size[pu] >= this.size[pv]) { + this.size[pu] += this.size[pv]; + this.parent[pv] = pu; + } else { + this.size[pv] += this.size[pu]; + this.parent[pu] = pv; + } + return true; + } +} + +class Solution { + /** + * @param {number[][]} graph + * @return {boolean} + */ + isBipartite(graph) { + let n = graph.length; + let dsu = new DSU(n); + + for (let node = 0; node < n; node++) { + for (let nei of graph[node]) { + if (dsu.find(node) === dsu.find(nei)) { + return false; + } + dsu.union(graph[node][0], nei); + } + } + return true; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(V + (E * α(V)))$ +* Space complexity: $O(V)$ + +> Where $V$ is the number of vertices and $E$ is the number of edges. $α()$ is used for amortized complexity. \ No newline at end of file diff --git a/articles/knight-dialer.md b/articles/knight-dialer.md new file mode 100644 index 000000000..9c87f4c13 --- /dev/null +++ b/articles/knight-dialer.md @@ -0,0 +1,946 @@ +## 1. Recursion + +::tabs-start + +```python +class Solution: + def knightDialer(self, n: int) -> int: + if n == 1: + return 10 + + mod = 1000000007 + jumps = [ + [4, 6], [6, 8], [7, 9], [4, 8], [0, 3, 9], + [], [0, 1, 7], [2, 6], [1, 3], [2, 4] + ] + + def dfs(n, d): + if n == 0: + return 1 + + res = 0 + for j in jumps[d]: + res = (res + dfs(n - 1, j)) % mod + return res + + res = 0 + for d in range(10): + res = (res + dfs(n - 1, d)) % mod + return res +``` + +```java +public class Solution { + private static final int MOD = 1000000007; + private static final int[][] jumps = { + {4, 6}, {6, 8}, {7, 9}, {4, 8}, {0, 3, 9}, + {}, {0, 1, 7}, {2, 6}, {1, 3}, {2, 4} + }; + + public int knightDialer(int n) { + if (n == 1) return 10; + int res = 0; + + for (int d = 0; d < 10; d++) { + res = (res + dfs(n - 1, d)) % MOD; + } + return res; + } + + private int dfs(int n, int d) { + if (n == 0) return 1; + + int res = 0; + for (int next : jumps[d]) { + res = (res + dfs(n - 1, next)) % MOD; + } + return res; + } +} +``` + +```cpp +class Solution { +private: + static constexpr int MOD = 1000000007; + const vector> jumps = { + {4, 6}, {6, 8}, {7, 9}, {4, 8}, {0, 3, 9}, + {}, {0, 1, 7}, {2, 6}, {1, 3}, {2, 4} + }; + +public: + int knightDialer(int n) { + if (n == 1) return 10; + int res = 0; + + for (int d = 0; d < 10; d++) { + res = (res + dfs(n - 1, d)) % MOD; + } + return res; + } + +private: + int dfs(int n, int d) { + if (n == 0) return 1; + + int res = 0; + for (int next : jumps[d]) { + res = (res + dfs(n - 1, next)) % MOD; + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {number} + */ + knightDialer(n) { + if (n === 1) return 10; + const MOD = 1000000007; + const jumps = [ + [4, 6], [6, 8], [7, 9], [4, 8], [0, 3, 9], + [], [0, 1, 7], [2, 6], [1, 3], [2, 4] + ]; + + const dfs = (n, d) => { + if (n === 0) return 1; + + let res = 0; + for (const next of jumps[d]) { + res = (res + dfs(n - 1, next)) % MOD; + } + return res; + }; + + let res = 0; + for (let d = 0; d < 10; d++) { + res = (res + dfs(n - 1, d)) % MOD; + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(3 ^ n)$ +* Space complexity: $O(n)$ for recursion stack. + +--- + +## 2. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def knightDialer(self, n: int) -> int: + if n == 1: + return 10 + + mod = 1000000007 + jumps = [ + [4, 6], [6, 8], [7, 9], [4, 8], [0, 3, 9], + [], [0, 1, 7], [2, 6], [1, 3], [2, 4] + ] + + dp = [[-1] * (n + 1) for _ in range(10)] + + def dfs(n, d): + if n == 0: + return 1 + if dp[d][n] != -1: + return dp[d][n] + + dp[d][n] = 0 + for j in jumps[d]: + dp[d][n] = (dp[d][n] + dfs(n - 1, j)) % mod + return dp[d][n] + + res = 0 + for d in range(10): + res = (res + dfs(n - 1, d)) % mod + return res +``` + +```java +public class Solution { + private static final int MOD = 1000000007; + private static final int[][] jumps = { + {4, 6}, {6, 8}, {7, 9}, {4, 8}, {0, 3, 9}, + {}, {0, 1, 7}, {2, 6}, {1, 3}, {2, 4} + }; + private int[][] dp; + + public int knightDialer(int n) { + if (n == 1) return 10; + dp = new int[10][n + 1]; + for (int[] row : dp) Arrays.fill(row, -1); + + int res = 0; + for (int d = 0; d < 10; d++) { + res = (res + dfs(n - 1, d)) % MOD; + } + return res; + } + + private int dfs(int n, int d) { + if (n == 0) return 1; + if (dp[d][n] != -1) return dp[d][n]; + + dp[d][n] = 0; + for (int next : jumps[d]) { + dp[d][n] = (dp[d][n] + dfs(n - 1, next)) % MOD; + } + return dp[d][n]; + } +} +``` + +```cpp +class Solution { +private: + static constexpr int MOD = 1000000007; + vector> jumps = { + {4, 6}, {6, 8}, {7, 9}, {4, 8}, {0, 3, 9}, + {}, {0, 1, 7}, {2, 6}, {1, 3}, {2, 4} + }; + vector> dp; + +public: + int knightDialer(int n) { + if (n == 1) return 10; + dp.assign(10, vector(n + 1, -1)); + + int res = 0; + for (int d = 0; d < 10; d++) { + res = (res + dfs(n - 1, d)) % MOD; + } + return res; + } + +private: + int dfs(int n, int d) { + if (n == 0) return 1; + if (dp[d][n] != -1) return dp[d][n]; + + dp[d][n] = 0; + for (int next : jumps[d]) { + dp[d][n] = (dp[d][n] + dfs(n - 1, next)) % MOD; + } + return dp[d][n]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {number} + */ + knightDialer(n) { + if (n === 1) return 10; + const MOD = 1000000007; + const jumps = [ + [4, 6], [6, 8], [7, 9], [4, 8], [0, 3, 9], + [], [0, 1, 7], [2, 6], [1, 3], [2, 4] + ]; + + const dp = Array.from({ length: 10 }, () => Array(n + 1).fill(-1)); + + const dfs = (n, d) => { + if (n === 0) return 1; + if (dp[d][n] !== -1) return dp[d][n]; + + dp[d][n] = 0; + for (const next of jumps[d]) { + dp[d][n] = (dp[d][n] + dfs(n - 1, next)) % MOD; + } + return dp[d][n]; + }; + + let res = 0; + for (let d = 0; d < 10; d++) { + res = (res + dfs(n - 1, d)) % MOD; + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def knightDialer(self, n: int) -> int: + if n == 1: + return 10 + + mod = 1000000007 + jumps = [ + [4, 6], [6, 8], [7, 9], [4, 8], [0, 3, 9], + [], [0, 1, 7], [2, 6], [1, 3], [2, 4] + ] + + dp = [[0] * (n + 1) for _ in range(10)] + + for d in range(10): + dp[d][0] = 1 + + for step in range(1, n): + for d in range(10): + dp[d][step] = sum(dp[j][step - 1] for j in jumps[d]) % mod + + return sum(dp[d][n - 1] for d in range(10)) % mod +``` + +```java +public class Solution { + private static final int MOD = 1000000007; + private static final int[][] jumps = { + {4, 6}, {6, 8}, {7, 9}, {4, 8}, {0, 3, 9}, + {}, {0, 1, 7}, {2, 6}, {1, 3}, {2, 4} + }; + + public int knightDialer(int n) { + if (n == 1) return 10; + + int[][] dp = new int[10][n + 1]; + for (int d = 0; d < 10; d++) { + dp[d][0] = 1; + } + + for (int step = 1; step < n; step++) { + for (int d = 0; d < 10; d++) { + for (int j : jumps[d]) { + dp[d][step] = (dp[d][step] + dp[j][step - 1]) % MOD; + } + } + } + + int res = 0; + for (int d = 0; d < 10; d++) { + res = (res + dp[d][n - 1]) % MOD; + } + return res; + } +} +``` + +```cpp +class Solution { +private: + static constexpr int MOD = 1000000007; + vector> jumps = { + {4, 6}, {6, 8}, {7, 9}, {4, 8}, {0, 3, 9}, + {}, {0, 1, 7}, {2, 6}, {1, 3}, {2, 4} + }; + +public: + int knightDialer(int n) { + if (n == 1) return 10; + + vector> dp(10, vector(n + 1, 0)); + for (int d = 0; d < 10; d++) { + dp[d][0] = 1; + } + + for (int step = 1; step < n; step++) { + for (int d = 0; d < 10; d++) { + for (int j : jumps[d]) { + dp[d][step] = (dp[d][step] + dp[j][step - 1]) % MOD; + } + } + } + + int res = 0; + for (int d = 0; d < 10; d++) { + res = (res + dp[d][n - 1]) % MOD; + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {number} + */ + knightDialer(n) { + if (n === 1) return 10; + const MOD = 1000000007; + const jumps = [ + [4, 6], [6, 8], [7, 9], [4, 8], [0, 3, 9], + [], [0, 1, 7], [2, 6], [1, 3], [2, 4] + ]; + + const dp = Array.from({ length: 10 }, () => Array(n + 1).fill(0)); + for (let d = 0; d < 10; d++) { + dp[d][0] = 1; + } + + for (let step = 1; step < n; step++) { + for (let d = 0; d < 10; d++) { + for (const j of jumps[d]) { + dp[d][step] = (dp[d][step] + dp[j][step - 1]) % MOD; + } + } + } + + let res = 0; + for (let d = 0; d < 10; d++) { + res = (res + dp[d][n - 1]) % MOD; + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 4. Dynamic Programming (Space Optimized) + +::tabs-start + +```python +class Solution: + def knightDialer(self, n: int) -> int: + if n == 1: + return 10 + + mod = 1000000007 + jumps = [ + [4, 6], [6, 8], [7, 9], [4, 8], [0, 3, 9], + [], [0, 1, 7], [2, 6], [1, 3], [2, 4] + ] + + dp = [1] * 10 + for step in range(n - 1): + next_dp = [0] * 10 + for d in range(10): + for j in jumps[d]: + next_dp[j] = (next_dp[j] + dp[d]) % mod + dp = next_dp + + res = 0 + for d in dp: + res = (res + d) % mod + return res +``` + +```java +public class Solution { + private static final int MOD = 1000000007; + private static final int[][] jumps = { + {4, 6}, {6, 8}, {7, 9}, {4, 8}, {0, 3, 9}, + {}, {0, 1, 7}, {2, 6}, {1, 3}, {2, 4} + }; + + public int knightDialer(int n) { + if (n == 1) return 10; + + int[] dp = new int[10]; + Arrays.fill(dp, 1); + + for (int step = 0; step < n - 1; step++) { + int[] nextDp = new int[10]; + for (int d = 0; d < 10; d++) { + for (int j : jumps[d]) { + nextDp[j] = (nextDp[j] + dp[d]) % MOD; + } + } + dp = nextDp; + } + + int res = 0; + for (int d : dp) { + res = (res + d) % MOD; + } + return res; + } +} +``` + +```cpp +class Solution { +private: + static constexpr int MOD = 1000000007; + vector> jumps = { + {4, 6}, {6, 8}, {7, 9}, {4, 8}, {0, 3, 9}, + {}, {0, 1, 7}, {2, 6}, {1, 3}, {2, 4} + }; + +public: + int knightDialer(int n) { + if (n == 1) return 10; + + vector dp(10, 1); + + for (int step = 0; step < n - 1; step++) { + vector nextDp(10, 0); + for (int d = 0; d < 10; d++) { + for (int j : jumps[d]) { + nextDp[j] = (nextDp[j] + dp[d]) % MOD; + } + } + dp = nextDp; + } + + int res = 0; + for (int d : dp) { + res = (res + d) % MOD; + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {number} + */ + knightDialer(n) { + if (n === 1) return 10; + const MOD = 1000000007; + const jumps = [ + [4, 6], [6, 8], [7, 9], [4, 8], [0, 3, 9], + [], [0, 1, 7], [2, 6], [1, 3], [2, 4] + ]; + + let dp = new Array(10).fill(1); + + for (let step = 0; step < n - 1; step++) { + let nextDp = new Array(10).fill(0); + for (let d = 0; d < 10; d++) { + for (const j of jumps[d]) { + nextDp[j] = (nextDp[j] + dp[d]) % MOD; + } + } + dp = nextDp; + } + + return dp.reduce((res, d) => (res + d) % MOD, 0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ + +--- + +## 5. Dynamic Programming (Optimal) + +::tabs-start + +```python +class Solution: + def knightDialer(self, n: int) -> int: + if n == 1: + return 10 + + mod = 10**9 + 7 + jumps = [1, 4, 2, 2] # [D, A, B, C] + + for _ in range(n - 1): + tmp = [0] * 4 + tmp[0] = jumps[3] + tmp[1] = 2 * jumps[2] + 2 * jumps[3] + tmp[2] = jumps[1] + tmp[3] = 2 * jumps[0] + jumps[1] + jumps = tmp + + return sum(jumps) % mod +``` + +```java +public class Solution { + public int knightDialer(int n) { + if (n == 1) return 10; + + int MOD = 1000000007; + long[] jumps = {1, 4, 2, 2}; // [D, A, B, C] + + for (int i = 0; i < n - 1; i++) { + long[] tmp = new long[4]; + tmp[0] = jumps[3]; + tmp[1] = (2 * jumps[2] + 2 * jumps[3]) % MOD; + tmp[2] = jumps[1]; + tmp[3] = (2 * jumps[0] + jumps[1]) % MOD; + jumps = tmp; + } + + long res = 0; + for (long num : jumps) { + res = (res + num) % MOD; + } + return (int) res; + } +} +``` + +```cpp +class Solution { +public: + int knightDialer(int n) { + if (n == 1) return 10; + + const int MOD = 1000000007; + vector jumps = {1, 4, 2, 2}; // [D, A, B, C] + + for (int i = 0; i < n - 1; i++) { + vector tmp(4); + tmp[0] = jumps[3]; + tmp[1] = (2 * jumps[2] + 2 * jumps[3]) % MOD; + tmp[2] = jumps[1]; + tmp[3] = (2 * jumps[0] + jumps[1]) % MOD; + jumps = tmp; + } + + return (jumps[0] + jumps[1] + jumps[2] + jumps[3]) % MOD; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {number} + */ + knightDialer(n) { + if (n === 1) return 10; + + const MOD = 1000000007; + let jumps = [1, 4, 2, 2]; // [D, A, B, C] + + for (let i = 0; i < n - 1; i++) { + let tmp = new Array(4).fill(0); + tmp[0] = jumps[3]; + tmp[1] = (2 * jumps[2] + 2 * jumps[3]) % MOD; + tmp[2] = jumps[1]; + tmp[3] = (2 * jumps[0] + jumps[1]) % MOD; + jumps = tmp; + } + + return jumps.reduce((sum, num) => (sum + num) % MOD, 0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ + +--- + +## 6. Matrix Exponentiation + +::tabs-start + +```python +class Matrix: + def __init__(self, size): + self.n = size + self.a = [[0] * size for _ in range(size)] + + def __mul__(self, other): + n = self.n + MOD = 10**9 + 7 + product = Matrix(n) + for i in range(n): + for j in range(n): + for k in range(n): + product.a[i][k] = (product.a[i][k] + self.a[i][j] * other.a[j][k]) % MOD + return product + + +def matpow(mat, n, size): + res = Matrix(size) + for i in range(size): + res.a[i][i] = 1 # Identity matrix + + while n: + if n & 1: + res = res * mat + mat = mat * mat + n >>= 1 + + return res + + +class Solution: + def knightDialer(self, n: int) -> int: + if n == 1: + return 10 + + MOD = 10**9 + 7 + jumps = [ + [4, 6], [6, 8], [7, 9], [4, 8], [0, 3, 9], + [], [0, 1, 7], [2, 6], [1, 3], [2, 4] + ] + + mat = Matrix(10) + for i in range(10): + for j in jumps[i]: + mat.a[i][j] = 1 + + res = matpow(mat, n - 1, 10) + + ans = sum(sum(res.a[i]) for i in range(10)) % MOD + return ans +``` + +```java +class Matrix { + int[][] a; + int size; + static final int MOD = 1_000_000_007; + + public Matrix(int size) { + this.size = size; + a = new int[size][size]; + } + + public Matrix multiply(Matrix other) { + Matrix product = new Matrix(size); + for (int i = 0; i < size; i++) { + for (int j = 0; j < size; j++) { + for (int k = 0; k < size; k++) { + product.a[i][k] = (int)((product.a[i][k] + (long) a[i][j] * other.a[j][k]) % MOD); + } + } + } + return product; + } +} + +public class Solution { + private Matrix matpow(Matrix mat, int n, int size) { + Matrix res = new Matrix(size); + for (int i = 0; i < size; i++) { + res.a[i][i] = 1; // Identity matrix + } + + while (n > 0) { + if ((n & 1) == 1) { + res = res.multiply(mat); + } + mat = mat.multiply(mat); + n >>= 1; + } + return res; + } + + public int knightDialer(int n) { + if (n == 1) return 10; + + int[][] jumps = { + {4, 6}, {6, 8}, {7, 9}, {4, 8}, {0, 3, 9}, + {}, {0, 1, 7}, {2, 6}, {1, 3}, {2, 4} + }; + + Matrix mat = new Matrix(10); + for (int i = 0; i < 10; i++) { + for (int j : jumps[i]) { + mat.a[i][j] = 1; + } + } + + Matrix res = matpow(mat, n - 1, 10); + + int ans = 0; + for (int i = 0; i < 10; i++) { + for (int j = 0; j < 10; j++) { + ans = (ans + res.a[i][j]) % Matrix.MOD; + } + } + return ans; + } +} +``` + +```cpp +class Matrix { +public: + vector> a; + int size; + static const int MOD = 1'000'000'007; + + Matrix(int n) : size(n) { + a.assign(n, vector(n, 0)); + } + + Matrix operator*(const Matrix &other) const { + Matrix product(size); + for (int i = 0; i < size; i++) { + for (int j = 0; j < size; j++) { + for (int k = 0; k < size; k++) { + product.a[i][k] = (product.a[i][k] + 1LL * a[i][j] * other.a[j][k]) % MOD; + } + } + } + return product; + } +}; + +Matrix matpow(Matrix mat, int n, int size) { + Matrix res(size); + for (int i = 0; i < size; i++) { + res.a[i][i] = 1; // Identity matrix + } + + while (n > 0) { + if (n & 1) res = res * mat; + mat = mat * mat; + n >>= 1; + } + return res; +} + +class Solution { +public: + int knightDialer(int n) { + if (n == 1) return 10; + + vector> jumps = { + {4, 6}, {6, 8}, {7, 9}, {4, 8}, {0, 3, 9}, + {}, {0, 1, 7}, {2, 6}, {1, 3}, {2, 4} + }; + + Matrix mat(10); + for (int i = 0; i < 10; i++) { + for (int j : jumps[i]) { + mat.a[i][j] = 1; + } + } + + Matrix res = matpow(mat, n - 1, 10); + + int ans = 0; + for (int i = 0; i < 10; i++) { + for (int j = 0; j < 10; j++) { + ans = (ans + res.a[i][j]) % Matrix::MOD; + } + } + return ans; + } +}; +``` + +```javascript +class Matrix { + constructor(size) { + this.size = size; + this.a = Array.from({ length: size }, () => Array(size).fill(0)); + this.MOD = BigInt(1e9 + 7); + } + + multiply(other) { + const product = new Matrix(this.size); + for (let i = 0; i < this.size; i++) { + for (let j = 0; j < this.size; j++) { + let sum = BigInt(0); + for (let k = 0; k < this.size; k++) { + sum = (sum + BigInt(this.a[i][k]) * BigInt(other.a[k][j])) % this.MOD; + } + product.a[i][j] = Number(sum); + } + } + return product; + } +} + +class Solution { + /** + * @param {number} n + * @return {number} + */ + knightDialer(n) { + if (n === 1) return 10; + + const matpow = (mat, exp, size) => { + let res = new Matrix(size); + for (let i = 0; i < size; i++) { + res.a[i][i] = 1; // Identity matrix + } + + while (exp > 0) { + if (exp & 1) { + res = res.multiply(mat); + } + mat = mat.multiply(mat); + exp >>= 1; + } + return res; + }; + + const jumps = [ + [4, 6], [6, 8], [7, 9], [4, 8], [0, 3, 9], + [], [0, 1, 7], [2, 6], [1, 3], [2, 4] + ]; + + const mat = new Matrix(10); + for (let i = 0; i < 10; i++) { + for (let j of jumps[i]) { + mat.a[i][j] = 1; + } + } + + const res = matpow(mat, n - 1, 10); + const mod = 1e9 + 7 + let ans = 0; + + for (let i = 0; i < 10; i++) { + for (let j = 0; j < 10; j++) { + ans = (ans + res.a[i][j]) % mod; + } + } + + return ans; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(\log n)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/largest-color-value-in-a-directed-graph.md b/articles/largest-color-value-in-a-directed-graph.md new file mode 100644 index 000000000..dcbf467a4 --- /dev/null +++ b/articles/largest-color-value-in-a-directed-graph.md @@ -0,0 +1,589 @@ +## 1. Brute Force (DFS) + +::tabs-start + +```python +class Solution: + def largestPathValue(self, colors: str, edges: list[list[int]]) -> int: + n = len(colors) + adj = [[] for _ in range(n)] + for u, v in edges: + adj[u].append(v) + + visit = [False] * n + def dfs(node, c): + if visit[node]: + return float("inf") + + visit[node] = True + clrCnt = 0 + for nei in adj[node]: + cur = dfs(nei, c) + if cur == float("inf"): + return cur + clrCnt = max(clrCnt, cur) + visit[node] = False + return clrCnt + (c == (ord(colors[node]) - ord('a'))) + + res = -1 + for i in range(n): + for c in range(26): + cnt = dfs(i, c) + if cnt == float("inf"): + return -1 + res = max(res, cnt) + return res +``` + +```java +public class Solution { + private int n; + private List[] adj; + private boolean[] visit; + + public int largestPathValue(String colors, int[][] edges) { + this.n = colors.length(); + this.adj = new ArrayList[n]; + this.visit = new boolean[n]; + + for (int i = 0; i < n; i++) { + adj[i] = new ArrayList<>(); + } + for (int[] edge : edges) { + adj[edge[0]].add(edge[1]); + } + + int res = -1; + for (int i = 0; i < n; i++) { + for (int c = 0; c < 26; c++) { + int cnt = dfs(i, c, colors); + if (cnt == Integer.MAX_VALUE) return -1; + res = Math.max(res, cnt); + } + } + return res; + } + + private int dfs(int node, int c, String colors) { + if (visit[node]) return Integer.MAX_VALUE; + + visit[node] = true; + int clrCnt = 0; + for (int nei : adj[node]) { + int cur = dfs(nei, c, colors); + if (cur == Integer.MAX_VALUE) return cur; + clrCnt = Math.max(clrCnt, cur); + } + visit[node] = false; + return clrCnt + ((colors.charAt(node) - 'a') == c ? 1 : 0); + } +} +``` + +```cpp +class Solution { +public: + int n; + vector> adj; + vector visit; + + int largestPathValue(string colors, vector>& edges) { + n = colors.size(); + adj.assign(n, vector()); + visit.assign(n, false); + + for (auto& edge : edges) { + adj[edge[0]].push_back(edge[1]); + } + + int res = -1; + for (int i = 0; i < n; i++) { + for (int c = 0; c < 26; c++) { + int cnt = dfs(i, c, colors); + if (cnt == 1e9) return -1; + res = max(res, cnt); + } + } + return res; + } + +private: + int dfs(int node, int c, string& colors) { + if (visit[node]) return 1e9; + + visit[node] = true; + int clrCnt = 0; + for (int nei : adj[node]) { + int cur = dfs(nei, c, colors); + if (cur == 1e9) return cur; + clrCnt = max(clrCnt, cur); + } + visit[node] = false; + return clrCnt + ((colors[node] - 'a') == c ? 1 : 0); + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} colors + * @param {number[][]} edges + * @return {number} + */ + largestPathValue(colors, edges) { + const n = colors.length; + const adj = Array.from({ length: n }, () => []); + for (const [u, v] of edges) { + adj[u].push(v); + } + + const visit = new Array(n).fill(false); + const dfs = (node, c) => { + if (visit[node]) return Infinity; + + visit[node] = true; + let clrCnt = 0; + for (const nei of adj[node]) { + const cur = dfs(nei, c); + if (cur === Infinity) return cur; + clrCnt = Math.max(clrCnt, cur); + } + visit[node] = false; + return clrCnt + (c === colors.charCodeAt(node) - 97 ? 1 : 0); + }; + + let res = -1; + for (let i = 0; i < n; i++) { + for (let c = 0; c < 26; c++) { + const cnt = dfs(i, c); + if (cnt === Infinity) return -1; + res = Math.max(res, cnt); + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(V * (V + E))$ +* Space complexity: $O(V + E)$ + +> Where $V$ is the number of verticies and $E$ is the number of edges. + +--- + +## 2. Depth First Search + +::tabs-start + +```python +class Solution: + def largestPathValue(self, colors: str, edges: list[list[int]]) -> int: + adj = defaultdict(list) + for src, dst in edges: + adj[src].append(dst) + + def dfs(node): + if node in path: + return float("inf") + if node in visit: + return 0 + + visit.add(node) + path.add(node) + colorIndex = ord(colors[node]) - ord('a') + count[node][colorIndex] = 1 + + for nei in adj[node]: + if dfs(nei) == float("inf"): + return float("inf") + for c in range(26): + count[node][c] = max( + count[node][c], + (1 if c == colorIndex else 0) + count[nei][c] + ) + + path.remove(node) + return 0 + + n, res = len(colors), 0 + visit, path = set(), set() + count = [[0] * 26 for _ in range(n)] + + for i in range(n): + if dfs(i) == float("inf"): + return -1 + res = max(res, max(count[i])) + + return res +``` + +```java +public class Solution { + private int n; + private List[] adj; + private boolean[] visit, path; + private int[][] count; + + public int largestPathValue(String colors, int[][] edges) { + this.n = colors.length(); + this.adj = new ArrayList[n]; + this.visit = new boolean[n]; + this.path = new boolean[n]; + this.count = new int[n][26]; + + for (int i = 0; i < n; i++) { + adj[i] = new ArrayList<>(); + } + for (int[] edge : edges) { + adj[edge[0]].add(edge[1]); + } + + int res = 0; + for (int i = 0; i < n; i++) { + if (dfs(i, colors) == Integer.MAX_VALUE) return -1; + for (int c = 0; c < 26; c++) { + res = Math.max(res, count[i][c]); + } + } + return res; + } + + private int dfs(int node, String colors) { + if (path[node]) return Integer.MAX_VALUE; + if (visit[node]) return 0; + + visit[node] = true; + path[node] = true; + int colorIndex = colors.charAt(node) - 'a'; + count[node][colorIndex] = 1; + + for (int nei : adj[node]) { + if (dfs(nei, colors) == Integer.MAX_VALUE) { + return Integer.MAX_VALUE; + } + for (int c = 0; c < 26; c++) { + count[node][c] = Math.max( + count[node][c], + (c == colorIndex ? 1 : 0) + count[nei][c] + ); + } + } + + path[node] = false; + return 0; + } +} +``` + +```cpp +class Solution { +public: + int n, INF = 1e9; + vector> adj; + vector visit, path; + vector> count; + + int largestPathValue(string colors, vector>& edges) { + this->n = colors.size(); + adj.resize(n); + visit.assign(n, false); + path.assign(n, false); + count.assign(n, vector(26)); + + for (auto& edge : edges) { + adj[edge[0]].push_back(edge[1]); + } + + int res = 0; + for (int i = 0; i < n; i++) { + if (dfs(i, colors) == INF) return -1; + for (int c = 0; c < 26; c++) { + res = max(res, count[i][c]); + } + } + return res; + } + +private: + int dfs(int node, string& colors) { + if (path[node]) return INF; + if (visit[node]) return 0; + + visit[node] = true; + path[node] = true; + int colorIndex = colors[node] - 'a'; + count[node][colorIndex] = 1; + + for (int& nei : adj[node]) { + if (dfs(nei, colors) == INF) return INF; + for (int c = 0; c < 26; c++) { + count[node][c] = max( + count[node][c], + (c == colorIndex ? 1 : 0) + count[nei][c] + ); + } + } + + path[node] = false; + return 0; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} colors + * @param {number[][]} edges + * @return {number} + */ + largestPathValue(colors, edges) { + const n = colors.length; + const adj = Array.from({ length: n }, () => []); + for (const [src, dst] of edges) { + adj[src].push(dst); + } + + const visit = new Array(n).fill(false); + const path = new Array(n).fill(false); + const count = Array.from({ length: n }, () => new Array(26).fill(0)); + + const dfs = (node) => { + if (path[node]) return Infinity; + if (visit[node]) return 0; + + visit[node] = true; + path[node] = true; + const colorIndex = colors.charCodeAt(node) - 'a'.charCodeAt(0); + count[node][colorIndex] = 1; + + for (const nei of adj[node]) { + if (dfs(nei) === Infinity) return Infinity; + for (let c = 0; c < 26; c++) { + count[node][c] = Math.max( + count[node][c], + (c === colorIndex ? 1 : 0) + count[nei][c] + ); + } + } + + path[node] = false; + return 0; + }; + + let res = 0; + for (let i = 0; i < n; i++) { + if (dfs(i) === Infinity) return -1; + for (let c = 0; c < 26; c++) { + res = Math.max(res, count[i][c]); + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(V + E)$ +* Space complexity: $O(V + E)$ + +> Where $V$ is the number of verticies and $E$ is the number of edges. + +--- + +## 3. Topological Sort (Kahn's Algorithm) + +::tabs-start + +```python +class Solution: + def largestPathValue(self, colors: str, edges: list[list[int]]) -> int: + n = len(colors) + adj = [[] for _ in range(n)] + indegree = [0] * n + count = [[0] * 26 for _ in range(n)] + + for u, v in edges: + adj[u].append(v) + indegree[v] += 1 + + q = deque() + for i in range(n): + if indegree[i] == 0: + q.append(i) + + visit = res = 0 + while q: + node = q.popleft() + visit += 1 + colorIndex = ord(colors[node]) - ord('a') + count[node][colorIndex] += 1 + res = max(res, count[node][colorIndex]) + + for nei in adj[node]: + for c in range(26): + count[nei][c] = max(count[nei][c], count[node][c]) + + indegree[nei] -= 1 + if indegree[nei] == 0: + q.append(nei) + + return res if visit == n else -1 +``` + +```java +public class Solution { + public int largestPathValue(String colors, int[][] edges) { + int n = colors.length(); + List[] adj = new ArrayList[n]; + int[] indegree = new int[n]; + int[][] count = new int[n][26]; + + for (int i = 0; i < n; i++) { + adj[i] = new ArrayList<>(); + } + + for (int[] edge : edges) { + adj[edge[0]].add(edge[1]); + indegree[edge[1]]++; + } + + Queue q = new LinkedList<>(); + for (int i = 0; i < n; i++) { + if (indegree[i] == 0) { + q.add(i); + } + } + + int visit = 0, res = 0; + while (!q.isEmpty()) { + int node = q.poll(); + visit++; + int colorIndex = colors.charAt(node) - 'a'; + count[node][colorIndex]++; + res = Math.max(res, count[node][colorIndex]); + + for (int nei : adj[node]) { + for (int c = 0; c < 26; c++) { + count[nei][c] = Math.max(count[nei][c], count[node][c]); + } + if (--indegree[nei] == 0) { + q.add(nei); + } + } + } + + return visit == n ? res : -1; + } +} +``` + +```cpp +class Solution { +public: + int largestPathValue(string colors, vector>& edges) { + int n = colors.size(); + vector> adj(n); + vector indegree(n); + vector> count(n, vector(26)); + + for (auto& edge : edges) { + adj[edge[0]].push_back(edge[1]); + indegree[edge[1]]++; + } + + queue q; + for (int i = 0; i < n; i++) { + if (indegree[i] == 0) { + q.push(i); + } + } + + int visit = 0, res = 0; + while (!q.empty()) { + int node = q.front();q.pop(); + visit++; + int colorIndex = colors[node] - 'a'; + count[node][colorIndex]++; + res = max(res, count[node][colorIndex]); + + for (int& nei : adj[node]) { + for (int c = 0; c < 26; c++) { + count[nei][c] = max(count[nei][c], count[node][c]); + } + if (--indegree[nei] == 0) { + q.push(nei); + } + } + } + + return visit == n ? res : -1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} colors + * @param {number[][]} edges + * @return {number} + */ + largestPathValue(colors, edges) { + const n = colors.length; + const adj = Array.from({ length: n }, () => []); + const indegree = new Array(n).fill(0); + const count = Array.from({ length: n }, () => new Array(26).fill(0)); + + for (const [u, v] of edges) { + adj[u].push(v); + indegree[v]++; + } + + const q = new Queue(); + for (let i = 0; i < n; i++) { + if (indegree[i] === 0) { + q.push(i); + } + } + + let visit = 0, res = 0; + while (!q.isEmpty()) { + const node = q.pop(); + visit++; + const colorIndex = colors.charCodeAt(node) - 'a'.charCodeAt(0); + count[node][colorIndex]++; + res = Math.max(res, count[node][colorIndex]); + + for (const nei of adj[node]) { + for (let c = 0; c < 26; c++) { + count[nei][c] = Math.max(count[nei][c], count[node][c]); + } + if (--indegree[nei] === 0) { + q.push(nei); + } + } + } + + return visit === n ? res : -1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(V + E)$ +* Space complexity: $O(V + E)$ + +> Where $V$ is the number of verticies and $E$ is the number of edges. \ No newline at end of file diff --git a/articles/largest-divisible-subset.md b/articles/largest-divisible-subset.md new file mode 100644 index 000000000..c5d8fa381 --- /dev/null +++ b/articles/largest-divisible-subset.md @@ -0,0 +1,738 @@ +## 1. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def largestDivisibleSubset(self, nums: List[int]) -> List[int]: + nums.sort() + cache = {} # (i, prevIndex) -> List + + def dfs(i, prevIndex): + if i == len(nums): + return [] + if (i, prevIndex) in cache: + return cache[(i, prevIndex)] + + res = dfs(i + 1, prevIndex) # Skip nums[i] + if prevIndex == -1 or nums[i] % nums[prevIndex] == 0: + tmp = [nums[i]] + dfs(i + 1, i) # Include nums[i] + res = tmp if len(tmp) > len(res) else res + + cache[(i, prevIndex)] = res + return res + + return dfs(0, -1) +``` + +```java +public class Solution { + private List[][] cache; + + public List largestDivisibleSubset(int[] nums) { + Arrays.sort(nums); + int n = nums.length; + cache = new ArrayList[n][n + 1]; + return dfs(0, -1, nums); + } + + private List dfs(int i, int prevIndex, int[] nums) { + if (i == nums.length) return new ArrayList<>(); + if (cache[i][prevIndex + 1] != null) return cache[i][prevIndex + 1]; + + List res = dfs(i + 1, prevIndex, nums); + + if (prevIndex == -1 || nums[i] % nums[prevIndex] == 0) { + List tmp = new ArrayList<>(); + tmp.add(nums[i]); + tmp.addAll(dfs(i + 1, i, nums)); + if (tmp.size() > res.size()) res = tmp; + } + + cache[i][prevIndex + 1] = res; + return res; + } +} +``` + +```cpp +class Solution { +private: + vector>> cache; + +public: + vector largestDivisibleSubset(vector& nums) { + sort(nums.begin(), nums.end()); + int n = nums.size(); + cache = vector>>(n, vector>(n + 1)); + return dfs(0, -1, nums); + } + + vector dfs(int i, int prevIndex, vector& nums) { + if (i == nums.size()) return {}; + if (!cache[i][prevIndex + 1].empty()) return cache[i][prevIndex + 1]; + + vector res = dfs(i + 1, prevIndex, nums); + + if (prevIndex == -1 || nums[i] % nums[prevIndex] == 0) { + vector tmp = {nums[i]}; + vector next = dfs(i + 1, i, nums); + tmp.insert(tmp.end(), next.begin(), next.end()); + if (tmp.size() > res.size()) res = tmp; + } + + return cache[i][prevIndex + 1] = res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number[]} + */ + largestDivisibleSubset(nums) { + nums.sort((a, b) => a - b); + const cache = new Map(); + + const dfs = (i, prevIndex) => { + if (i === nums.length) return []; + + let key = `${i},${prevIndex}`; + if (cache.has(key)) return cache.get(key); + + let res = dfs(i + 1, prevIndex); + if (prevIndex === -1 || nums[i] % nums[prevIndex] === 0) { + let tmp = [nums[i], ...dfs(i + 1, i)]; + if (tmp.length > res.length) res = tmp; + } + + cache.set(key, res); + return res; + }; + + return dfs(0, -1); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n ^ 2)$ + +--- + +## 2. Dynamic Programming (Top-Down) Space Optimized + +::tabs-start + +```python +class Solution: + def largestDivisibleSubset(self, nums: List[int]) -> List[int]: + nums.sort() + cache = {} + + def dfs(i): + if i in cache: + return cache[i] + + res = [nums[i]] + for j in range(i + 1, len(nums)): + if nums[j] % nums[i] == 0: + tmp = [nums[i]] + dfs(j) + if len(tmp) > len(res): + res = tmp + + cache[i] = res + return res + + res = [] + for i in range(len(nums)): + tmp = dfs(i) + if len(tmp) > len(res): + res = tmp + return res +``` + +```java +public class Solution { + private List[] cache; + + public List largestDivisibleSubset(int[] nums) { + Arrays.sort(nums); + int n = nums.length; + cache = new ArrayList[n]; + + List res = new ArrayList<>(); + for (int i = 0; i < n; i++) { + List tmp = dfs(i, nums); + if (tmp.size() > res.size()) { + res = tmp; + } + } + return res; + } + + private List dfs(int i, int[] nums) { + if (cache[i] != null) return cache[i]; + + List res = new ArrayList<>(); + res.add(nums[i]); + for (int j = i + 1; j < nums.length; j++) { + if (nums[j] % nums[i] == 0) { + List tmp = new ArrayList<>(); + tmp.add(nums[i]); + tmp.addAll(dfs(j, nums)); + + if (tmp.size() > res.size()) { + res = tmp; + } + } + } + return cache[i] = res; + } +} +``` + +```cpp +class Solution { +private: + vector> cache; + +public: + vector largestDivisibleSubset(vector& nums) { + sort(nums.begin(), nums.end()); + int n = nums.size(); + cache.resize(n, vector()); + + vector res; + for (int i = 0; i < n; i++) { + vector tmp = dfs(i, nums); + if (tmp.size() > res.size()) { + res = tmp; + } + } + return res; + } + + vector dfs(int i, vector& nums) { + if (!cache[i].empty()) return cache[i]; + + vector res = {nums[i]}; + for (int j = i + 1; j < nums.size(); j++) { + if (nums[j] % nums[i] == 0) { + vector tmp = {nums[i]}; + vector next = dfs(j, nums); + tmp.insert(tmp.end(), next.begin(), next.end()); + + if (tmp.size() > res.size()) { + res = tmp; + } + } + } + return cache[i] = res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number[]} + */ + largestDivisibleSubset(nums) { + nums.sort((a, b) => a - b); + const n = nums.length; + const cache = new Array(n).fill(null); + + const dfs = (i) => { + if (cache[i] !== null) return cache[i]; + + let res = [nums[i]]; + for (let j = i + 1; j < n; j++) { + if (nums[j] % nums[i] === 0) { + let tmp = [nums[i], ...dfs(j)]; + if (tmp.length > res.length) { + res = tmp; + } + } + } + return (cache[i] = res); + }; + + let res = []; + for (let i = 0; i < n; i++) { + let tmp = dfs(i); + if (tmp.length > res.length) { + res = tmp; + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n)$ + +--- + +## 3. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def largestDivisibleSubset(self, nums: List[int]) -> List[int]: + nums.sort() + dp = [[num] for num in nums] # dp[i] = longest start at i + res = [] + for i in range(len(nums) - 1, -1, -1): + for j in range(i + 1, len(nums)): + if nums[j] % nums[i] == 0: + tmp = [nums[i]] + dp[j] + dp[i] = tmp if len(tmp) > len(dp[i]) else dp[i] + res = dp[i] if len(dp[i]) > len(res) else res + return res +``` + +```java +public class Solution { + public List largestDivisibleSubset(int[] nums) { + Arrays.sort(nums); + int n = nums.length; + List[] dp = new ArrayList[n]; + List res = new ArrayList<>(); + + for (int i = n - 1; i >= 0; i--) { + dp[i] = new ArrayList<>(); + dp[i].add(nums[i]); + + for (int j = i + 1; j < n; j++) { + if (nums[j] % nums[i] == 0) { + List tmp = new ArrayList<>(); + tmp.add(nums[i]); + tmp.addAll(dp[j]); + + if (tmp.size() > dp[i].size()) { + dp[i] = tmp; + } + } + } + if (dp[i].size() > res.size()) { + res = dp[i]; + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + vector largestDivisibleSubset(vector& nums) { + sort(nums.begin(), nums.end()); + int n = nums.size(); + vector> dp(n); + vector res; + + for (int i = n - 1; i >= 0; i--) { + dp[i].push_back(nums[i]); + + for (int j = i + 1; j < n; j++) { + if (nums[j] % nums[i] == 0) { + vector tmp = dp[j]; + tmp.insert(tmp.begin(), nums[i]); + + if (tmp.size() > dp[i].size()) { + dp[i] = tmp; + } + } + } + if (dp[i].size() > res.size()) { + res = dp[i]; + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number[]} + */ + largestDivisibleSubset(nums) { + nums.sort((a, b) => a - b); + const n = nums.length; + const dp = new Array(n).fill(0).map(() => []); + let res = []; + + for (let i = n - 1; i >= 0; i--) { + dp[i] = [nums[i]]; + + for (let j = i + 1; j < n; j++) { + if (nums[j] % nums[i] === 0) { + let tmp = [nums[i], ...dp[j]]; + + if (tmp.length > dp[i].length) { + dp[i] = tmp; + } + } + } + if (dp[i].length > res.length) { + res = dp[i]; + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n)$ + +--- + +## 4. Dynamic Programming (Top-Down) + Tracing + +::tabs-start + +```python +class Solution: + def largestDivisibleSubset(self, nums: List[int]) -> List[int]: + nums.sort() + n = len(nums) + dp = [[-1, -1] for _ in range(n)] # dp[i] = [maxLen, prevIdx] + + def dfs(i): + if dp[i][0] != -1: + return dp[i][0] + + dp[i][0] = 1 + for j in range(i + 1, n): + if nums[j] % nums[i] == 0: + length = dfs(j) + 1 + if length > dp[i][0]: + dp[i][0] = length + dp[i][1] = j + + return dp[i][0] + + max_len, start_index = 1, 0 + for i in range(n): + if dfs(i) > max_len: + max_len = dfs(i) + start_index = i + + subset = [] + while start_index != -1: + subset.append(nums[start_index]) + start_index = dp[start_index][1] + + return subset +``` + +```java +public class Solution { + private int[][] dp; + + public List largestDivisibleSubset(int[] nums) { + Arrays.sort(nums); + int n = nums.length; + dp = new int[n][2]; + for (int i = 0; i < n; i++) { + dp[i][0] = -1; + dp[i][1] = -1; + } + + int maxLen = 1, startIndex = 0; + for (int i = 0; i < n; i++) { + if (dfs(i, nums) > maxLen) { + maxLen = dp[i][0]; + startIndex = i; + } + } + + List subset = new ArrayList<>(); + while (startIndex != -1) { + subset.add(nums[startIndex]); + startIndex = dp[startIndex][1]; + } + return subset; + } + + private int dfs(int i, int[] nums) { + if (dp[i][0] != -1) return dp[i][0]; + + dp[i][0] = 1; + for (int j = i + 1; j < nums.length; j++) { + if (nums[j] % nums[i] == 0) { + int length = dfs(j, nums) + 1; + if (length > dp[i][0]) { + dp[i][0] = length; + dp[i][1] = j; + } + } + } + return dp[i][0]; + } +} +``` + +```cpp +class Solution { + vector> dp; + +public: + vector largestDivisibleSubset(vector& nums) { + sort(nums.begin(), nums.end()); + int n = nums.size(); + dp.assign(n, vector(2, -1)); + + int maxLen = 1, startIndex = 0; + for (int i = 0; i < n; i++) { + if (dfs(i, nums) > maxLen) { + maxLen = dp[i][0]; + startIndex = i; + } + } + + vector subset; + while (startIndex != -1) { + subset.push_back(nums[startIndex]); + startIndex = dp[startIndex][1]; + } + return subset; + } + +private: + int dfs(int i, vector& nums) { + if (dp[i][0] != -1) return dp[i][0]; + + dp[i][0] = 1; + for (int j = i + 1; j < nums.size(); j++) { + if (nums[j] % nums[i] == 0) { + int length = dfs(j, nums) + 1; + if (length > dp[i][0]) { + dp[i][0] = length; + dp[i][1] = j; + } + } + } + return dp[i][0]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number[]} + */ + largestDivisibleSubset(nums) { + nums.sort((a, b) => a - b); + let n = nums.length; + let dp = Array.from({ length: n }, () => [-1, -1]); // dp[i] = [maxLen, prevIdx] + + const dfs = (i) => { + if (dp[i][0] !== -1) return dp[i][0]; + + dp[i][0] = 1; + for (let j = i + 1; j < n; j++) { + if (nums[j] % nums[i] === 0) { + let length = dfs(j) + 1; + if (length > dp[i][0]) { + dp[i][0] = length; + dp[i][1] = j; + } + } + } + return dp[i][0]; + }; + + let maxLen = 1, startIndex = 0; + for (let i = 0; i < n; i++) { + if (dfs(i) > maxLen) { + maxLen = dfs(i); + startIndex = i; + } + } + + let subset = []; + while (startIndex !== -1) { + subset.push(nums[startIndex]); + startIndex = dp[startIndex][1]; + } + return subset; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n)$ + +--- + +## 5. Dynamic Programming (Bottom-Up) + Tracing + +::tabs-start + +```python +class Solution: + def largestDivisibleSubset(self, nums: List[int]) -> List[int]: + nums.sort() + n = len(nums) + dp = [[1, -1] for _ in range(n)] # dp[i] = [maxLen, prevIdx] + + max_len, start_index = 1, 0 + + for i in range(n): + for j in range(i): + if nums[i] % nums[j] == 0 and dp[j][0] + 1 > dp[i][0]: + dp[i][0] = dp[j][0] + 1 + dp[i][1] = j + + if dp[i][0] > max_len: + max_len = dp[i][0] + start_index = i + + subset = [] + while start_index != -1: + subset.append(nums[start_index]) + start_index = dp[start_index][1] + return subset +``` + +```java +public class Solution { + public List largestDivisibleSubset(int[] nums) { + Arrays.sort(nums); + int n = nums.length; + int[][] dp = new int[n][2]; // dp[i] = {maxLen, prevIdx} + + int maxLen = 1, startIndex = 0; + for (int i = 0; i < n; i++) { + dp[i][0] = 1; + dp[i][1] = -1; + for (int j = 0; j < i; j++) { + if (nums[i] % nums[j] == 0 && dp[j][0] + 1 > dp[i][0]) { + dp[i][0] = dp[j][0] + 1; + dp[i][1] = j; + } + } + + if (dp[i][0] > maxLen) { + maxLen = dp[i][0]; + startIndex = i; + } + } + + List subset = new ArrayList<>(); + while (startIndex != -1) { + subset.add(nums[startIndex]); + startIndex = dp[startIndex][1]; + } + return subset; + } +} +``` + +```cpp +class Solution { +public: + vector largestDivisibleSubset(vector& nums) { + sort(nums.begin(), nums.end()); + int n = nums.size(); + vector> dp(n, vector(2, -1)); // dp[i] = {maxLen, prevIdx} + + int maxLen = 1, startIndex = 0; + for (int i = 0; i < n; i++) { + dp[i][0] = 1; + dp[i][1] = -1; + for (int j = 0; j < i; j++) { + if (nums[i] % nums[j] == 0 && dp[j][0] + 1 > dp[i][0]) { + dp[i][0] = dp[j][0] + 1; + dp[i][1] = j; + } + } + + if (dp[i][0] > maxLen) { + maxLen = dp[i][0]; + startIndex = i; + } + } + + vector subset; + while (startIndex != -1) { + subset.push_back(nums[startIndex]); + startIndex = dp[startIndex][1]; + } + return subset; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number[]} + */ + largestDivisibleSubset(nums) { + nums.sort((a, b) => a - b); + let n = nums.length; + let dp = Array.from({ length: n }, () => [1, -1]); // dp[i] = [maxLen, prevIdx] + + let maxLen = 1, startIndex = 0; + for (let i = 0; i < n; i++) { + for (let j = 0; j < i; j++) { + if (nums[i] % nums[j] === 0 && dp[j][0] + 1 > dp[i][0]) { + dp[i][0] = dp[j][0] + 1; + dp[i][1] = j; + } + } + + if (dp[i][0] > maxLen) { + maxLen = dp[i][0]; + startIndex = i; + } + } + + let subset = []; + while (startIndex !== -1) { + subset.push(nums[startIndex]); + startIndex = dp[startIndex][1]; + } + return subset; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/largest-submatrix-with-rearrangements.md b/articles/largest-submatrix-with-rearrangements.md new file mode 100644 index 000000000..bebc4c94e --- /dev/null +++ b/articles/largest-submatrix-with-rearrangements.md @@ -0,0 +1,522 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def largestSubmatrix(self, matrix: List[List[int]]) -> int: + ROWS, COLS = len(matrix), len(matrix[0]) + res = 0 + + for start_row in range(ROWS): + ones = deque(list(range(COLS))) + + for r in range(start_row, ROWS): + if not ones: + break + for _ in range(len(ones)): + c = ones.popleft() + if matrix[r][c] == 1: + ones.append(c) + + res = max(res, len(ones) * (r - start_row + 1)) + + return res +``` + +```java +public class Solution { + public int largestSubmatrix(int[][] matrix) { + int ROWS = matrix.length, COLS = matrix[0].length; + int res = 0; + + for (int startRow = 0; startRow < ROWS; startRow++) { + Queue ones = new LinkedList<>(); + for (int c = 0; c < COLS; c++) { + ones.add(c); + } + + for (int r = startRow; r < ROWS; r++) { + if (ones.isEmpty()) break; + + for (int i = ones.size(); i > 0; i--) { + int c = ones.poll(); + if (matrix[r][c] == 1) { + ones.add(c); + } + } + + res = Math.max(res, ones.size() * (r - startRow + 1)); + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int largestSubmatrix(vector>& matrix) { + int ROWS = matrix.size(), COLS = matrix[0].size(); + int res = 0; + + for (int startRow = 0; startRow < ROWS; startRow++) { + queue ones; + for (int c = 0; c < COLS; c++) { + ones.push(c); + } + + for (int r = startRow; r < ROWS; r++) { + if (ones.empty()) break; + + for (int i = ones.size(); i > 0; i--) { + int c = ones.front(); ones.pop(); + if (matrix[r][c] == 1) { + ones.push(c); + } + } + + res = max(res, (int)ones.size() * (r - startRow + 1)); + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} matrix + * @return {number} + */ + largestSubmatrix(matrix) { + const ROWS = matrix.length, COLS = matrix[0].length; + let res = 0; + + for (let startRow = 0; startRow < ROWS; startRow++) { + const ones = new Queue(); + for (let c = 0; c < COLS; c++) { + ones.push(c); + } + + for (let r = startRow; r < ROWS; r++) { + if (ones.isEmpty()) break; + + for (let i = ones.size(); i > 0; i--) { + let c = ones.pop(); + if (matrix[r][c] === 1) { + ones.push(c); + } + } + + res = Math.max(res, ones.size() * (r - startRow + 1)); + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n ^ 2)$ +* Space complexity: $O(n)$ + +> Where $m$ is the number of rows and $n$ is the number of columns. + +--- + +## 2. Greedy + Sorting + +::tabs-start + +```python +class Solution: + def largestSubmatrix(self, matrix: List[List[int]]) -> int: + ROWS, COLS = len(matrix), len(matrix[0]) + res = 0 + prev_heights = [0] * COLS + + for r in range(ROWS): + heights = matrix[r][:] + for c in range(COLS): + if heights[c] > 0: + heights[c] += prev_heights[c] + + sorted_heights = sorted(heights, reverse=True) + for i in range(COLS): + res = max(res, (i + 1) * sorted_heights[i]) + + prev_heights = heights + + return res +``` + +```java +public class Solution { + public int largestSubmatrix(int[][] matrix) { + int ROWS = matrix.length, COLS = matrix[0].length; + int res = 0; + int[] prevHeights = new int[COLS]; + + for (int r = 0; r < ROWS; r++) { + int[] heights = Arrays.copyOf(matrix[r], COLS); + int[] sortedHgts = Arrays.copyOf(matrix[r], COLS); + + for (int c = 0; c < COLS; c++) { + if (heights[c] > 0) { + heights[c] += prevHeights[c]; + sortedHgts[c] = heights[c]; + } + } + + Arrays.sort(sortedHgts); + for (int i = COLS - 1; i >= 0; i--) { + res = Math.max(res, (COLS - i) * sortedHgts[i]); + } + + prevHeights = heights; + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int largestSubmatrix(vector>& matrix) { + int ROWS = matrix.size(), COLS = matrix[0].size(); + int res = 0; + vector prevHeights(COLS); + + for (int r = 0; r < ROWS; r++) { + vector heights = matrix[r]; + vector sortedHgts = matrix[r]; + + for (int c = 0; c < COLS; c++) { + if (heights[c] > 0) { + heights[c] += prevHeights[c]; + sortedHgts[c] = heights[c]; + } + } + + sort(sortedHgts.begin(), sortedHgts.end(), greater()); + for (int i = 0; i < COLS; i++) { + res = max(res, (i + 1) * sortedHgts[i]); + } + + prevHeights = heights; + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} matrix + * @return {number} + */ + largestSubmatrix(matrix) { + const ROWS = matrix.length, COLS = matrix[0].length; + let res = 0; + let prevHeights = new Array(COLS).fill(0); + + for (let r = 0; r < ROWS; r++) { + let heights = [...matrix[r]]; + let sortedHgts = [...matrix[r]]; + + for (let c = 0; c < COLS; c++) { + if (heights[c] > 0) { + heights[c] += prevHeights[c]; + sortedHgts[c] = heights[c]; + } + } + + sortedHgts.sort((a, b) => b - a); + for (let i = 0; i < COLS; i++) { + res = Math.max(res, (i + 1) * sortedHgts[i]); + } + + prevHeights = heights; + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n \log n)$ +* Space complexity: $O(n)$ + +> Where $m$ is the number of rows and $n$ is the number of columns. + +--- + +## 3. Greedy + Sorting (Overwriting the Input) + +::tabs-start + +```python +class Solution: + def largestSubmatrix(self, matrix: List[List[int]]) -> int: + ROWS, COLS = len(matrix), len(matrix[0]) + res = 0 + + for r in range(1, ROWS): + for c in range(COLS): + if matrix[r][c]: + matrix[r][c] += matrix[r - 1][c] + + for r in range(ROWS): + matrix[r].sort(reverse=True) + for i in range(COLS): + res = max(res, (i + 1) * matrix[r][i]) + + return res +``` + +```java +public class Solution { + public int largestSubmatrix(int[][] matrix) { + int ROWS = matrix.length, COLS = matrix[0].length; + int res = 0; + + for (int r = 1; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + if (matrix[r][c] > 0) { + matrix[r][c] += matrix[r - 1][c]; + } + } + } + + for (int r = 0; r < ROWS; r++) { + Arrays.sort(matrix[r]); + for (int i = 0; i < COLS; i++) { + res = Math.max(res, (COLS - i) * matrix[r][i]); + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int largestSubmatrix(vector>& matrix) { + int ROWS = matrix.size(), COLS = matrix[0].size(); + int res = 0; + + for (int r = 1; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + if (matrix[r][c] > 0) { + matrix[r][c] += matrix[r - 1][c]; + } + } + } + + for (int r = 0; r < ROWS; r++) { + sort(matrix[r].begin(), matrix[r].end(), greater()); + for (int i = 0; i < COLS; i++) { + res = max(res, (i + 1) * matrix[r][i]); + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} matrix + * @return {number} + */ + largestSubmatrix(matrix) { + const ROWS = matrix.length, COLS = matrix[0].length; + let res = 0; + + for (let r = 1; r < ROWS; r++) { + for (let c = 0; c < COLS; c++) { + if (matrix[r][c] > 0) { + matrix[r][c] += matrix[r - 1][c]; + } + } + } + + for (let r = 0; r < ROWS; r++) { + matrix[r].sort((a, b) => b - a); + for (let i = 0; i < COLS; i++) { + res = Math.max(res, (i + 1) * matrix[r][i]); + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n \log n)$ +* Space complexity: $O(1)$ or $O(n)$ depending on the sorting algoirhtm. + +> Where $m$ is the number of rows and $n$ is the number of columns. + +--- + +## 4. Greedy + +::tabs-start + +```python +class Solution: + def largestSubmatrix(self, matrix: List[List[int]]) -> int: + ROWS, COLS = len(matrix), len(matrix[0]) + res = 0 + prevHeights = [] + + for r in range(ROWS): + heights = [] + for c in prevHeights: + if matrix[r][c]: + matrix[r][c] += matrix[r - 1][c] + heights.append(c) + + for c in range(COLS): + if matrix[r][c] == 1: + heights.append(c) + + for i, c in enumerate(heights): + res = max(res, (i + 1) * matrix[r][c]) + + prevHeights = heights + + return res +``` + +```java +public class Solution { + public int largestSubmatrix(int[][] matrix) { + int ROWS = matrix.length, COLS = matrix[0].length; + int res = 0; + List prevHeights = new ArrayList<>(); + + for (int r = 0; r < ROWS; r++) { + List heights = new ArrayList<>(); + + for (int c : prevHeights) { + if (matrix[r][c] == 1) { + matrix[r][c] += matrix[r - 1][c]; + heights.add(c); + } + } + + for (int c = 0; c < COLS; c++) { + if (matrix[r][c] == 1) { + heights.add(c); + } + } + + for (int i = 0; i < heights.size(); i++) { + res = Math.max(res, (i + 1) * matrix[r][heights.get(i)]); + } + + prevHeights = heights; + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int largestSubmatrix(vector>& matrix) { + int ROWS = matrix.size(), COLS = matrix[0].size(), res = 0; + vector prevHeights; + + for (int r = 0; r < ROWS; r++) { + vector heights; + + for (int c : prevHeights) { + if (matrix[r][c] == 1) { + matrix[r][c] += matrix[r - 1][c]; + heights.push_back(c); + } + } + + for (int c = 0; c < COLS; c++) { + if (matrix[r][c] == 1) { + heights.push_back(c); + } + } + + for (int i = 0; i < heights.size(); i++) { + res = max(res, (i + 1) * matrix[r][heights[i]]); + } + + prevHeights = heights; + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} matrix + * @return {number} + */ + largestSubmatrix(matrix) { + const ROWS = matrix.length, COLS = matrix[0].length; + let res = 0, prevHeights = []; + + for (let r = 0; r < ROWS; r++) { + let heights = []; + + for (let c of prevHeights) { + if (matrix[r][c] === 1) { + matrix[r][c] += matrix[r - 1][c]; + heights.push(c); + } + } + + for (let c = 0; c < COLS; c++) { + if (matrix[r][c] === 1) { + heights.push(c); + } + } + + for (let i = 0; i < heights.length; i++) { + res = Math.max(res, (i + 1) * matrix[r][heights[i]]); + } + + prevHeights = heights; + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n)$ +* Space complexity: $O(n)$ + +> Where $m$ is the number of rows and $n$ is the number of columns. \ No newline at end of file diff --git a/articles/maximize-score-after-n-operations.md b/articles/maximize-score-after-n-operations.md new file mode 100644 index 000000000..bf90e9d55 --- /dev/null +++ b/articles/maximize-score-after-n-operations.md @@ -0,0 +1,629 @@ +## 1. Brute Force (Backtracking) + +::tabs-start + +```python +class Solution: + def maxScore(self, nums: List[int]) -> int: + N = len(nums) + visit = [False] * N + + def dfs(n): + if n > (N // 2): + return 0 + + res = 0 + for i in range(N): + if visit[i]: + continue + visit[i] = True + for j in range(i + 1, N): + if visit[j]: + continue + visit[j] = True + g = gcd(nums[i], nums[j]) + res = max(res, n * g + dfs(n + 1)) + visit[j] = False + visit[i] = False + + return res + + return dfs(1) +``` + +```java +public class Solution { + public int maxScore(int[] nums) { + int N = nums.length; + boolean[] visit = new boolean[N]; + return dfs(nums, visit, 1, N); + } + + private int dfs(int[] nums, boolean[] visit, int n, int N) { + if (n > N / 2) { + return 0; + } + + int res = 0; + for (int i = 0; i < N; i++) { + if (visit[i]) continue; + visit[i] = true; + for (int j = i + 1; j < N; j++) { + if (visit[j]) continue; + visit[j] = true; + int g = gcd(nums[i], nums[j]); + res = Math.max(res, n * g + dfs(nums, visit, n + 1, N)); + visit[j] = false; + } + visit[i] = false; + } + + return res; + } + + private int gcd(int a, int b) { + return b == 0 ? a : gcd(b, a % b); + } +} +``` + +```cpp +class Solution { +public: + int maxScore(vector& nums) { + int N = nums.size(); + vector visit(N, false); + return dfs(nums, visit, 1, N); + } + +private: + int dfs(vector& nums, vector& visit, int n, int N) { + if (n > N / 2) { + return 0; + } + + int res = 0; + for (int i = 0; i < N; i++) { + if (visit[i]) continue; + visit[i] = true; + for (int j = i + 1; j < N; j++) { + if (visit[j]) continue; + visit[j] = true; + int g = gcd(nums[i], nums[j]); + res = max(res, n * g + dfs(nums, visit, n + 1, N)); + visit[j] = false; + } + visit[i] = false; + } + + return res; + } + + int gcd(int a, int b) { + return b == 0 ? a : gcd(b, a % b); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + maxScore(nums) { + const N = nums.length; + const visit = new Array(N).fill(false); + + const gcd = (a, b) => { + return b === 0 ? a : gcd(b, a % b); + }; + const dfs = (n) => { + if (n > N / 2) { + return 0; + } + + let res = 0; + for (let i = 0; i < N; i++) { + if (visit[i]) continue; + visit[i] = true; + for (let j = i + 1; j < N; j++) { + if (visit[j]) continue; + visit[j] = true; + let g = gcd(nums[i], nums[j]); + res = Math.max(res, n * g + dfs(n + 1)); + visit[j] = false; + } + visit[i] = false; + } + + return res; + }; + + return dfs(1); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ n * \log m)$ +* Space complexity: $O(n)$ + +> Where $n$ is the size of the array $nums$ and $m$ is the maximum element in the array. + +--- + +## 2. Bitmask DP (Top-Down) - I + +::tabs-start + +```python +class Solution: + def maxScore(self, nums: List[int]) -> int: + cache = collections.defaultdict(int) + + def dfs(mask, op): + if mask in cache: + return cache[mask] + + for i in range(len(nums)): + for j in range(i + 1, len(nums)): + if (1 << i) & mask or (1 << j) & mask: + continue + + newMask = mask | (1 << i) | (1 << j) + score = op * math.gcd(nums[i], nums[j]) + cache[mask] = max( + cache[mask], + score + dfs(newMask, op + 1) + ) + + return cache[mask] + + return dfs(0, 1) +``` + +```java +public class Solution { + private Map cache; + + public int maxScore(int[] nums) { + cache = new HashMap<>(); + return dfs(0, 1, nums); + } + + private int dfs(int mask, int op, int[] nums) { + if (cache.containsKey(mask)) { + return cache.get(mask); + } + + int maxScore = 0; + int n = nums.length; + for (int i = 0; i < n; i++) { + if ((mask & (1 << i)) != 0) continue; + for (int j = i + 1; j < n; j++) { + if ((mask & (1 << j)) != 0) continue; + int newMask = mask | (1 << i) | (1 << j); + int score = op * gcd(nums[i], nums[j]) + dfs(newMask, op + 1, nums); + maxScore = Math.max(maxScore, score); + } + } + + cache.put(mask, maxScore); + return maxScore; + } + + private int gcd(int a, int b) { + return b == 0 ? a : gcd(b, a % b); + } +} +``` + +```cpp +class Solution { +public: + int maxScore(vector& nums) { + return dfs(0, 1, nums); + } + +private: + unordered_map cache; + + int dfs(int mask, int op, vector& nums) { + if (cache.count(mask)) { + return cache[mask]; + } + + int maxScore = 0; + int n = nums.size(); + for (int i = 0; i < n; i++) { + if ((mask & (1 << i)) != 0) continue; + for (int j = i + 1; j < n; j++) { + if ((mask & (1 << j)) != 0) continue; + int newMask = mask | (1 << i) | (1 << j); + int score = op * gcd(nums[i], nums[j]) + dfs(newMask, op + 1, nums); + maxScore = max(maxScore, score); + } + } + + return cache[mask] = maxScore; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + maxScore(nums) { + const cache = new Map(); + + const gcd = (a, b) => (b === 0 ? a : gcd(b, a % b)); + + const dfs = (mask, op) => { + if (cache.has(mask)) { + return cache.get(mask); + } + + let maxScore = 0; + const n = nums.length; + for (let i = 0; i < n; i++) { + if ((mask & (1 << i)) !== 0) continue; + for (let j = i + 1; j < n; j++) { + if ((mask & (1 << j)) !== 0) continue; + let newMask = mask | (1 << i) | (1 << j); + let score = op * gcd(nums[i], nums[j]) + dfs(newMask, op + 1); + maxScore = Math.max(maxScore, score); + } + } + + cache.set(mask, maxScore); + return maxScore; + }; + + + return dfs(0, 1); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2 * 2 ^ n * \log m)$ +* Space complexity: $O(2 ^ n)$ + +> Where $n$ is the size of the array $nums$ and $m$ is the maximum element in the array. + +--- + +## 3. Bitmask DP (Top-Down) - II + +::tabs-start + +```python +class Solution: + def maxScore(self, nums: List[int]) -> int: + n = len(nums) + GCD = [[0] * n for _ in range(n)] + for i in range(n): + for j in range(i + 1, n): + GCD[i][j] = gcd(nums[i], nums[j]) + + dp = [-1] * (1 << n) + def dfs(mask, op): + if dp[mask] != -1: + return dp[mask] + + max_score = 0 + for i in range(n): + if mask & (1 << i): + continue + for j in range(i + 1, n): + if mask & (1 << j): + continue + new_mask = mask | (1 << i) | (1 << j) + max_score = max( + max_score, + op * GCD[i][j] + dfs(new_mask, op + 1) + ) + + dp[mask] = max_score + return max_score + + return dfs(0, 1) +``` + +```java +public class Solution { + private int[][] GCD; + private int[] dp; + + public int maxScore(int[] nums) { + int n = nums.length; + GCD = new int[n][n]; + dp = new int[1 << n]; + Arrays.fill(dp, -1); + + for (int i = 0; i < n; i++) { + for (int j = i + 1; j < n; j++) { + GCD[i][j] = gcd(nums[i], nums[j]); + } + } + + return (int) dfs(0, 1, nums); + } + + private int dfs(int mask, int op, int[] nums) { + if (dp[mask] != -1) return dp[mask]; + + int maxScore = 0; + for (int i = 0; i < nums.length; i++) { + if ((mask & (1 << i)) != 0) continue; + for (int j = i + 1; j < nums.length; j++) { + if ((mask & (1 << j)) != 0) continue; + int newMask = mask | (1 << i) | (1 << j); + maxScore = Math.max( + maxScore, + op * GCD[i][j] + dfs(newMask, op + 1, nums) + ); + } + } + return dp[mask] = maxScore; + } + + private int gcd(int a, int b) { + return b == 0 ? a : gcd(b, a % b); + } +} +``` + +```cpp +class Solution { +public: + int maxScore(vector& nums) { + int n = nums.size(); + GCD.assign(n, vector(n, 0)); + dp.assign(1 << n, -1); + + for (int i = 0; i < n; i++) { + for (int j = i + 1; j < n; j++) { + GCD[i][j] = gcd(nums[i], nums[j]); + } + } + + return dfs(0, 1, nums); + } + +private: + vector> GCD; + vector dp; + + int dfs(int mask, int op, vector& nums) { + if (dp[mask] != -1) return dp[mask]; + + int maxScore = 0; + for (int i = 0; i < nums.size(); i++) { + if (mask & (1 << i)) continue; + for (int j = i + 1; j < nums.size(); j++) { + if (mask & (1 << j)) continue; + int newMask = mask | (1 << i) | (1 << j); + maxScore = max( + maxScore, + op * GCD[i][j] + dfs(newMask, op + 1, nums) + ); + } + } + return dp[mask] = maxScore; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + maxScore(nums) { + const n = nums.length; + const GCD = Array.from({ length: n }, () => Array(n).fill(0)); + const dp = Array(1 << n).fill(-1); + + const gcd = (a, b) => (b === 0 ? a : gcd(b, a % b)); + for (let i = 0; i < n; i++) { + for (let j = i + 1; j < n; j++) { + GCD[i][j] = gcd(nums[i], nums[j]); + } + } + + const dfs = (mask, op) => { + if (dp[mask] !== -1) return dp[mask]; + + let maxScore = 0; + for (let i = 0; i < n; i++) { + if (mask & (1 << i)) continue; + for (let j = i + 1; j < n; j++) { + if (mask & (1 << j)) continue; + const newMask = mask | (1 << i) | (1 << j); + maxScore = Math.max( + maxScore, + op * GCD[i][j] + dfs(newMask, op + 1) + ); + } + } + return dp[mask] = maxScore; + }; + + return dfs(0, 1); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2 * (2 ^ n + \log m))$ +* Space complexity: $O(n ^ 2 + 2 ^ n)$ + +> Where $n$ is the size of the array $nums$ and $m$ is the maximum element in the array. + +--- + +## 4. Bitmask DP (Bottom-Up) + +::tabs-start + +```python +class Solution: + def maxScore(self, nums: List[int]) -> int: + n = len(nums) + N = 1 << n + GCD = [[0] * n for _ in range(n)] + for i in range(n): + for j in range(i + 1, n): + GCD[i][j] = gcd(nums[i], nums[j]) + + dp = [0] * N + for mask in range(N - 1, -1, -1): + bits = bin(mask).count('1') + if bits % 2 == 1: + continue + op = bits // 2 + 1 + + for i in range(n): + if mask & (1 << i): + continue + for j in range(i + 1, n): + if mask & (1 << j): + continue + new_mask = mask | (1 << i) | (1 << j) + dp[mask] = max(dp[mask], op * GCD[i][j] + dp[new_mask]) + + return dp[0] +``` + +```java +public class Solution { + public int maxScore(int[] nums) { + int n = nums.length; + int N = 1 << n; + int[][] GCD = new int[n][n]; + + for (int i = 0; i < n; i++) { + for (int j = i + 1; j < n; j++) { + GCD[i][j] = gcd(nums[i], nums[j]); + } + } + + int[] dp = new int[N]; + for (int mask = N - 1; mask >= 0; mask--) { + int bits = Integer.bitCount(mask); + if (bits % 2 == 1) continue; + int op = bits / 2 + 1; + + for (int i = 0; i < n; i++) { + if ((mask & (1 << i)) != 0) continue; + for (int j = i + 1; j < n; j++) { + if ((mask & (1 << j)) != 0) continue; + int newMask = mask | (1 << i) | (1 << j); + dp[mask] = Math.max(dp[mask], op * GCD[i][j] + dp[newMask]); + } + } + } + return dp[0]; + } + + private int gcd(int a, int b) { + return b == 0 ? a : gcd(b, a % b); + } +} +``` + +```cpp +class Solution { +public: + int maxScore(vector& nums) { + int n = nums.size(); + int N = 1 << n; + vector> GCD(n, vector(n, 0)); + + for (int i = 0; i < n; i++) { + for (int j = i + 1; j < n; j++) { + GCD[i][j] = __gcd(nums[i], nums[j]); + } + } + + vector dp(N, 0); + for (int mask = N - 1; mask >= 0; mask--) { + int bits = __builtin_popcount(mask); + if (bits % 2 == 1) continue; + int op = bits / 2 + 1; + + for (int i = 0; i < n; i++) { + if (mask & (1 << i)) continue; + for (int j = i + 1; j < n; j++) { + if (mask & (1 << j)) continue; + int newMask = mask | (1 << i) | (1 << j); + dp[mask] = max(dp[mask], op * GCD[i][j] + dp[newMask]); + } + } + } + return dp[0]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + maxScore(nums) { + const n = nums.length; + const N = 1 << n; + const GCD = Array.from({ length: n }, () => Array(n).fill(0)); + + const gcd = (a, b) => (b === 0 ? a : gcd(b, a % b)); + for (let i = 0; i < n; i++) { + for (let j = i + 1; j < n; j++) { + GCD[i][j] = gcd(nums[i], nums[j]); + } + } + + const dp = Array(N).fill(0); + for (let mask = N - 1; mask >= 0; mask--) { + let bits = mask.toString(2).split("0").join("").length; + if (bits % 2 === 1) continue; + let op = bits / 2 + 1; + + for (let i = 0; i < n; i++) { + if ((mask & (1 << i)) !== 0) continue; + for (let j = i + 1; j < n; j++) { + if ((mask & (1 << j)) !== 0) continue; + let newMask = mask | (1 << i) | (1 << j); + dp[mask] = Math.max(dp[mask], op * GCD[i][j] + dp[newMask]); + } + } + } + return dp[0]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2 * (2 ^ n + \log m))$ +* Space complexity: $O(n ^ 2 + 2 ^ n)$ + +> Where $n$ is the size of the array $nums$ and $m$ is the maximum element in the array. \ No newline at end of file diff --git a/articles/maximum-element-after-decreasing-and-rearranging.md b/articles/maximum-element-after-decreasing-and-rearranging.md new file mode 100644 index 000000000..b7c01b0db --- /dev/null +++ b/articles/maximum-element-after-decreasing-and-rearranging.md @@ -0,0 +1,158 @@ +## 1. Greedy + Sorting + +::tabs-start + +```python +class Solution: + def maximumElementAfterDecrementingAndRearranging(self, arr: List[int]) -> int: + arr.sort() + prev = 0 + for num in arr: + prev = min(prev + 1, num) + return prev +``` + +```java +public class Solution { + public int maximumElementAfterDecrementingAndRearranging(int[] arr) { + Arrays.sort(arr); + int prev = 0; + for (int num : arr) { + prev = Math.min(prev + 1, num); + } + return prev; + } +} +``` + +```cpp +class Solution { +public: + int maximumElementAfterDecrementingAndRearranging(vector& arr) { + sort(arr.begin(), arr.end()); + int prev = 0; + for (int num : arr) { + prev = min(prev + 1, num); + } + return prev; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} arr + * @return {number} + */ + maximumElementAfterDecrementingAndRearranging(arr) { + arr.sort((a, b) => a - b); + let prev = 0; + for (let num of arr) { + prev = Math.min(prev + 1, num); + } + return prev; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(1)$ or $O(n)$ depending on the sorting algorithm. + +--- + +## 2. Greedy + +::tabs-start + +```python +class Solution: + def maximumElementAfterDecrementingAndRearranging(self, arr: List[int]) -> int: + n = len(arr) + count = [0] * (n + 1) + + for num in arr: + count[min(num, n)] += 1 + + prev = 1 + for num in range(2, n + 1): + prev = min(prev + count[num], num) + + return prev +``` + +```java +public class Solution { + public int maximumElementAfterDecrementingAndRearranging(int[] arr) { + int n = arr.length; + int[] count = new int[n + 1]; + + for (int num : arr) { + count[Math.min(num, n)]++; + } + + int prev = 1; + for (int num = 2; num <= n; num++) { + prev = Math.min(prev + count[num], num); + } + + return prev; + } +} +``` + +```cpp +class Solution { +public: + int maximumElementAfterDecrementingAndRearranging(vector& arr) { + int n = arr.size(); + vector count(n + 1, 0); + + for (int num : arr) { + count[min(num, n)]++; + } + + int prev = 1; + for (int num = 2; num <= n; num++) { + prev = min(prev + count[num], num); + } + + return prev; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} arr + * @return {number} + */ + maximumElementAfterDecrementingAndRearranging(arr) { + let n = arr.length; + let count = new Array(n + 1).fill(0); + + for (let num of arr) { + count[Math.min(num, n)]++; + } + + let prev = 1; + for (let num = 2; num <= n; num++) { + prev = Math.min(prev + count[num], num); + } + + return prev; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/maximum-length-of-a-concatenated-string-with-unique-characters.md b/articles/maximum-length-of-a-concatenated-string-with-unique-characters.md new file mode 100644 index 000000000..f8cdea423 --- /dev/null +++ b/articles/maximum-length-of-a-concatenated-string-with-unique-characters.md @@ -0,0 +1,857 @@ +## 1. Backtracking (Hash Set) + +::tabs-start + +```python +class Solution: + def maxLength(self, arr: List[str]) -> int: + charSet = set() + + def overlap(charSet, s): + prev = set() + for c in s: + if c in charSet or c in prev: + return True + prev.add(c) + return False + + def backtrack(i): + if i == len(arr): + return len(charSet) + + res = 0 + if not overlap(charSet, arr[i]): + for c in arr[i]: + charSet.add(c) + res = backtrack(i + 1) + for c in arr[i]: + charSet.remove(c) + + return max(res, backtrack(i + 1)) + + return backtrack(0) +``` + +```java +public class Solution { + public int maxLength(List arr) { + Set charSet = new HashSet<>(); + return backtrack(0, arr, charSet); + } + + private boolean overlap(Set charSet, String s) { + Set prev = new HashSet<>(); + for (char c : s.toCharArray()) { + if (charSet.contains(c) || prev.contains(c)) { + return true; + } + prev.add(c); + } + return false; + } + + private int backtrack(int i, List arr, Set charSet) { + if (i == arr.size()) { + return charSet.size(); + } + + int res = 0; + if (!overlap(charSet, arr.get(i))) { + for (char c : arr.get(i).toCharArray()) { + charSet.add(c); + } + res = backtrack(i + 1, arr, charSet); + for (char c : arr.get(i).toCharArray()) { + charSet.remove(c); + } + } + + return Math.max(res, backtrack(i + 1, arr, charSet)); + } +} +``` + +```cpp +class Solution { +public: + int maxLength(vector& arr) { + unordered_set charSet; + return backtrack(0, arr, charSet); + } + +private: + bool overlap(unordered_set& charSet, const string& s) { + unordered_set prev; + for (char c : s) { + if (charSet.count(c) || prev.count(c)) { + return true; + } + prev.insert(c); + } + return false; + } + + int backtrack(int i, vector& arr, unordered_set& charSet) { + if (i == arr.size()) { + return charSet.size(); + } + + int res = 0; + if (!overlap(charSet, arr[i])) { + for (char c : arr[i]) { + charSet.insert(c); + } + res = backtrack(i + 1, arr, charSet); + for (char c : arr[i]) { + charSet.erase(c); + } + } + + return max(res, backtrack(i + 1, arr, charSet)); + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} arr + * @return {number} + */ + maxLength(arr) { + let charSet = new Set(); + + const overlap = (charSet, s) => { + let prev = new Set(); + for (const c of s) { + if (charSet.has(c) || prev.has(c)) { + return true; + } + prev.add(c); + } + return false; + }; + + const backtrack = (i) => { + if (i === arr.length) { + return charSet.size; + } + + let res = 0; + if (!overlap(charSet, arr[i])) { + for (const c of arr[i]) { + charSet.add(c); + } + res = backtrack(i + 1); + for (const c of arr[i]) { + charSet.delete(c); + } + } + + return Math.max(res, backtrack(i + 1)); + }; + + return backtrack(0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * 2 ^ n)$ +* Space complexity: $O(n)$ for recursion stack. + +> Where $n$ is the number of strings and $m$ is the maximum length of a string. + +--- + +## 2. Backtracking (Boolean Array) + +::tabs-start + +```python +class Solution: + def maxLength(self, arr: List[str]) -> int: + charSet = [False] * 26 + + def getIdx(c): + return ord(c) - ord('a') + + def overlap(charSet, s): + for i in range(len(s)): + c = getIdx(s[i]) + if charSet[c]: + for j in range(i): + charSet[getIdx(s[j])] = False + return True + charSet[c] = True + return False + + def backtrack(i): + if i == len(arr): + return 0 + + res = 0 + if not overlap(charSet, arr[i]): + res = len(arr[i]) + backtrack(i + 1) + for c in arr[i]: + charSet[getIdx(c)] = False + return max(res, backtrack(i + 1)) + + return backtrack(0) +``` + +```java +public class Solution { + public int maxLength(List arr) { + boolean[] charSet = new boolean[26]; + return backtrack(0, arr, charSet); + } + + private int getIdx(char c) { + return c - 'a'; + } + + private boolean overlap(boolean[] charSet, String s) { + for (int i = 0; i < s.length(); i++) { + int c = getIdx(s.charAt(i)); + if (charSet[c]) { + for (int j = 0; j < i; j++) { + charSet[getIdx(s.charAt(j))] = false; + } + return true; + } + charSet[c] = true; + } + return false; + } + + private int backtrack(int i, List arr, boolean[] charSet) { + if (i == arr.size()) { + return 0; + } + + int res = 0; + if (!overlap(charSet, arr.get(i))) { + res = arr.get(i).length() + backtrack(i + 1, arr, charSet); + for (char c : arr.get(i).toCharArray()) { + charSet[getIdx(c)] = false; + } + } + return Math.max(res, backtrack(i + 1, arr, charSet)); + } +} +``` + +```cpp +class Solution { +public: + int maxLength(vector& arr) { + bool charSet[26] = {false}; + return backtrack(0, arr, charSet); + } + +private: + int getIdx(char c) { + return c - 'a'; + } + + bool overlap(bool charSet[], const string& s) { + for (int i = 0; i < s.length(); i++) { + int c = getIdx(s[i]); + if (charSet[c]) { + for (int j = 0; j < i; j++) { + charSet[getIdx(s[j])] = false; + } + return true; + } + charSet[c] = true; + } + return false; + } + + int backtrack(int i, vector& arr, bool charSet[]) { + if (i == arr.size()) { + return 0; + } + + int res = 0; + if (!overlap(charSet, arr[i])) { + res = arr[i].length() + backtrack(i + 1, arr, charSet); + for (char c : arr[i]) { + charSet[getIdx(c)] = false; + } + } + return max(res, backtrack(i + 1, arr, charSet)); + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} arr + * @return {number} + */ + maxLength(arr) { + let charSet = new Array(26).fill(false); + + const getIdx = (c) => c.charCodeAt(0) - 'a'.charCodeAt(0); + + const overlap = (charSet, s) => { + for (let i = 0; i < s.length; i++) { + let c = getIdx(s[i]); + if (charSet[c]) { + for (let j = 0; j < i; j++) { + charSet[getIdx(s[j])] = false; + } + return true; + } + charSet[c] = true; + } + return false; + }; + + const backtrack = (i) => { + if (i === arr.length) { + return 0; + } + + let res = 0; + if (!overlap(charSet, arr[i])) { + res = arr[i].length + backtrack(i + 1); + for (const c of arr[i]) { + charSet[getIdx(c)] = false; + } + } + return Math.max(res, backtrack(i + 1)); + }; + + return backtrack(0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * 2 ^ n)$ +* Space complexity: $O(n)$ for recursion stack. + +> Where $n$ is the number of strings and $m$ is the maximum length of a string. + +--- + +## 3. Recursion (Bit Mask) - I + +::tabs-start + +```python +class Solution: + def maxLength(self, arr: List[str]) -> int: + def getIdx(c): + return ord(c) - ord('a') + + A = [] + for s in arr: + cur = 0 + valid = True + for c in s: + if cur & (1 << getIdx(c)): + valid = False + break + cur |= (1 << getIdx(c)) + + if valid: + A.append([cur, len(s)]) + + def dfs(i, subSeq): + if i == len(A): + return 0 + + res = dfs(i + 1, subSeq) + + curSeq, length = A[i][0], A[i][1] + if (subSeq & curSeq) == 0: + res = max(res, length + dfs(i + 1, subSeq | curSeq)) + return res + + return dfs(0, 0) +``` + +```java +public class Solution { + public int maxLength(List arr) { + List A = new ArrayList<>(); + + for (String s : arr) { + int cur = 0; + boolean valid = true; + + for (char c : s.toCharArray()) { + if ((cur & (1 << (c - 'a'))) != 0) { + valid = false; + break; + } + cur |= (1 << (c - 'a')); + } + + if (valid) { + A.add(new int[]{cur, s.length()}); + } + } + + return dfs(0, 0, A); + } + + private int dfs(int i, int subSeq, List A) { + if (i == A.size()) { + return 0; + } + + int res = dfs(i + 1, subSeq, A); + + int curSeq = A.get(i)[0], length = A.get(i)[1]; + if ((subSeq & curSeq) == 0) { + res = Math.max(res, length + dfs(i + 1, subSeq | curSeq, A)); + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int maxLength(vector& arr) { + vector> A; + + for (const string& s : arr) { + int cur = 0; + bool valid = true; + + for (char c : s) { + if (cur & (1 << (c - 'a'))) { + valid = false; + break; + } + cur |= (1 << (c - 'a')); + } + + if (valid) { + A.emplace_back(cur, s.length()); + } + } + + return dfs(0, 0, A); + } + +private: + int dfs(int i, int subSeq, vector>& A) { + if (i == A.size()) { + return 0; + } + + int res = dfs(i + 1, subSeq, A); + + int curSeq = A[i].first, length = A[i].second; + if ((subSeq & curSeq) == 0) { + res = max(res, length + dfs(i + 1, subSeq | curSeq, A)); + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} arr + * @return {number} + */ + maxLength(arr) { + let A = []; + + for (const s of arr) { + let cur = 0; + let valid = true; + + for (const c of s) { + if (cur & (1 << (c.charCodeAt(0) - 97))) { + valid = false; + break; + } + cur |= (1 << (c.charCodeAt(0) - 97)); + } + + if (valid) { + A.push([cur, s.length]); + } + } + + const dfs = (i, subSeq) => { + if (i === A.length) { + return 0; + } + + let res = dfs(i + 1, subSeq); + + let [curSeq, length] = A[i]; + if ((subSeq & curSeq) === 0) { + res = Math.max(res, length + dfs(i + 1, subSeq | curSeq)); + } + return res; + }; + + return dfs(0, 0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n + 2 ^ n)$ +* Space complexity: $O(n)$ + +> Where $n$ is the number of strings and $m$ is the maximum length of a string. + +--- + +## 4. Recursion (Bit Mask) - II + +::tabs-start + +```python +class Solution: + def maxLength(self, arr: List[str]) -> int: + def getIdx(c): + return ord(c) - ord('a') + + A = [] + for s in arr: + cur = 0 + valid = True + for c in s: + if cur & (1 << getIdx(c)): + valid = False + break + cur |= (1 << getIdx(c)) + + if valid: + A.append([cur, len(s)]) + + def dfs(i, subSeq): + res = 0 + for j in range(i, len(A)): + curSeq, length = A[j][0], A[j][1] + if (subSeq & curSeq) == 0: + res = max(res, length + dfs(j + 1, subSeq | curSeq)) + return res + + return dfs(0, 0) +``` + +```java +public class Solution { + public int maxLength(List arr) { + List A = new ArrayList<>(); + + for (String s : arr) { + int cur = 0; + boolean valid = true; + + for (char c : s.toCharArray()) { + if ((cur & (1 << (c - 'a'))) != 0) { + valid = false; + break; + } + cur |= (1 << (c - 'a')); + } + + if (valid) { + A.add(new int[]{cur, s.length()}); + } + } + + return dfs(0, 0, A); + } + + private int dfs(int i, int subSeq, List A) { + int res = 0; + for (int j = i; j < A.size(); j++) { + int curSeq = A.get(j)[0], length = A.get(j)[1]; + if ((subSeq & curSeq) == 0) { + res = Math.max(res, length + dfs(j + 1, subSeq | curSeq, A)); + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int maxLength(vector& arr) { + vector> A; + + for (const string& s : arr) { + int cur = 0; + bool valid = true; + + for (char c : s) { + if (cur & (1 << (c - 'a'))) { + valid = false; + break; + } + cur |= (1 << (c - 'a')); + } + + if (valid) { + A.emplace_back(cur, s.length()); + } + } + + return dfs(0, 0, A); + } + +private: + int dfs(int i, int subSeq, vector>& A) { + int res = 0; + for (int j = i; j < A.size(); j++) { + int curSeq = A[j].first, length = A[j].second; + if ((subSeq & curSeq) == 0) { + res = max(res, length + dfs(j + 1, subSeq | curSeq, A)); + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} arr + * @return {number} + */ + maxLength(arr) { + let A = []; + + for (const s of arr) { + let cur = 0; + let valid = true; + + for (const c of s) { + if (cur & (1 << (c.charCodeAt(0) - 97))) { + valid = false; + break; + } + cur |= (1 << (c.charCodeAt(0) - 97)); + } + + if (valid) { + A.push([cur, s.length]); + } + } + + const dfs = (i, subSeq) => { + let res = 0; + for (let j = i; j < A.length; j++) { + let [curSeq, length] = A[j]; + if ((subSeq & curSeq) === 0) { + res = Math.max(res, length + dfs(j + 1, subSeq | curSeq)); + } + } + return res; + }; + + return dfs(0, 0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * (m + 2 ^ n))$ +* Space complexity: $O(n)$ + +> Where $n$ is the number of strings and $m$ is the maximum length of a string. + +--- + +## 5. Dynamic Programming + +::tabs-start + +```python +class Solution: + def maxLength(self, arr: List[str]) -> int: + dp = {0} + res = 0 + + for s in arr: + cur = 0 + valid = True + + for c in s: + bit = 1 << (ord(c) - ord('a')) + if cur & bit: + valid = False + break + cur |= bit + + if not valid: + continue + + next_dp = dp.copy() + for seq in dp: + if (seq & cur) or (seq | cur) in dp: + continue + next_dp.add(seq | cur) + res = max(res, bin(seq | cur).count('1')) + dp = next_dp + + return res +``` + +```java +public class Solution { + public int maxLength(List arr) { + Set dp = new HashSet<>(); + dp.add(0); + int res = 0; + + for (String s : arr) { + int cur = 0; + boolean valid = true; + + for (char c : s.toCharArray()) { + int bit = 1 << (c - 'a'); + if ((cur & bit) != 0) { + valid = false; + break; + } + cur |= bit; + } + + if (!valid) { + continue; + } + + Set next_dp = new HashSet<>(dp); + for (int seq : dp) { + if ((seq & cur) != 0 || next_dp.contains(seq | cur)) { + continue; + } + next_dp.add(seq | cur); + res = Math.max(res, Integer.bitCount(seq | cur)); + } + dp = next_dp; + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int maxLength(vector& arr) { + unordered_set dp; + dp.insert(0); + int res = 0; + + for (const string& s : arr) { + int cur = 0; + bool valid = true; + + for (char c : s) { + int bit = 1 << (c - 'a'); + if (cur & bit) { + valid = false; + break; + } + cur |= bit; + } + + if (!valid) { + continue; + } + + unordered_set next_dp(dp); + for (int seq : dp) { + if ((seq & cur) || next_dp.count(seq | cur)) { + continue; + } + next_dp.insert(seq | cur); + res = max(res, __builtin_popcount(seq | cur)); + } + dp = next_dp; + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} arr + * @return {number} + */ + maxLength(arr) { + let dp = new Set(); + dp.add(0); + let res = 0; + + for (const s of arr) { + let cur = 0; + let valid = true; + + for (const c of s) { + let bit = 1 << (c.charCodeAt(0) - 97); + if (cur & bit) { + valid = false; + break; + } + cur |= bit; + } + + if (!valid) { + continue; + } + + let next_dp = new Set(dp); + for (let seq of dp) { + if ((seq & cur) || next_dp.has(seq | cur)) { + continue; + } + next_dp.add(seq | cur); + res = Math.max(res, (seq | cur).toString(2).split('0').join('').length); + } + dp = next_dp; + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * (m + 2 ^ n))$ +* Space complexity: $O(2 ^ n)$ + +> Where $n$ is the number of strings and $m$ is the maximum length of a string. \ No newline at end of file diff --git a/articles/maximum-length-of-pair-chain.md b/articles/maximum-length-of-pair-chain.md new file mode 100644 index 000000000..2855ede51 --- /dev/null +++ b/articles/maximum-length-of-pair-chain.md @@ -0,0 +1,556 @@ +## 1. Recursion + +::tabs-start + +```python +class Solution: + def findLongestChain(self, pairs: List[List[int]]) -> int: + n = len(pairs) + pairs.sort(key=lambda x: x[1]) + + def dfs(i, j): + if i == n: + return 0 + + res = dfs(i + 1, j) + if j == -1 or pairs[j][1] < pairs[i][0]: + res = max(res, 1 + dfs(i + 1, i)) + + return res + + return dfs(0, -1) +``` + +```java +public class Solution { + public int findLongestChain(int[][] pairs) { + int n = pairs.length; + Arrays.sort(pairs, (a, b) -> Integer.compare(a[1], b[1])); + return dfs(pairs, 0, -1, n); + } + + private int dfs(int[][] pairs, int i, int j, int n) { + if (i == n) { + return 0; + } + + int res = dfs(pairs, i + 1, j, n); + if (j == -1 || pairs[j][1] < pairs[i][0]) { + res = Math.max(res, 1 + dfs(pairs, i + 1, i, n)); + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int findLongestChain(vector>& pairs) { + int n = pairs.size(); + sort(pairs.begin(), pairs.end(), [](const auto& a, const auto& b) { + return a[1] < b[1]; + }); + + return dfs(pairs, 0, -1, n); + } + +private: + int dfs(vector>& pairs, int i, int j, int n) { + if (i == n) { + return 0; + } + + int res = dfs(pairs, i + 1, j, n); + if (j == -1 || pairs[j][1] < pairs[i][0]) { + res = max(res, 1 + dfs(pairs, i + 1, i, n)); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} pairs + * @return {number} + */ + findLongestChain(pairs) { + pairs.sort((a, b) => a[1] - b[1]); + let n = pairs.length; + + const dfs = (i, j) => { + if (i === n) { + return 0; + } + + let res = dfs(i + 1, j); + if (j === -1 || pairs[j][1] < pairs[i][0]) { + res = Math.max(res, 1 + dfs(i + 1, i)); + } + + return res; + }; + + return dfs(0, -1); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(2 ^ n)$ +* Space complexity: $O(n)$ for recursion stack. + +--- + +## 2. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def findLongestChain(self, pairs: List[List[int]]) -> int: + n = len(pairs) + pairs.sort(key=lambda x: x[1]) + dp = [[-1] * (n + 1) for _ in range(n)] + + def dfs(i, j): + if i == n: + return 0 + if dp[i][j + 1] != -1: + return dp[i][j + 1] + + res = dfs(i + 1, j) + if j == -1 or pairs[j][1] < pairs[i][0]: + res = max(res, 1 + dfs(i + 1, i)) + + dp[i][j + 1] = res + return res + + return dfs(0, -1) +``` + +```java +public class Solution { + private int[][] dp; + + public int findLongestChain(int[][] pairs) { + int n = pairs.length; + Arrays.sort(pairs, (a, b) -> Integer.compare(a[1], b[1])); + dp = new int[n][n + 1]; + + for (int[] row : dp) { + Arrays.fill(row, -1); + } + + return dfs(pairs, 0, -1, n); + } + + private int dfs(int[][] pairs, int i, int j, int n) { + if (i == n) { + return 0; + } + if (dp[i][j + 1] != -1) { + return dp[i][j + 1]; + } + + int res = dfs(pairs, i + 1, j, n); + if (j == -1 || pairs[j][1] < pairs[i][0]) { + res = Math.max(res, 1 + dfs(pairs, i + 1, i, n)); + } + + dp[i][j + 1] = res; + return res; + } +} +``` + +```cpp +class Solution { +public: + vector> dp; + + int findLongestChain(vector>& pairs) { + int n = pairs.size(); + sort(pairs.begin(), pairs.end(), [](const auto& a, const auto& b) { + return a[1] < b[1]; + }); + + dp = vector>(n, vector(n + 1, -1)); + return dfs(pairs, 0, -1, n); + } + +private: + int dfs(vector>& pairs, int i, int j, int n) { + if (i == n) { + return 0; + } + if (dp[i][j + 1] != -1) { + return dp[i][j + 1]; + } + + int res = dfs(pairs, i + 1, j, n); + if (j == -1 || pairs[j][1] < pairs[i][0]) { + res = max(res, 1 + dfs(pairs, i + 1, i, n)); + } + + dp[i][j + 1] = res; + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} pairs + * @return {number} + */ + findLongestChain(pairs) { + pairs.sort((a, b) => a[1] - b[1]); + let n = pairs.length; + let dp = Array.from({ length: n }, () => new Array(n + 1).fill(-1)); + + const dfs = (i, j) => { + if (i === n) { + return 0; + } + if (dp[i][j + 1] !== -1) { + return dp[i][j + 1]; + } + + let res = dfs(i + 1, j); + if (j === -1 || pairs[j][1] < pairs[i][0]) { + res = Math.max(res, 1 + dfs(i + 1, i)); + } + + dp[i][j + 1] = res; + return res; + }; + + return dfs(0, -1); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n ^ 2)$ + +--- + +## 3. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def findLongestChain(self, pairs: List[List[int]]) -> int: + n = len(pairs) + pairs.sort(key=lambda x: x[1]) + dp = [1] * n + + for i in range(n): + for j in range(i): + if pairs[j][1] < pairs[i][0]: + dp[i] = max(dp[i], dp[j] + 1) + + return max(dp) +``` + +```java +public class Solution { + public int findLongestChain(int[][] pairs) { + int n = pairs.length; + Arrays.sort(pairs, (a, b) -> Integer.compare(a[1], b[1])); + int[] dp = new int[n]; + Arrays.fill(dp, 1); + + for (int i = 0; i < n; i++) { + for (int j = 0; j < i; j++) { + if (pairs[j][1] < pairs[i][0]) { + dp[i] = Math.max(dp[i], dp[j] + 1); + } + } + } + + return Arrays.stream(dp).max().getAsInt(); + } +} +``` + +```cpp +class Solution { +public: + int findLongestChain(vector>& pairs) { + int n = pairs.size(); + sort(pairs.begin(), pairs.end(), [](const auto& a, const auto& b) { + return a[1] < b[1]; + }); + + vector dp(n, 1); + + for (int i = 0; i < n; i++) { + for (int j = 0; j < i; j++) { + if (pairs[j][1] < pairs[i][0]) { + dp[i] = max(dp[i], dp[j] + 1); + } + } + } + + return *max_element(dp.begin(), dp.end()); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} pairs + * @return {number} + */ + findLongestChain(pairs) { + let n = pairs.length; + pairs.sort((a, b) => a[1] - b[1]); + let dp = new Array(n).fill(1); + + for (let i = 0; i < n; i++) { + for (let j = 0; j < i; j++) { + if (pairs[j][1] < pairs[i][0]) { + dp[i] = Math.max(dp[i], dp[j] + 1); + } + } + } + + return Math.max(...dp); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n)$ + +--- + +## 4. Dynamic Programming (Bianry Search) + +::tabs-start + +```python +class Solution: + def findLongestChain(self, pairs: List[List[int]]) -> int: + pairs.sort(key=lambda x: x[0]) + dp = [] + + for a, b in pairs: + pos = bisect_left(dp, a) + if pos == len(dp): + dp.append(b) + else: + dp[pos] = min(dp[pos], b) + + return len(dp) +``` + +```java +public class Solution { + public int findLongestChain(int[][] pairs) { + Arrays.sort(pairs, Comparator.comparingInt(a -> a[0])); + List dp = new ArrayList<>(); + + for (int[] pair : pairs) { + int pos = binarySearch(dp, pair[0]); + if (pos == dp.size()) { + dp.add(pair[1]); + } else { + dp.set(pos, Math.min(dp.get(pos), pair[1])); + } + } + + return dp.size(); + } + + private int binarySearch(List dp, int target) { + int left = 0, right = dp.size() - 1; + while (left <= right) { + int mid = left + (right - left) / 2; + if (dp.get(mid) < target) { + left = mid + 1; + } else { + right = mid - 1; + } + } + return left; + } +} +``` + +```cpp +class Solution { +public: + int findLongestChain(vector>& pairs) { + sort(pairs.begin(), pairs.end()); + vector dp; + + for (auto& pair : pairs) { + auto it = lower_bound(dp.begin(), dp.end(), pair[0]); + if (it == dp.end()) { + dp.push_back(pair[1]); + } else { + *it = min(*it, pair[1]); + } + } + + return dp.size(); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} pairs + * @return {number} + */ + findLongestChain(pairs) { + pairs.sort((a, b) => a[0] - b[0]); + let dp = []; + + const binarySearch = (target) => { + let left = 0, right = dp.length - 1; + while (left <= right) { + let mid = Math.floor((left + right) / 2); + if (dp[mid] < target) { + left = mid + 1; + } else { + right = mid - 1; + } + } + return left; + }; + + for (let i = 0; i < pairs.length; i++) { + let pos = binarySearch(pairs[i][0]); + if (pos === dp.length) { + dp.push(pairs[i][1]); + } else { + dp[pos] = Math.min(dp[pos], pairs[i][1]); + } + } + + return dp.length; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ + +--- + +## 5. Greedy + +::tabs-start + +```python +class Solution: + def findLongestChain(self, pairs: List[List[int]]) -> int: + pairs.sort(key=lambda p: p[1]) + length = 1 + end = pairs[0][1] + + for i in range(1, len(pairs)): + if end < pairs[i][0]: + length += 1 + end = pairs[i][1] + + return length +``` + +```java +public class Solution { + public int findLongestChain(int[][] pairs) { + Arrays.sort(pairs, (a, b) -> Integer.compare(a[1], b[1])); + int length = 1; + int end = pairs[0][1]; + + for (int i = 1; i < pairs.length; i++) { + if (end < pairs[i][0]) { + length++; + end = pairs[i][1]; + } + } + + return length; + } +} +``` + +```cpp +class Solution { +public: + int findLongestChain(vector>& pairs) { + sort(pairs.begin(), pairs.end(), [](const auto& a, const auto& b) { + return a[1] < b[1]; + }); + + int length = 1, end = pairs[0][1]; + + for (int i = 1; i < pairs.size(); i++) { + if (end < pairs[i][0]) { + length++; + end = pairs[i][1]; + } + } + + return length; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} pairs + * @return {number} + */ + findLongestChain(pairs) { + pairs.sort((a, b) => a[1] - b[1]); + let length = 1; + let end = pairs[0][1]; + + for (let i = 1; i < pairs.length; i++) { + if (end < pairs[i][0]) { + length++; + end = pairs[i][1]; + } + } + + return length; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n\log n)$ +* Space complexity: $O(1)$ or $O(n)$ depending on the sorting algorithm. \ No newline at end of file diff --git a/articles/maximum-nesting-depth-of-the-parentheses.md b/articles/maximum-nesting-depth-of-the-parentheses.md new file mode 100644 index 000000000..f18d24a7f --- /dev/null +++ b/articles/maximum-nesting-depth-of-the-parentheses.md @@ -0,0 +1,305 @@ +## 1. Recursion + +::tabs-start + +```python +class Solution: + def maxDepth(self, s: str) -> int: + res = 0 + + def dfs(i): + nonlocal res + if i == len(s): + return 0 + + cur = dfs(i + 1) + if s[i] == '(': + cur += 1 + elif s[i] == ')': + cur -= 1 + + res = max(res, abs(cur)) + return cur + + dfs(0) + return res +``` + +```java +public class Solution { + private int res = 0; + + public int maxDepth(String s) { + dfs(s, 0); + return res; + } + + private int dfs(String s, int i) { + if (i == s.length()) { + return 0; + } + + int cur = dfs(s, i + 1); + if (s.charAt(i) == '(') { + cur += 1; + } else if (s.charAt(i) == ')') { + cur -= 1; + } + + res = Math.max(res, Math.abs(cur)); + return cur; + } +} +``` + +```cpp +class Solution { +private: + int res = 0; + + int dfs(const string& s, int i) { + if (i == s.length()) { + return 0; + } + + int cur = dfs(s, i + 1); + if (s[i] == '(') { + cur += 1; + } else if (s[i] == ')') { + cur -= 1; + } + + res = max(res, abs(cur)); + return cur; + } + +public: + int maxDepth(string s) { + dfs(s, 0); + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {number} + */ + maxDepth(s) { + let res = 0; + + const dfs = (i) => { + if (i === s.length) { + return 0; + } + + let cur = dfs(i + 1); + if (s[i] === '(') { + cur += 1; + } else if (s[i] === ')') { + cur -= 1; + } + + res = Math.max(res, Math.abs(cur)); + return cur; + }; + + dfs(0); + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ for recursion stack. + +--- + +## 2. Stack + +::tabs-start + +```python +class Solution: + def maxDepth(self, s: str) -> int: + res, stack = 0, [] + + for c in s: + if c == "(": + stack.append(c) + res = max(res, len(stack)) + elif c == ")": + stack.pop() + + return res +``` + +```java +public class Solution { + public int maxDepth(String s) { + int res = 0; + Stack stack = new Stack<>(); + + for (char c : s.toCharArray()) { + if (c == '(') { + stack.push(c); + res = Math.max(res, stack.size()); + } else if (c == ')') { + stack.pop(); + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int maxDepth(string s) { + int res = 0; + stack stack; + + for (char c : s) { + if (c == '(') { + stack.push(c); + res = max(res, (int)stack.size()); + } else if (c == ')') { + stack.pop(); + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {number} + */ + maxDepth(s) { + let res = 0; + let stack = []; + + for (let c of s) { + if (c === '(') { + stack.push(c); + res = Math.max(res, stack.length); + } else if (c === ')') { + stack.pop(); + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Iteration + +::tabs-start + +```python +class Solution: + def maxDepth(self, s: str) -> int: + res = 0 + cur = 0 + + for c in s: + if c == "(": + cur += 1 + elif c == ")": + cur -= 1 + res = max(res, cur) + + return res +``` + +```java +public class Solution { + public int maxDepth(String s) { + int res = 0, cur = 0; + + for (int i = 0; i < s.length(); i++) { + char c = s.charAt(i); + if (c == '(') { + cur++; + } else if (c == ')') { + cur--; + } + res = Math.max(res, cur); + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int maxDepth(string s) { + int res = 0, cur = 0; + + for (char c : s) { + if (c == '(') { + cur++; + } else if (c == ')') { + cur--; + } + res = max(res, cur); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {number} + */ + maxDepth(s) { + let res = 0, cur = 0; + + for (let c of s) { + if (c === '(') { + cur++; + } else if (c === ')') { + cur--; + } + res = Math.max(res, cur); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. \ No newline at end of file diff --git a/articles/maximum-points-you-can-obtain-from-cards.md b/articles/maximum-points-you-can-obtain-from-cards.md new file mode 100644 index 000000000..8d1b9294a --- /dev/null +++ b/articles/maximum-points-you-can-obtain-from-cards.md @@ -0,0 +1,462 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def maxScore(self, cardPoints: List[int], k: int) -> int: + n = len(cardPoints) + res = 0 + + for left in range(k + 1): + leftSum = sum(cardPoints[:left]) + rightSum = sum(cardPoints[n - (k - left):]) + res = max(res, leftSum + rightSum) + + return res +``` + +```java +public class Solution { + public int maxScore(int[] cardPoints, int k) { + int n = cardPoints.length; + int res = 0; + + for (int left = 0; left <= k; left++) { + int leftSum = 0; + for (int i = 0; i < left; i++) { + leftSum += cardPoints[i]; + } + + int rightSum = 0; + for (int i = n - (k - left); i < n; i++) { + rightSum += cardPoints[i]; + } + + res = Math.max(res, leftSum + rightSum); + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int maxScore(vector& cardPoints, int k) { + int n = cardPoints.size(); + int res = 0; + + for (int left = 0; left <= k; left++) { + int leftSum = 0; + for (int i = 0; i < left; i++) { + leftSum += cardPoints[i]; + } + + int rightSum = 0; + for (int i = n - (k - left); i < n; i++) { + rightSum += cardPoints[i]; + } + + res = max(res, leftSum + rightSum); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} cardPoints + * @param {number} k + * @return {number} + */ + maxScore(cardPoints, k) { + let n = cardPoints.length; + let res = 0; + + for (let left = 0; left <= k; left++) { + let leftSum = 0; + for (let i = 0; i < left; i++) { + leftSum += cardPoints[i]; + } + + let rightSum = 0; + for (let i = n - (k - left); i < n; i++) { + rightSum += cardPoints[i]; + } + + res = Math.max(res, leftSum + rightSum); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(k ^ 2)$ +* Space complexity: $O(1)$ extra space. + +> Where $k$ is the number of cards to pick. + +--- + +## 2. Prefix & Suffix Sums + +::tabs-start + +```python +class Solution: + def maxScore(self, cardPoints: List[int], k: int) -> int: + n = len(cardPoints) + + prefix = [0] * (n + 1) + for i in range(n): + prefix[i + 1] = prefix[i] + cardPoints[i] + + suffix = [0] * (n + 1) + for i in range(n - 1, -1, -1): + suffix[i] = suffix[i + 1] + cardPoints[i] + + res = 0 + for left in range(k + 1): + right = k - left + res = max(res, prefix[left] + suffix[n - right]) + + return res +``` + +```java +public class Solution { + public int maxScore(int[] cardPoints, int k) { + int n = cardPoints.length; + + int[] prefix = new int[n + 1]; + for (int i = 0; i < n; i++) { + prefix[i + 1] = prefix[i] + cardPoints[i]; + } + + int[] suffix = new int[n + 1]; + for (int i = n - 1; i >= 0; i--) { + suffix[i] = suffix[i + 1] + cardPoints[i]; + } + + int res = 0; + for (int left = 0; left <= k; left++) { + int right = k - left; + res = Math.max(res, prefix[left] + suffix[n - right]); + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int maxScore(vector& cardPoints, int k) { + int n = cardPoints.size(); + + vector prefix(n + 1, 0); + for (int i = 0; i < n; i++) { + prefix[i + 1] = prefix[i] + cardPoints[i]; + } + + vector suffix(n + 1, 0); + for (int i = n - 1; i >= 0; i--) { + suffix[i] = suffix[i + 1] + cardPoints[i]; + } + + int res = 0; + for (int left = 0; left <= k; left++) { + int right = k - left; + res = max(res, prefix[left] + suffix[n - right]); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} cardPoints + * @param {number} k + * @return {number} + */ + maxScore(cardPoints, k) { + let n = cardPoints.length; + + let prefix = new Array(n + 1).fill(0); + for (let i = 0; i < n; i++) { + prefix[i + 1] = prefix[i] + cardPoints[i]; + } + + let suffix = new Array(n + 1).fill(0); + for (let i = n - 1; i >= 0; i--) { + suffix[i] = suffix[i + 1] + cardPoints[i]; + } + + let res = 0; + for (let left = 0; left <= k; left++) { + let right = k - left; + res = Math.max(res, prefix[left] + suffix[n - right]); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Sliding Window (Minimum Sum Window) + +::tabs-start + +```python +class Solution: + def maxScore(self, cardPoints: List[int], k: int) -> int: + n = len(cardPoints) + windowSize = n - k + + if windowSize == 0: + return sum(cardPoints) + + total = 0 + minWindowSum = float("inf") + curSum = 0 + + for i in range(n): + total += cardPoints[i] + curSum += cardPoints[i] + if i >= windowSize - 1: + minWindowSum = min(minWindowSum, curSum) + curSum -= cardPoints[i - windowSize + 1] + + return total - minWindowSum +``` + +```java +public class Solution { + public int maxScore(int[] cardPoints, int k) { + int n = cardPoints.length; + int windowSize = n - k; + + if (windowSize == 0) { + int sum = 0; + for (int num : cardPoints) sum += num; + return sum; + } + + int total = 0; + int minWindowSum = Integer.MAX_VALUE; + int curSum = 0; + + for (int i = 0; i < n; i++) { + total += cardPoints[i]; + curSum += cardPoints[i]; + if (i >= windowSize - 1) { + minWindowSum = Math.min(minWindowSum, curSum); + curSum -= cardPoints[i - windowSize + 1]; + } + } + + return total - minWindowSum; + } +} +``` + +```cpp +class Solution { +public: + int maxScore(vector& cardPoints, int k) { + int n = cardPoints.size(); + int windowSize = n - k; + + if (windowSize == 0) { + return accumulate(cardPoints.begin(), cardPoints.end(), 0); + } + + int total = 0; + int minWindowSum = INT_MAX; + int curSum = 0; + + for (int i = 0; i < n; i++) { + total += cardPoints[i]; + curSum += cardPoints[i]; + if (i >= windowSize - 1) { + minWindowSum = min(minWindowSum, curSum); + curSum -= cardPoints[i - windowSize + 1]; + } + } + + return total - minWindowSum; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} cardPoints + * @param {number} k + * @return {number} + */ + maxScore(cardPoints, k) { + let n = cardPoints.length; + let windowSize = n - k; + + if (windowSize === 0) { + return cardPoints.reduce((sum, num) => sum + num, 0); + } + + let total = 0; + let minWindowSum = Infinity; + let curSum = 0; + + for (let i = 0; i < n; i++) { + total += cardPoints[i]; + curSum += cardPoints[i]; + if (i >= windowSize - 1) { + minWindowSum = Math.min(minWindowSum, curSum); + curSum -= cardPoints[i - windowSize + 1]; + } + } + + return total - minWindowSum; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. + +--- + +## 4. Sliding Window + +::tabs-start + +```python +class Solution: + def maxScore(self, cardPoints: List[int], k: int) -> int: + l, r = 0, len(cardPoints) - k + total = sum(cardPoints[r:]) + res = total + + while r < len(cardPoints): + total += cardPoints[l] - cardPoints[r] + res = max(res, total) + l += 1 + r += 1 + + return res +``` + +```java +public class Solution { + public int maxScore(int[] cardPoints, int k) { + int l = 0, r = cardPoints.length - k; + int total = 0; + + for (int i = r; i < cardPoints.length; i++) { + total += cardPoints[i]; + } + + int res = total; + + while (r < cardPoints.length) { + total += cardPoints[l] - cardPoints[r]; + res = Math.max(res, total); + l++; + r++; + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int maxScore(vector& cardPoints, int k) { + int l = 0, r = cardPoints.size() - k; + int total = 0; + + for (int i = r; i < cardPoints.size(); i++) { + total += cardPoints[i]; + } + + int res = total; + + while (r < cardPoints.size()) { + total += cardPoints[l] - cardPoints[r]; + res = max(res, total); + l++; + r++; + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} cardPoints + * @param {number} k + * @return {number} + */ + maxScore(cardPoints, k) { + let l = 0, r = cardPoints.length - k; + let total = 0; + + for (let i = r; i < cardPoints.length; i++) { + total += cardPoints[i]; + } + + let res = total; + + while (r < cardPoints.length) { + total += cardPoints[l] - cardPoints[r]; + res = Math.max(res, total); + l++; + r++; + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(k)$ +* Space complexity: $O(1)$ extra space. + +> Where $k$ is the number of cards to pick. \ No newline at end of file diff --git a/articles/maximum-score-of-a-good-subarray.md b/articles/maximum-score-of-a-good-subarray.md new file mode 100644 index 000000000..f8b6fbdc0 --- /dev/null +++ b/articles/maximum-score-of-a-good-subarray.md @@ -0,0 +1,729 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def maximumScore(self, nums: List[int], k: int) -> int: + n, res = len(nums), 0 + + for i in range(k + 1): + minEle = nums[i] + for j in range(i, n): + minEle = min(minEle, nums[j]) + if j >= k: + res = max(res, minEle * (j - i + 1)) + + return res +``` + +```java +public class Solution { + public int maximumScore(int[] nums, int k) { + int n = nums.length, res = 0; + + for (int i = 0; i <= k; i++) { + int minEle = nums[i]; + for (int j = i; j < n; j++) { + minEle = Math.min(minEle, nums[j]); + if (j >= k) { + res = Math.max(res, minEle * (j - i + 1)); + } + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int maximumScore(vector& nums, int k) { + int n = nums.size(), res = 0; + + for (int i = 0; i <= k; i++) { + int minEle = nums[i]; + for (int j = i; j < n; j++) { + minEle = min(minEle, nums[j]); + if (j >= k) { + res = max(res, minEle * (j - i + 1)); + } + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {number} + */ + maximumScore(nums, k) { + let n = nums.length, res = 0; + + for (let i = 0; i <= k; i++) { + let minEle = nums[i]; + for (let j = i; j < n; j++) { + minEle = Math.min(minEle, nums[j]); + if (j >= k) { + res = Math.max(res, minEle * (j - i + 1)); + } + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(1)$ extra space. + +--- + +## 2. Binary Search + +::tabs-start + +```python +class Solution: + def maximumScore(self, nums: List[int], k: int) -> int: + n, res = len(nums), 0 + arr = nums[:] + + for i in range(k - 1, -1, -1): + arr[i] = min(arr[i], arr[i + 1]) + for i in range(k + 1, n): + arr[i] = min(arr[i], arr[i - 1]) + + left_arr = arr[:k+1] + right_arr = arr[k:] + + def find_right(target): + lo, hi = 0, len(right_arr) - 1 + pos = 0 + while lo <= hi: + mid = (lo + hi) // 2 + if right_arr[mid] >= target: + pos = mid + lo = mid + 1 + else: + hi = mid - 1 + return pos + + for minVal in set(arr): + l = bisect_left(left_arr, minVal) + r = find_right(minVal) + res = max(res, minVal * (k - l + 1 + r)) + return res +``` + +```java +public class Solution { + public int maximumScore(int[] nums, int k) { + int n = nums.length, res = 0; + int[] arr = Arrays.copyOf(nums, n); + Set candidates = new HashSet<>(); + candidates.add(arr[k]); + + for (int i = k - 1; i >= 0; i--) { + arr[i] = Math.min(arr[i], arr[i + 1]); + candidates.add(arr[i]); + } + for (int i = k + 1; i < n; i++) { + arr[i] = Math.min(arr[i], arr[i - 1]); + candidates.add(arr[i]); + } + + int[] leftArr = Arrays.copyOfRange(arr, 0, k + 1); + int[] rightArr = Arrays.copyOfRange(arr, k, n); + + for (int minVal : candidates) { + int l = findLeft(leftArr, minVal); + int r = findRight(rightArr, minVal); + res = Math.max(res, minVal * (k - l + 1 + r)); + } + return res; + } + + private int findLeft(int[] arr, int target) { + int lo = 0, hi = arr.length - 1; + while (lo <= hi) { + int mid = (lo + hi) / 2; + if (arr[mid] < target) { + lo = mid + 1; + } else { + hi = mid - 1; + } + } + return lo; + } + + private int findRight(int[] arr, int target) { + int lo = 0, hi = arr.length - 1, pos = 0; + while (lo <= hi) { + int mid = (lo + hi) / 2; + if (arr[mid] >= target) { + pos = mid; + lo = mid + 1; + } else { + hi = mid - 1; + } + } + return pos; + } +} +``` + +```cpp +class Solution { +public: + int maximumScore(vector& nums, int k) { + int n = nums.size(), res = 0; + vector arr = nums; + + for (int i = k - 1; i >= 0; i--) { + arr[i] = min(arr[i], arr[i + 1]); + } + for (int i = k + 1; i < n; i++) { + arr[i] = min(arr[i], arr[i - 1]); + } + + vector leftArr(arr.begin(), arr.begin() + k + 1); + vector rightArr(arr.begin() + k, arr.end()); + + set candidates(arr.begin(), arr.end()); + for (int minVal : candidates) { + int l = lower_bound(leftArr.begin(), leftArr.end(), minVal) - leftArr.begin(); + int r = findRight(rightArr, minVal); + res = max(res, minVal * (k - l + 1 + r)); + } + return res; + } + +private: + int findRight(vector& arr, int target) { + int lo = 0, hi = arr.size() - 1, pos = 0; + while (lo <= hi) { + int mid = (lo + hi) / 2; + if (arr[mid] >= target) { + pos = mid; + lo = mid + 1; + } else { + hi = mid - 1; + } + } + return pos; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {number} + */ + maximumScore(nums, k) { + let n = nums.length, res = 0; + let arr = [...nums]; + + for (let i = k - 1; i >= 0; i--) { + arr[i] = Math.min(arr[i], arr[i + 1]); + } + for (let i = k + 1; i < n; i++) { + arr[i] = Math.min(arr[i], arr[i - 1]); + } + + let leftArr = arr.slice(0, k + 1); + let rightArr = arr.slice(k); + + const findLeft = (target) => { + let lo = 0, hi = leftArr.length - 1; + while (lo <= hi) { + let mid = Math.floor((lo + hi) / 2); + if (leftArr[mid] < target) { + lo = mid + 1; + } else { + hi = mid - 1; + } + } + return lo; + }; + + const findRight = (target) => { + let lo = 0, hi = rightArr.length - 1, pos = 0; + while (lo <= hi) { + let mid = Math.floor((lo + hi) / 2); + if (rightArr[mid] >= target) { + pos = mid; + lo = mid + 1; + } else { + hi = mid - 1; + } + } + return pos; + }; + + let candidates = [...new Set(arr)]; + for (let minVal of candidates) { + let l = findLeft(minVal); + let r = findRight(minVal); + res = Math.max(res, minVal * (k - l + 1 + r)); + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Binary Search (Overwriting the Input) + +::tabs-start + +```python +class Solution: + def maximumScore(self, nums: List[int], k: int) -> int: + n, res = len(nums), 0 + + for i in range(k - 1, -1, -1): + nums[i] = min(nums[i], nums[i + 1]) + for i in range(k + 1, n): + nums[i] = min(nums[i], nums[i - 1]) + + def find_left(target): + lo, hi = 0, k + while lo <= hi: + mid = (lo + hi) // 2 + if nums[mid] < target: + lo = mid + 1 + else: + hi = mid - 1 + return lo + + def find_right(target): + lo, hi = k, n - 1 + while lo <= hi: + mid = (lo + hi) // 2 + if nums[mid] >= target: + lo = mid + 1 + else: + hi = mid - 1 + return hi + + for minVal in set(nums): + i = find_left(minVal) + j = find_right(minVal) + res = max(res, minVal * (j - i + 1)) + return res +``` + +```java +public class Solution { + public int maximumScore(int[] nums, int k) { + int n = nums.length, res = 0; + + for (int i = k - 1; i >= 0; i--) { + nums[i] = Math.min(nums[i], nums[i + 1]); + } + for (int i = k + 1; i < n; i++) { + nums[i] = Math.min(nums[i], nums[i - 1]); + } + + Set candidates = new TreeSet<>(); + for (int num : nums) { + candidates.add(num); + } + + for (int minVal : candidates) { + int i = findLeft(nums, k, minVal); + int j = findRight(nums, k, minVal); + res = Math.max(res, minVal * (j - i + 1)); + } + return res; + } + + int findLeft(int[] nums, int k, int target) { + int lo = 0, hi = k; + while (lo <= hi) { + int mid = (lo + hi) / 2; + if (nums[mid] < target) { + lo = mid + 1; + } else { + hi = mid - 1; + } + } + return lo; + } + + int findRight(int[] nums, int k, int target) { + int lo = k, hi = nums.length - 1; + while (lo <= hi) { + int mid = (lo + hi) / 2; + if (nums[mid] >= target) { + lo = mid + 1; + } else { + hi = mid - 1; + } + } + return hi; + } +} +``` + +```cpp +class Solution { +public: + int maximumScore(vector& nums, int k) { + int n = nums.size(), res = 0; + + for (int i = k - 1; i >= 0; i--) { + nums[i] = min(nums[i], nums[i + 1]); + } + for (int i = k + 1; i < n; i++) { + nums[i] = min(nums[i], nums[i - 1]); + } + + auto findLeft = [&](int target) { + int lo = 0, hi = k; + while (lo <= hi) { + int mid = (lo + hi) / 2; + if (nums[mid] < target) { + lo = mid + 1; + } else { + hi = mid - 1; + } + } + return lo; + }; + + auto findRight = [&](int target) { + int lo = k, hi = n - 1; + while (lo <= hi) { + int mid = (lo + hi) / 2; + if (nums[mid] >= target) { + lo = mid + 1; + } else { + hi = mid - 1; + } + } + return hi; + }; + + set candidates(nums.begin(), nums.end()); + for (int minVal : candidates) { + int i = findLeft(minVal); + int j = findRight(minVal); + res = max(res, minVal * (j - i + 1)); + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {number} + */ + maximumScore(nums, k) { + let n = nums.length, res = 0; + + for (let i = k - 1; i >= 0; i--) { + nums[i] = Math.min(nums[i], nums[i + 1]); + } + for (let i = k + 1; i < n; i++) { + nums[i] = Math.min(nums[i], nums[i - 1]); + } + + const findLeft = (target) => { + let lo = 0, hi = k; + while (lo <= hi) { + let mid = Math.floor((lo + hi) / 2); + if (nums[mid] < target) { + lo = mid + 1; + } else { + hi = mid - 1; + } + } + return lo; + }; + + const findRight = (target) => { + let lo = k, hi = n - 1; + while (lo <= hi) { + let mid = Math.floor((lo + hi) / 2); + if (nums[mid] >= target) { + lo = mid + 1; + } else { + hi = mid - 1; + } + } + return hi; + }; + + let candidates = new Set(nums); + for (let minVal of candidates) { + let i = findLeft(minVal); + let j = findRight(minVal); + res = Math.max(res, minVal * (j - i + 1)); + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ + +--- + +## 4. Monotonic Stack + +::tabs-start + +```python +class Solution: + def maximumScore(self, nums: List[int], k: int) -> int: + n, res = len(nums), 0 + stack = [] + + for i in range(n + 1): + while stack and (i == n or nums[stack[-1]] >= nums[i]): + mini = nums[stack.pop()] + j = stack[-1] if stack else -1 + if j < k < i: + res = max(res, mini * (i - j - 1)) + stack.append(i) + + return res +``` + +```java +public class Solution { + public int maximumScore(int[] nums, int k) { + int n = nums.length, res = 0; + Stack stack = new Stack<>(); + + for (int i = 0; i <= n; i++) { + while (!stack.isEmpty() && (i == n || nums[stack.peek()] >= nums[i])) { + int mini = nums[stack.pop()]; + int j = stack.isEmpty() ? -1 : stack.peek(); + if (j < k && k < i) { + res = Math.max(res, mini * (i - j - 1)); + } + } + stack.push(i); + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int maximumScore(vector& nums, int k) { + int n = nums.size(), res = 0; + stack stk; + + for (int i = 0; i <= n; i++) { + while (!stk.empty() && (i == n || nums[stk.top()] >= nums[i])) { + int mini = nums[stk.top()]; + stk.pop(); + int j = stk.empty() ? -1 : stk.top(); + if (j < k && k < i) { + res = max(res, mini * (i - j - 1)); + } + } + stk.push(i); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {number} + */ + maximumScore(nums, k) { + let n = nums.length, res = 0; + let stack = []; + + for (let i = 0; i <= n; i++) { + while (stack.length && (i === n || nums[stack[stack.length - 1]] >= nums[i])) { + let mini = nums[stack.pop()]; + let j = stack.length ? stack[stack.length - 1] : -1; + if (j < k && k < i) { + res = Math.max(res, mini * (i - j - 1)); + } + } + stack.push(i); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 5. Greedy + Two Pointers + +::tabs-start + +```python +class Solution: + def maximumScore(self, nums: List[int], k: int) -> int: + l = r = k + res = nums[k] + cur_min = nums[k] + + while l > 0 or r < len(nums) - 1: + left = nums[l - 1] if l > 0 else 0 + right = nums[r + 1] if r < len(nums) - 1 else 0 + + if left > right: + l -= 1 + cur_min = min(cur_min, left) + else: + r += 1 + cur_min = min(cur_min, right) + + res = max(res, cur_min * (r - l + 1)) + + return res +``` + +```java +public class Solution { + public int maximumScore(int[] nums, int k) { + int l = k, r = k; + int res = nums[k]; + int curMin = nums[k]; + int n = nums.length; + + while (l > 0 || r < n - 1) { + int left = (l > 0) ? nums[l - 1] : 0; + int right = (r < n - 1) ? nums[r + 1] : 0; + + if (left > right) { + l--; + curMin = Math.min(curMin, left); + } else { + r++; + curMin = Math.min(curMin, right); + } + + res = Math.max(res, curMin * (r - l + 1)); + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int maximumScore(vector& nums, int k) { + int l = k, r = k; + int res = nums[k]; + int curMin = nums[k]; + int n = nums.size(); + + while (l > 0 || r < n - 1) { + int left = (l > 0) ? nums[l - 1] : 0; + int right = (r < n - 1) ? nums[r + 1] : 0; + + if (left > right) { + l--; + curMin = min(curMin, left); + } else { + r++; + curMin = min(curMin, right); + } + + res = max(res, curMin * (r - l + 1)); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {number} + */ + maximumScore(nums, k) { + let l = k, r = k; + let res = nums[k]; + let curMin = nums[k]; + let n = nums.length; + + while (l > 0 || r < n - 1) { + let left = (l > 0) ? nums[l - 1] : 0; + let right = (r < n - 1) ? nums[r + 1] : 0; + + if (left > right) { + l--; + curMin = Math.min(curMin, left); + } else { + r++; + curMin = Math.min(curMin, right); + } + + res = Math.max(res, curMin * (r - l + 1)); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. \ No newline at end of file diff --git a/articles/maximum-score-words-formed-by-letters.md b/articles/maximum-score-words-formed-by-letters.md new file mode 100644 index 000000000..f9b510481 --- /dev/null +++ b/articles/maximum-score-words-formed-by-letters.md @@ -0,0 +1,676 @@ +## 1. Backtracking + +::tabs-start + +```python +class Solution: + def maxScoreWords(self, words: List[str], letters: List[str], score: List[int]) -> int: + def can_form_word(w, letter_cnt): + word_cnt = Counter(w) + for c in word_cnt: + if word_cnt[c] > letter_cnt[c]: + return False + return True + + def get_score(w): + res = 0 + for c in w: + res += score[ord(c) - ord('a')] + return res + + letter_cnt = Counter(letters) + + def backtrack(i): + if i == len(words): + return 0 + + res = backtrack(i + 1) # skip + if can_form_word(words[i], letter_cnt): # include (when possible) + for c in words[i]: + letter_cnt[c] -= 1 + res = max(res, get_score(words[i]) + backtrack(i + 1)) + for c in words[i]: + letter_cnt[c] += 1 + + return res + + return backtrack(0) +``` + +```java +public class Solution { + int[] letterCnt; + int[] score; + + public int maxScoreWords(String[] words, char[] letters, int[] score) { + this.score = score; + this.letterCnt = new int[26]; + + for (char c : letters) { + letterCnt[c - 'a']++; + } + + return backtrack(0, words); + } + + private int canFormWord(String word) { + int[] wordCnt = new int[26]; + for (char c : word.toCharArray()) { + wordCnt[c - 'a']++; + if (wordCnt[c - 'a'] > letterCnt[c - 'a']) { + return 0; + } + } + return 1; + } + + private int getScore(String word) { + int res = 0; + for (char c : word.toCharArray()) { + res += score[c - 'a']; + } + return res; + } + + private int backtrack(int i, String[] words) { + if (i == words.length) { + return 0; + } + + int res = backtrack(i + 1, words); // skip + if (canFormWord(words[i]) == 1) { // include (when possible) + for (char c : words[i].toCharArray()) { + letterCnt[c - 'a']--; + } + res = Math.max(res, getScore(words[i]) + backtrack(i + 1, words)); + for (char c : words[i].toCharArray()) { + letterCnt[c - 'a']++; + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector letterCnt = vector(26, 0); + vector score; + vector words; + + int maxScoreWords(vector& words, vector& letters, vector& score) { + this->words = words; + this->score = score; + + for (char c : letters) { + letterCnt[c - 'a']++; + } + + return backtrack(0); + } + + bool canFormWord(string& word) { + vector wordCnt(26, 0); + for (char c : word) { + wordCnt[c - 'a']++; + if (wordCnt[c - 'a'] > letterCnt[c - 'a']) { + return false; + } + } + return true; + } + + int getScore(string& word) { + int res = 0; + for (char c : word) { + res += score[c - 'a']; + } + return res; + } + + int backtrack(int i) { + if (i == words.size()) { + return 0; + } + + int res = backtrack(i + 1); // skip + if (canFormWord(words[i])) { // include (when possible) + for (char c : words[i]) { + letterCnt[c - 'a']--; + } + res = max(res, getScore(words[i]) + backtrack(i + 1)); + for (char c : words[i]) { + letterCnt[c - 'a']++; + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} words + * @param {string[]} letters + * @param {number[]} score + * @return {number} + */ + maxScoreWords(words, letters, score) { + const canFormWord = (w, letterCnt) => { + let wordCnt = new Array(26).fill(0); + for (let c of w) { + let idx = c.charCodeAt(0) - 'a'.charCodeAt(0); + wordCnt[idx]++; + if (wordCnt[idx] > letterCnt[idx]) { + return false; + } + } + return true; + }; + + const getScore = (w) => { + let res = 0; + for (let c of w) { + res += score[c.charCodeAt(0) - 'a'.charCodeAt(0)]; + } + return res; + }; + + let letterCnt = new Array(26).fill(0); + for (let c of letters) { + letterCnt[c.charCodeAt(0) - 'a'.charCodeAt(0)]++; + } + + const backtrack = (i) => { + if (i === words.length) { + return 0; + } + + let res = backtrack(i + 1); // skip + + if (canFormWord(words[i], letterCnt)) { // include (when possible) + for (let c of words[i]) { + letterCnt[c.charCodeAt(0) - 'a'.charCodeAt(0)]--; + } + res = Math.max(res, getScore(words[i]) + backtrack(i + 1)); + for (let c of words[i]) { + letterCnt[c.charCodeAt(0) - 'a'.charCodeAt(0)]++; + } + } + + return res; + }; + + return backtrack(0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(2 ^ n * (w + m) + N)$ +* Space complexity: $O(n + w)$ + +> Where $n$ is the number of words, $w$ is the maximum length of a word, $m$ is the size of the array $scores$, and $N$ is the size of the array $letters$. + +--- + +## 2. Bactracking + Precomputation + +::tabs-start + +```python +class Solution: + def maxScoreWords(self, words: List[str], letters: List[str], score: List[int]) -> int: + letter_cnt = [0] * 26 + for c in letters: + letter_cnt[ord(c) - ord('a')] += 1 + + n = len(words) + word_scores = [0] * n + word_freqs = [[0] * 26 for _ in range(n)] + + for i, word in enumerate(words): + for c in word: + idx = ord(c) - ord('a') + word_freqs[i][idx] += 1 + word_scores[i] += score[idx] + + def backtrack(i): + if i == n: + return 0 + + res = backtrack(i + 1) # skip + can_include = all(word_freqs[i][j] <= letter_cnt[j] for j in range(26)) + + if can_include: # include (when possible) + for j in range(26): + letter_cnt[j] -= word_freqs[i][j] + res = max(res, word_scores[i] + backtrack(i + 1)) + for j in range(26): + letter_cnt[j] += word_freqs[i][j] + + return res + + return backtrack(0) +``` + +```java +public class Solution { + private int[] letterCnt = new int[26]; + private int[] wordScores; + private int[][] wordFreqs; + private int n; + + public int maxScoreWords(String[] words, char[] letters, int[] score) { + Arrays.fill(letterCnt, 0); + for (char c : letters) { + letterCnt[c - 'a']++; + } + + n = words.length; + wordScores = new int[n]; + wordFreqs = new int[n][26]; + + for (int i = 0; i < n; i++) { + for (char c : words[i].toCharArray()) { + int idx = c - 'a'; + wordFreqs[i][idx]++; + wordScores[i] += score[idx]; + } + } + + return backtrack(0, words); + } + + private int backtrack(int i, String[] words) { + if (i == n) { + return 0; + } + + int res = backtrack(i + 1, words); // skip + boolean canInclude = true; + + for (int j = 0; j < 26; j++) { + if (wordFreqs[i][j] > letterCnt[j]) { + canInclude = false; + break; + } + } + + if (canInclude) { // include (when possible) + for (int j = 0; j < 26; j++) { + letterCnt[j] -= wordFreqs[i][j]; + } + res = Math.max(res, wordScores[i] + backtrack(i + 1, words)); + for (int j = 0; j < 26; j++) { + letterCnt[j] += wordFreqs[i][j]; + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector letterCnt = vector(26, 0); + vector wordScores; + vector> wordFreqs; + int n; + + int maxScoreWords(vector& words, vector& letters, vector& score) { + fill(letterCnt.begin(), letterCnt.end(), 0); + for (char c : letters) { + letterCnt[c - 'a']++; + } + + n = words.size(); + wordScores = vector(n, 0); + wordFreqs = vector>(n, vector(26, 0)); + + for (int i = 0; i < n; i++) { + for (char c : words[i]) { + int idx = c - 'a'; + wordFreqs[i][idx]++; + wordScores[i] += score[idx]; + } + } + + return backtrack(0, words); + } + +private: + int backtrack(int i, vector& words) { + if (i == n) { + return 0; + } + + int res = backtrack(i + 1, words); // skip + bool canInclude = true; + + for (int j = 0; j < 26; j++) { + if (wordFreqs[i][j] > letterCnt[j]) { + canInclude = false; + break; + } + } + + if (canInclude) { // include (when possible) + for (int j = 0; j < 26; j++) { + letterCnt[j] -= wordFreqs[i][j]; + } + res = max(res, wordScores[i] + backtrack(i + 1, words)); + for (int j = 0; j < 26; j++) { + letterCnt[j] += wordFreqs[i][j]; + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} words + * @param {string[]} letters + * @param {number[]} score + * @return {number} + */ + maxScoreWords(words, letters, score) { + let letterCnt = new Array(26).fill(0); + for (let c of letters) { + letterCnt[c.charCodeAt(0) - 'a'.charCodeAt(0)]++; + } + + let n = words.length; + let wordScores = new Array(n).fill(0); + let wordFreqs = Array.from({ length: n }, () => new Array(26).fill(0)); + + for (let i = 0; i < n; i++) { + for (let c of words[i]) { + let idx = c.charCodeAt(0) - 'a'.charCodeAt(0); + wordFreqs[i][idx]++; + wordScores[i] += score[idx]; + } + } + + const backtrack = (i) => { + if (i === n) { + return 0; + } + + let res = backtrack(i + 1); // skip + let canInclude = true; + + for (let j = 0; j < 26; j++) { + if (wordFreqs[i][j] > letterCnt[j]) { + canInclude = false; + break; + } + } + + if (canInclude) { // include (when possible) + for (let j = 0; j < 26; j++) { + letterCnt[j] -= wordFreqs[i][j]; + } + res = Math.max(res, wordScores[i] + backtrack(i + 1)); + for (let j = 0; j < 26; j++) { + letterCnt[j] += wordFreqs[i][j]; + } + } + + return res; + }; + + return backtrack(0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * 2 ^ n + N)$ +* Space complexity: $O(n + w)$ + +> Where $n$ is the number of words, $w$ is the maximum length of a word, $m$ is the size of the array $scores$, and $N$ is the size of the array $letters$. + +--- + +## 3. Backtracking (Bit Mask) + +::tabs-start + +```python +class Solution: + def maxScoreWords(self, words: List[str], letters: List[str], score: List[int]) -> int: + letter_cnt = [0] * 26 + for c in letters: + letter_cnt[ord(c) - ord('a')] += 1 + + n = len(words) + word_scores = [0] * n + word_freqs = [[0] * 26 for _ in range(n)] + + for i, word in enumerate(words): + for c in word: + idx = ord(c) - ord('a') + word_freqs[i][idx] += 1 + word_scores[i] += score[idx] + + res = 0 + + for mask in range(1 << n): + cur_score = 0 + cur_letter_cnt = letter_cnt[:] + valid = True + + for i in range(n): + if mask & (1 << i): + for j in range(26): + if word_freqs[i][j] > cur_letter_cnt[j]: + valid = False + break + if not valid: + break + + for j in range(26): + cur_letter_cnt[j] -= word_freqs[i][j] + + cur_score += word_scores[i] + + if valid: + res = max(res, cur_score) + + return res +``` + +```java +public class Solution { + public int maxScoreWords(String[] words, char[] letters, int[] score) { + int[] letterCnt = new int[26]; + for (char c : letters) { + letterCnt[c - 'a']++; + } + + int n = words.length; + int[] wordScores = new int[n]; + int[][] wordFreqs = new int[n][26]; + + for (int i = 0; i < n; i++) { + for (char c : words[i].toCharArray()) { + int idx = c - 'a'; + wordFreqs[i][idx]++; + wordScores[i] += score[idx]; + } + } + + int res = 0; + for (int mask = 0; mask < (1 << n); mask++) { + int curScore = 0; + int[] curLetterCnt = Arrays.copyOf(letterCnt, 26); + boolean valid = true; + + for (int i = 0; i < n; i++) { + if ((mask & (1 << i)) != 0) { + for (int j = 0; j < 26; j++) { + if (wordFreqs[i][j] > curLetterCnt[j]) { + valid = false; + break; + } + } + if (!valid) break; + + for (int j = 0; j < 26; j++) { + curLetterCnt[j] -= wordFreqs[i][j]; + } + + curScore += wordScores[i]; + } + } + + if (valid) { + res = Math.max(res, curScore); + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int maxScoreWords(vector& words, vector& letters, vector& score) { + vector letterCnt(26, 0); + for (char c : letters) { + letterCnt[c - 'a']++; + } + + int n = words.size(); + vector wordScores(n, 0); + vector> wordFreqs(n, vector(26, 0)); + + for (int i = 0; i < n; i++) { + for (char c : words[i]) { + int idx = c - 'a'; + wordFreqs[i][idx]++; + wordScores[i] += score[idx]; + } + } + + int res = 0; + for (int mask = 0; mask < (1 << n); mask++) { + int curScore = 0; + vector curLetterCnt = letterCnt; + bool valid = true; + + for (int i = 0; i < n; i++) { + if (mask & (1 << i)) { + for (int j = 0; j < 26; j++) { + if (wordFreqs[i][j] > curLetterCnt[j]) { + valid = false; + break; + } + } + if (!valid) break; + + for (int j = 0; j < 26; j++) { + curLetterCnt[j] -= wordFreqs[i][j]; + } + + curScore += wordScores[i]; + } + } + + if (valid) { + res = max(res, curScore); + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} words + * @param {string[]} letters + * @param {number[]} score + * @return {number} + */ + maxScoreWords(words, letters, score) { + let letterCnt = new Array(26).fill(0); + for (let c of letters) { + letterCnt[c.charCodeAt(0) - 'a'.charCodeAt(0)]++; + } + + let n = words.length; + let wordScores = new Array(n).fill(0); + let wordFreqs = Array.from({ length: n }, () => new Array(26).fill(0)); + + for (let i = 0; i < n; i++) { + for (let c of words[i]) { + let idx = c.charCodeAt(0) - 'a'.charCodeAt(0); + wordFreqs[i][idx]++; + wordScores[i] += score[idx]; + } + } + + let res = 0; + for (let mask = 0; mask < (1 << n); mask++) { + let curScore = 0; + let curLetterCnt = [...letterCnt]; + let valid = true; + + for (let i = 0; i < n; i++) { + if ((mask & (1 << i)) !== 0) { + for (let j = 0; j < 26; j++) { + if (wordFreqs[i][j] > curLetterCnt[j]) { + valid = false; + break; + } + } + if (!valid) break; + + for (let j = 0; j < 26; j++) { + curLetterCnt[j] -= wordFreqs[i][j]; + } + + curScore += wordScores[i]; + } + } + + if (valid) { + res = Math.max(res, curScore); + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n * 2 ^ n + N)$ +* Space complexity: $O(n + w)$ + +> Where $n$ is the number of words, $w$ is the maximum length of a word, $m$ is the size of the array $scores$, and $N$ is the size of the array $letters$. \ No newline at end of file diff --git a/articles/minimize-maximum-of-array.md b/articles/minimize-maximum-of-array.md new file mode 100644 index 000000000..b851e40b5 --- /dev/null +++ b/articles/minimize-maximum-of-array.md @@ -0,0 +1,210 @@ +## 1. Binary Search + +::tabs-start + +```python +class Solution: + def minimizeArrayValue(self, nums: List[int]) -> int: + def isValid(maxVal): + prefix_sum = 0 + for i in range(len(nums)): + prefix_sum += nums[i] + if prefix_sum > maxVal * (i + 1): + return False + return True + + left, right = 0, max(nums) + while left < right: + mid = left + (right - left) // 2 + if isValid(mid): + right = mid + else: + left = mid + 1 + + return left +``` + +```java +public class Solution { + public int minimizeArrayValue(int[] nums) { + int left = 0, right = 0; + for (int num : nums) { + right = Math.max(right, num); + } + + while (left < right) { + int mid = left + (right - left) / 2; + if (isValid(nums, mid)) { + right = mid; + } else { + left = mid + 1; + } + } + + return left; + } + + private boolean isValid(int[] nums, int maxVal) { + long prefixSum = 0; + for (int i = 0; i < nums.length; i++) { + prefixSum += nums[i]; + if (prefixSum > (long) maxVal * (i + 1)) { + return false; + } + } + return true; + } +} +``` + +```cpp +class Solution { +public: + int minimizeArrayValue(vector& nums) { + int left = 0, right = *max_element(nums.begin(), nums.end()); + + while (left < right) { + int mid = left + (right - left) / 2; + if (isValid(nums, mid)) { + right = mid; + } else { + left = mid + 1; + } + } + + return left; + } + +private: + bool isValid(vector& nums, int maxVal) { + long long prefixSum = 0; + for (int i = 0; i < nums.size(); i++) { + prefixSum += nums[i]; + if (prefixSum > (long long)maxVal * (i + 1)) { + return false; + } + } + return true; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + minimizeArrayValue(nums) { + const isValid = (maxVal) => { + let prefixSum = 0; + for (let i = 0; i < nums.length; i++) { + prefixSum += nums[i]; + if (prefixSum > maxVal * (i + 1)) { + return false; + } + } + return true; + }; + + let left = 0, right = Math.max(...nums); + while (left < right) { + let mid = left + Math.floor((right - left) / 2); + if (isValid(mid)) { + right = mid; + } else { + left = mid + 1; + } + } + + return left; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log m)$ +* Space complexity: $O(1)$ extra space. + +> Where $n$ is the size of the array $nums$ and $m$ is the maximum value in the array. + +--- + +## 2. Prefix Sum + Greedy + +::tabs-start + +```python +class Solution: + def minimizeArrayValue(self, nums: List[int]) -> int: + res = total = nums[0] + + for i in range(1, len(nums)): + total += nums[i] + res = max(res, math.ceil(total / (i + 1))) + + return res +``` + +```java +public class Solution { + public int minimizeArrayValue(int[] nums) { + int res = nums[0]; + long total = nums[0]; + + for (int i = 1; i < nums.length; i++) { + total += nums[i]; + res = Math.max(res, (int) Math.ceil((double) total / (i + 1))); + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int minimizeArrayValue(vector& nums) { + int res = nums[0]; + long long total = nums[0]; + + for (int i = 1; i < nums.size(); i++) { + total += nums[i]; + res = max(res, (int)ceil((double)total / (i + 1))); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + minimizeArrayValue(nums) { + let res = nums[0]; + let total = nums[0]; + + for (let i = 1; i < nums.length; i++) { + total += nums[i]; + res = Math.max(res, Math.ceil(total / (i + 1))); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. \ No newline at end of file diff --git a/articles/minimum-deletions-to-make-character-frequencies-unique.md b/articles/minimum-deletions-to-make-character-frequencies-unique.md new file mode 100644 index 000000000..60c9564fa --- /dev/null +++ b/articles/minimum-deletions-to-make-character-frequencies-unique.md @@ -0,0 +1,348 @@ +## 1. Hash Set + +::tabs-start + +```python +class Solution: + def minDeletions(self, s: str) -> int: + count = [0] * 26 + for c in s: + count[ord(c) - ord('a')] += 1 + + used_freq = set() + res = 0 + for freq in count: + while freq > 0 and freq in used_freq: + freq -= 1 + res += 1 + used_freq.add(freq) + + return res +``` + +```java +class Solution { + public int minDeletions(String s) { + int[] count = new int[26]; + for (int i = 0; i < s.length(); i++) { + count[s.charAt(i) - 'a']++; + } + + Set usedFreq = new HashSet<>(); + int res = 0; + + for (int freq : count) { + while (freq > 0 && usedFreq.contains(freq)) { + freq--; + res++; + } + usedFreq.add(freq); + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int minDeletions(string s) { + vector count(26, 0); + for (char& c : s) { + count[c - 'a']++; + } + + unordered_set usedFreq; + int res = 0; + + for (int& freq : count) { + while (freq > 0 && usedFreq.count(freq)) { + freq--; + res++; + } + usedFreq.insert(freq); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {number} + */ + minDeletions(s) { + let count = new Array(26).fill(0); + for (let c of s) { + count[c.charCodeAt(0) - 'a'.charCodeAt(0)]++; + } + + let usedFreq = new Set(); + let res = 0; + + for (let freq of count) { + while (freq > 0 && usedFreq.has(freq)) { + freq--; + res++; + } + usedFreq.add(freq); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n + m ^ 2)$ +* Space complexity: $O(m)$ + +> Where $n$ is the length of the string $s$ and $m$ is the total number of unique frequncies possible. + +--- + +## 2. Max-Heap + +::tabs-start + +```python +class Solution: + def minDeletions(self, s: str) -> int: + freq = Counter(s) + maxHeap = [-f for f in freq.values()] + heapq.heapify(maxHeap) + + res = 0 + while len(maxHeap) > 1: + top = -heapq.heappop(maxHeap) + if top == -maxHeap[0]: + if top - 1 > 0: + heapq.heappush(maxHeap, -(top - 1)) + res += 1 + + return res +``` + +```java +public class Solution { + public int minDeletions(String s) { + Map freq = new HashMap<>(); + for (int i = 0; i < s.length(); i++) { + char c = s.charAt(i); + freq.put(c, freq.getOrDefault(c, 0) + 1); + } + + PriorityQueue maxHeap = new PriorityQueue<>((a, b) -> b - a); + maxHeap.addAll(freq.values()); + + int res = 0; + while (maxHeap.size() > 1) { + int top = maxHeap.poll(); + if (top == maxHeap.peek()) { + if (top - 1 > 0) { + maxHeap.add(top - 1); + } + res++; + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int minDeletions(string s) { + unordered_map freq; + for (char& c : s) { + freq[c]++; + } + + priority_queue maxHeap; + for (auto& f : freq) { + maxHeap.push(f.second); + } + + int res = 0; + while (maxHeap.size() > 1) { + int top = maxHeap.top(); + maxHeap.pop(); + if (top == maxHeap.top()) { + if (top - 1 > 0) { + maxHeap.push(top - 1); + } + res++; + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {number} + */ + minDeletions(s) { + let freq = new Map(); + for (let c of s) { + freq.set(c, (freq.get(c) || 0) + 1); + } + + const maxHeap = new MaxPriorityQueue(); + for (let value of freq.values()) { + maxHeap.enqueue(value); + } + + let res = 0; + while (maxHeap.size() > 1) { + let top = maxHeap.dequeue().element; + if (maxHeap.front().element === top) { + if (top - 1 > 0) { + maxHeap.enqueue(top - 1); + } + res++; + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n + m ^ 2 \log m)$ +* Space complexity: $O(m)$ + +> Where $n$ is the length of the string $s$ and $m$ is the total number of unique frequncies possible. + +--- + +## 3. Sorting + +::tabs-start + +```python +class Solution: + def minDeletions(self, s: str) -> int: + count = [0] * 26 + for c in s: + count[ord(c) - ord('a')] += 1 + + count.sort(reverse=True) + res = 0 + maxAllowedFreq = count[0] + + for freq in count: + if freq > maxAllowedFreq: + res += freq - maxAllowedFreq + freq = maxAllowedFreq + maxAllowedFreq = max(0, freq - 1) + + return res +``` + +```java +public class Solution { + public int minDeletions(String s) { + int[] count = new int[26]; + for (int i = 0; i < s.length(); i++) { + count[s.charAt(i) - 'a']++; + } + + Arrays.sort(count); + int res = 0; + int maxAllowedFreq = count[25]; + + for (int i = 25; i >= 0; i--) { + if (count[i] > maxAllowedFreq) { + res += count[i] - maxAllowedFreq; + count[i] = maxAllowedFreq; + } + maxAllowedFreq = Math.max(0, count[i] - 1); + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int minDeletions(string s) { + vector count(26, 0); + for (char& c : s) { + count[c - 'a']++; + } + + sort(count.begin(), count.end(), greater()); + int res = 0; + int maxAllowedFreq = count[0]; + + for (int& freq : count) { + if (freq > maxAllowedFreq) { + res += freq - maxAllowedFreq; + freq = maxAllowedFreq; + } + maxAllowedFreq = max(0, freq - 1); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {number} + */ + minDeletions(s) { + let count = new Array(26).fill(0); + for (let c of s) { + count[c.charCodeAt(0) - 'a'.charCodeAt(0)]++; + } + + count.sort((a, b) => b - a); + + let res = 0; + let maxAllowedFreq = count[0]; + + for (let i = 0; i < 26; i++) { + if (count[i] > maxAllowedFreq) { + res += count[i] - maxAllowedFreq; + count[i] = maxAllowedFreq; + } + maxAllowedFreq = Math.max(0, count[i] - 1); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n + m \log m)$ +* Space complexity: $O(m)$ + +> Where $n$ is the length of the string $s$ and $m$ is the total number of unique frequncies possible. \ No newline at end of file diff --git a/articles/minimum-number-of-arrows-to-burst-balloons.md b/articles/minimum-number-of-arrows-to-burst-balloons.md new file mode 100644 index 000000000..195e1e3e0 --- /dev/null +++ b/articles/minimum-number-of-arrows-to-burst-balloons.md @@ -0,0 +1,183 @@ +## 1. Greedy (Sort By Start Value) + +::tabs-start + +```python +class Solution: + def findMinArrowShots(self, points: List[List[int]]) -> int: + points.sort() + res, prevEnd = len(points), points[0][1] + + for i in range(1, len(points)): + curr = points[i] + if curr[0] <= prevEnd: + res -= 1 + prevEnd = min(curr[1], prevEnd) + else: + prevEnd = curr[1] + + return res +``` + +```java +public class Solution { + public int findMinArrowShots(int[][] points) { + Arrays.sort(points, (a, b) -> Integer.compare(a[0], b[0])); + int res = points.length, prevEnd = points[0][1]; + + for (int i = 1; i < points.length; i++) { + int[] curr = points[i]; + if (curr[0] <= prevEnd) { + res--; + prevEnd = Math.min(curr[1], prevEnd); + } else { + prevEnd = curr[1]; + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int findMinArrowShots(vector>& points) { + sort(points.begin(), points.end()); + int res = points.size(), prevEnd = points[0][1]; + + for (int i = 1; i < points.size(); i++) { + vector& curr = points[i]; + if (curr[0] <= prevEnd) { + res--; + prevEnd = min(curr[1], prevEnd); + } else { + prevEnd = curr[1]; + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} points + * @return {number} + */ + findMinArrowShots(points) { + points.sort((a, b) => a[0] - b[0]); + let res = points.length, prevEnd = points[0][1]; + + for (let i = 1; i < points.length; i++) { + let curr = points[i]; + if (curr[0] <= prevEnd) { + res--; + prevEnd = Math.min(curr[1], prevEnd); + } else { + prevEnd = curr[1]; + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(1)$ or $O(n)$ depending on the sorting algorithm. + +--- + +## 2. Greedy (Sort By End Value) + +::tabs-start + +```python +class Solution: + def findMinArrowShots(self, points: List[List[int]]) -> int: + points.sort(key=lambda x: x[1]) + res, prevEnd = 1, points[0][1] + + for i in range(1, len(points)): + if points[i][0] > prevEnd: + prevEnd = points[i][1] + res += 1 + + return res +``` + +```java +public class Solution { + public int findMinArrowShots(int[][] points) { + Arrays.sort(points, (a, b) -> Integer.compare(a[1], b[1])); + int res = 1, prevEnd = points[0][1]; + + for (int i = 1; i < points.length; i++) { + if (points[i][0] > prevEnd) { + prevEnd = points[i][1]; + res++; + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int findMinArrowShots(vector>& points) { + sort(points.begin(), points.end(), [](const auto& a, const auto& b) { + return a[1] < b[1]; + }); + int res = 1, prevEnd = points[0][1]; + + for (int i = 1; i < points.size(); i++) { + if (points[i][0] > prevEnd) { + prevEnd = points[i][1]; + res++; + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} points + * @return {number} + */ + findMinArrowShots(points) { + points.sort((a, b) => a[1] - b[1]); + let res = 1, prevEnd = points[0][1]; + + for (let i = 1; i < points.length; i++) { + if (points[i][0] > prevEnd) { + prevEnd = points[i][1]; + res++; + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(1)$ or $O(n)$ depending on the sorting algorithm. \ No newline at end of file diff --git a/articles/minimum-number-of-days-to-eat-n-oranges.md b/articles/minimum-number-of-days-to-eat-n-oranges.md new file mode 100644 index 000000000..115da181e --- /dev/null +++ b/articles/minimum-number-of-days-to-eat-n-oranges.md @@ -0,0 +1,354 @@ +## 1. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def minDays(self, n: int) -> int: + dp = {} + + def dfs(n): + if n == 0: + return 0 + if n in dp: + return dp[n] + + res = 1 + dfs(n - 1) + if n % 3 == 0: + res = min(res, 1 + dfs(n // 3)) + if n % 2 == 0: + res = min(res, 1 + dfs(n // 2)) + + dp[n] = res + return res + + return dfs(n) +``` + +```java +public class Solution { + private Map dp = new HashMap<>(); + + public int minDays(int n) { + return dfs(n); + } + + private int dfs(int n) { + if (n == 0) return 0; + if (dp.containsKey(n)) return dp.get(n); + + int res = 1 + dfs(n - 1); + if (n % 3 == 0) res = Math.min(res, 1 + dfs(n / 3)); + if (n % 2 == 0) res = Math.min(res, 1 + dfs(n / 2)); + + dp.put(n, res); + return res; + } +} +``` + +```cpp +class Solution { +public: + unordered_map dp; + + int minDays(int n) { + return dfs(n); + } + + int dfs(int n) { + if (n == 0) return 0; + if (dp.count(n)) return dp[n]; + + int res = 1 + dfs(n - 1); + if (n % 3 == 0) res = min(res, 1 + dfs(n / 3)); + if (n % 2 == 0) res = min(res, 1 + dfs(n / 2)); + + return dp[n] = res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {number} + */ + minDays(n) { + const dp = new Map(); + + const dfs = (n) => { + if (n === 0) return 0; + if (dp.has(n)) return dp.get(n); + + let res = 1 + dfs(n - 1); + if (n % 3 === 0) res = Math.min(res, 1 + dfs(n / 3)); + if (n % 2 === 0) res = Math.min(res, 1 + dfs(n / 2)); + + dp.set(n, res); + return res; + }; + + return dfs(n); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Greedy + Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def minDays(self, n: int) -> int: + dp = {0: 0, 1: 1} + + def dfs(n): + if n in dp: + return dp[n] + + res = 1 + (n % 2) + dfs(n // 2) + res = min(res, 1 + (n % 3) + dfs(n // 3)) + dp[n] = res + return res + + return dfs(n) +``` + +```java +public class Solution { + private Map dp = new HashMap<>(); + + public int minDays(int n) { + dp.put(0, 0); + dp.put(1, 1); + return dfs(n); + } + + private int dfs(int n) { + if (dp.containsKey(n)) return dp.get(n); + + int res = 1 + (n % 2) + dfs(n / 2); + res = Math.min(res, 1 + (n % 3) + dfs(n / 3)); + + dp.put(n, res); + return res; + } +} +``` + +```cpp +class Solution { +public: + unordered_map dp; + + int minDays(int n) { + dp[0] = 0; + dp[1] = 1; + return dfs(n); + } + +private: + int dfs(int n) { + if (dp.count(n)) return dp[n]; + + int res = 1 + (n % 2) + dfs(n / 2); + res = min(res, 1 + (n % 3) + dfs(n / 3)); + + return dp[n] = res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {number} + */ + minDays(n) { + const dp = new Map(); + dp.set(0, 0); + dp.set(1, 1); + + const dfs = (n) => { + if (dp.has(n)) return dp.get(n); + + let res = 1 + (n % 2) + dfs(Math.floor(n / 2)); + res = Math.min(res, 1 + (n % 3) + dfs(Math.floor(n / 3))); + + dp.set(n, res); + return res; + }; + + return dfs(n); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(\log n)$ +* Space complexity: $O(\log n)$ + +--- + +## 3. Breadth First Search + +::tabs-start + +```python +class Solution: + def minDays(self, n: int) -> int: + q = deque([n]) + visit = set() + res = 0 + + while q: + res += 1 + for _ in range(len(q)): + node = q.popleft() + nei = node - 1 + if nei == 0: + return res + if nei not in visit: + visit.add(nei) + q.append(nei) + for d in range(2, 4): + if node % d == 0: + nei = node // d + if nei == 0: + return res + if nei not in visit: + visit.add(nei) + q.append(nei) + return res +``` + +```java +public class Solution { + public int minDays(int n) { + Queue q = new LinkedList<>(); + Set visit = new HashSet<>(); + q.offer(n); + int res = 0; + + while (!q.isEmpty()) { + res++; + for (int i = q.size(); i > 0; i--) { + int node = q.poll(); + int nei = node - 1; + if (nei == 0) return res; + if (!visit.contains(nei)) { + visit.add(nei); + q.offer(nei); + } + for (int d = 2; d <= 3; d++) { + if (node % d == 0) { + nei = node / d; + if (nei == 0) return res; + if (!visit.contains(nei)) { + visit.add(nei); + q.offer(nei); + } + } + } + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int minDays(int n) { + queue q; + unordered_set visit; + q.push(n); + int res = 0; + + while (!q.empty()) { + res++; + for (int i = q.size(); i > 0; i--) { + int node = q.front(); q.pop(); + int nei = node - 1; + if (nei == 0) return res; + if (visit.find(nei) == visit.end()) { + visit.insert(nei); + q.push(nei); + } + for (int d = 2; d <= 3; d++) { + if (node % d == 0) { + nei = node / d; + if (nei == 0) return res; + if (visit.find(nei) == visit.end()) { + visit.insert(nei); + q.push(nei); + } + } + } + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {number} + */ + minDays(n) { + const q = new Queue([n]); + const visit = new Set(); + let res = 0; + + while (!q.isEmpty()) { + res++; + for (let i = q.size(); i > 0; i--) { + let node = q.pop(); + let nei = node - 1; + if (nei === 0) return res; + if (!visit.has(nei)) { + visit.add(nei); + q.push(nei); + } + for (let d = 2; d <= 3; d++) { + if (node % d === 0) { + nei = Math.floor(node / d); + if (nei === 0) return res; + if (!visit.has(nei)) { + visit.add(nei); + q.push(nei); + } + } + } + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(\log n)$ +* Space complexity: $O(\log n)$ \ No newline at end of file diff --git a/articles/minimum-number-of-vertices-to-reach-all-nodes.md b/articles/minimum-number-of-vertices-to-reach-all-nodes.md new file mode 100644 index 000000000..aa09f1c80 --- /dev/null +++ b/articles/minimum-number-of-vertices-to-reach-all-nodes.md @@ -0,0 +1,465 @@ +## 1. Depth First Search + +::tabs-start + +```python +class Solution: + def findSmallestSetOfVertices(self, n: int, edges: List[List[int]]) -> List[int]: + adj = [[] for _ in range(n)] + for u, v in edges: + adj[u].append(v) + + res = set(range(n)) + visited = [False] * n + + def dfs(node): + visited[node] = True + for nei in adj[node]: + if not visited[nei]: + dfs(nei) + res.discard(nei) + + for i in range(n): + if not visited[i]: + dfs(i) + return list(res) +``` + +```java +public class Solution { + public List findSmallestSetOfVertices(int n, List> edges) { + List[] adj = new ArrayList[n]; + for (int i = 0; i < n; i++) adj[i] = new ArrayList<>(); + for (List edge : edges) { + adj[edge.get(0)].add(edge.get(1)); + } + + Set res = new HashSet<>(); + for (int i = 0; i < n; i++) { + res.add(i); + } + + boolean[] visited = new boolean[n]; + for (int i = 0; i < n; i++) { + dfs(i, adj, visited, res); + } + return new ArrayList<>(res); + } + + private void dfs(int node, List[] adj, boolean[] visited, Set res) { + visited[node] = true; + for (int nei : adj[node]) { + if (!visited[nei]) dfs(nei, adj, visited, res); + res.remove(nei); + } + } +} +``` + +```cpp +class Solution { +public: + vector findSmallestSetOfVertices(int n, vector>& edges) { + vector> adj(n); + for (auto& edge : edges) { + adj[edge[0]].push_back(edge[1]); + } + + unordered_set res; + vector visited(n, false); + for (int i = 0; i < n; i++) res.insert(i); + + function dfs = [&](int node) { + visited[node] = true; + for (int& nei : adj[node]) { + if (!visited[nei]) dfs(nei); + res.erase(nei); + } + }; + + for (int i = 0; i < n; i++) { + if (!visited[i]) dfs(i); + } + return vector(res.begin(), res.end()); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number[][]} edges + * @return {number[]} + */ + findSmallestSetOfVertices(n, edges) { + const adj = Array.from({ length: n }, () => []); + for (const [u, v] of edges) { + adj[u].push(v); + } + + const res = new Set(Array.from({ length: n }, (_, i) => i)); + const visited = new Array(n).fill(false); + + const dfs = (node) => { + visited[node] = true; + for (const nei of adj[node]) { + if (!visited[nei]) dfs(nei); + res.delete(nei); + } + }; + + for (let i = 0; i < n; i++) { + if (!visited[i]) dfs(i); + } + + return Array.from(res); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(V + E)$ +* Space complexity: $O(V + E)$ + +> Where $V$ is the number of vertices and $E$ is the number of edges. + +--- + +## 2. Iterative DFS + +::tabs-start + +```python +class Solution: + def findSmallestSetOfVertices(self, n: int, edges: List[List[int]]) -> List[int]: + adj = [[] for _ in range(n)] + for u, v in edges: + adj[u].append(v) + + res = [True] * n + visited = [False] * n + stack = [] + + for i in range(n): + if not visited[i]: + stack.append(i) + while stack: + node = stack.pop() + if visited[node]: + continue + visited[node] = True + for nei in adj[node]: + if not visited[nei]: + stack.append(nei) + res[nei] = False + + return [i for i in range(n) if res[i]] +``` + +```java +public class Solution { + public List findSmallestSetOfVertices(int n, List> edges) { + List[] adj = new ArrayList[n]; + for (int i = 0; i < n; i++) { + adj[i] = new ArrayList<>(); + } + for (List edge : edges) { + adj[edge.get(0)].add(edge.get(1)); + } + + boolean[] res = new boolean[n]; + Arrays.fill(res, true); + boolean[] visited = new boolean[n]; + Stack stack = new Stack<>(); + + for (int i = 0; i < n; i++) { + if (!visited[i]) { + stack.push(i); + while (!stack.isEmpty()) { + int node = stack.pop(); + if (visited[node]) continue; + visited[node] = true; + for (int nei : adj[node]) { + if (!visited[nei]) stack.push(nei); + res[nei] = false; + } + } + } + } + + List result = new ArrayList<>(); + for (int i = 0; i < n; i++) { + if (res[i]) result.add(i); + } + return result; + } +} +``` + +```cpp +class Solution { +public: + vector findSmallestSetOfVertices(int n, vector>& edges) { + vector> adj(n); + for (auto& edge : edges) { + adj[edge[0]].push_back(edge[1]); + } + + vector res(n, true), visited(n, false); + stack stack; + + for (int i = 0; i < n; i++) { + if (!visited[i]) { + stack.push(i); + while (!stack.empty()) { + int node = stack.top(); + stack.pop(); + if (visited[node]) continue; + visited[node] = true; + for (int nei : adj[node]) { + if (!visited[nei]) stack.push(nei); + res[nei] = false; + } + } + } + } + + vector result; + for (int i = 0; i < n; i++) { + if (res[i]) result.push_back(i); + } + return result; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number[][]} edges + * @return {number[]} + */ + findSmallestSetOfVertices(n, edges) { + const adj = Array.from({ length: n }, () => []); + for (const [u, v] of edges) { + adj[u].push(v); + } + + const res = Array(n).fill(true); + const visited = Array(n).fill(false); + const stack = []; + + for (let i = 0; i < n; i++) { + if (!visited[i]) { + stack.push(i); + while (stack.length) { + const node = stack.pop(); + if (visited[node]) continue; + visited[node] = true; + for (const nei of adj[node]) { + if (!visited[nei]) stack.push(nei); + res[nei] = false; + } + } + } + } + + return res.map((val, i) => val ? i : -1).filter(i => i !== -1); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(V + E)$ +* Space complexity: $O(V + E)$ + +> Where $V$ is the number of vertices and $E$ is the number of edges. + +--- + +## 3. Indegree Count + +::tabs-start + +```python +class Solution: + def findSmallestSetOfVertices(self, n: int, edges: List[List[int]]) -> List[int]: + incoming = collections.defaultdict(list) + for src, dst in edges: + incoming[dst].append(src) + + res = [] + for i in range(n): + if not incoming[i]: + res.append(i) + return res +``` + +```java +public class Solution { + public List findSmallestSetOfVertices(int n, List> edges) { + List[] incoming = new ArrayList[n]; + for (int i = 0; i < n; i++) { + incoming[i] = new ArrayList<>(); + } + for (List edge : edges) { + incoming[edge.get(1)].add(edge.get(0)); + } + + List res = new ArrayList<>(); + for (int i = 0; i < n; i++) { + if (incoming[i].isEmpty()) { + res.add(i); + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + vector findSmallestSetOfVertices(int n, vector>& edges) { + vector> incoming(n); + for (auto& edge : edges) { + incoming[edge[1]].push_back(edge[0]); + } + + vector res; + for (int i = 0; i < n; i++) { + if (incoming[i].empty()) { + res.push_back(i); + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number[][]} edges + * @return {number[]} + */ + findSmallestSetOfVertices(n, edges) { + const incoming = Array.from({ length: n }, () => []); + + for (const [src, dst] of edges) { + incoming[dst].push(src); + } + + const res = []; + for (let i = 0; i < n; i++) { + if (incoming[i].length === 0) { + res.push(i); + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(V + E)$ +* Space complexity: $O(V)$ + +> Where $V$ is the number of vertices and $E$ is the number of edges. + +--- + +## 4. Indegree Count + +::tabs-start + +```python +class Solution: + def findSmallestSetOfVertices(self, n: int, edges: List[List[int]]) -> List[int]: + indegree = [False] * n + for src, dst in edges: + indegree[dst] = True + return [i for i in range(n) if not indegree[i]] +``` + +```java +public class Solution { + public List findSmallestSetOfVertices(int n, List> edges) { + boolean[] indegree = new boolean[n]; + for (List edge : edges) { + indegree[edge.get(1)] = true; + } + + List res = new ArrayList<>(); + for (int i = 0; i < n; i++) { + if (!indegree[i]) { + res.add(i); + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + vector findSmallestSetOfVertices(int n, vector>& edges) { + vector indegree(n, false); + for (const auto& edge : edges) { + indegree[edge[1]] = true; + } + + vector res; + for (int i = 0; i < n; i++) { + if (!indegree[i]) { + res.push_back(i); + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number[][]} edges + * @return {number[]} + */ + findSmallestSetOfVertices(n, edges) { + const indegree = new Array(n).fill(false); + for (const [src, dst] of edges) { + indegree[dst] = true; + } + + let res = []; + for (let i = 0; i < n; i++) { + if (!indegree[i]) res.push(i); + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(V + E)$ +* Space complexity: $O(V)$ + +> Where $V$ is the number of vertices and $E$ is the number of edges. \ No newline at end of file diff --git a/articles/minimum-one-bit-operations-to-make-integers-zero.md b/articles/minimum-one-bit-operations-to-make-integers-zero.md new file mode 100644 index 000000000..7e4071ce6 --- /dev/null +++ b/articles/minimum-one-bit-operations-to-make-integers-zero.md @@ -0,0 +1,313 @@ +## 1. Math (Recursion) + +::tabs-start + +```python +class Solution: + def minimumOneBitOperations(self, n: int) -> int: + if n == 0: + return 0 + + k = 1 + while (k << 1) <= n: + k <<= 1 + + return (k << 1) - 1 - self.minimumOneBitOperations(k ^ n) +``` + +```java +public class Solution { + public int minimumOneBitOperations(int n) { + if (n == 0) { + return 0; + } + + int k = 1; + while ((k << 1) <= n) { + k <<= 1; + } + + return (k << 1) - 1 - minimumOneBitOperations(k ^ n); + } +} +``` + +```cpp +class Solution { +public: + int minimumOneBitOperations(int n) { + if (n == 0) { + return 0; + } + + int k = 1; + while ((k << 1) <= n) { + k <<= 1; + } + + return (k << 1) - 1 - minimumOneBitOperations(k ^ n); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {number} + */ + minimumOneBitOperations(n) { + if (n === 0) { + return 0; + } + + let k = 1; + while ((k << 1) <= n) { + k <<= 1; + } + + return (k << 1) - 1 - this.minimumOneBitOperations(k ^ n); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(\log n)$ +* Space complexity: $O(\log n)$ for recursion stack. + +--- + +## 2. Math (Iteration) - I + +::tabs-start + +```python +class Solution: + def minimumOneBitOperations(self, n: int) -> int: + res = 0 + k = 1 << 30 + sign = 1 + + while n: + while k > n: + k >>= 1 + + res += (sign * ((k << 1) - 1)) + sign *= -1 + n ^= k + + return res +``` + +```java +public class Solution { + public int minimumOneBitOperations(int n) { + int res = 0, k = 1 << 30, sign = 1; + + while (n != 0) { + while (k > n) { + k >>= 1; + } + + res += (sign * ((k << 1) - 1)); + sign *= -1; + n ^= k; + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int minimumOneBitOperations(int n) { + int res = 0, k = 1 << 30, sign = 1; + + while (n != 0) { + while (k > n) { + k >>= 1; + } + + res += sign * ((k << 1) - 1); + sign *= -1; + n ^= k; + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {number} + */ + minimumOneBitOperations(n) { + let res = 0, k = 1 << 30, sign = 1; + + while (n !== 0) { + while (k > n) { + k >>= 1; + } + + res += sign * ((k << 1) - 1); + sign *= -1; + n ^= k; + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(\log n)$ +* Space complexity: $O(1)$ + +--- + +## 3. Math (Iteration) - II + +::tabs-start + +```python +class Solution: + def minimumOneBitOperations(self, n: int) -> int: + res, sign = 0, 1 + while n: + res += sign * (n ^ (n - 1)) + n &= (n - 1) + sign *= -1 + return abs(res) +``` + +```java +public class Solution { + public int minimumOneBitOperations(int n) { + int res = 0, sign = 1; + while (n != 0) { + res += sign * (n ^ (n - 1)); + n &= (n - 1); + sign *= -1; + } + return Math.abs(res); + } +} +``` + +```cpp +class Solution { +public: + int minimumOneBitOperations(int n) { + int res = 0, sign = 1; + while (n != 0) { + res += sign * (n ^ (n - 1)); + n &= (n - 1); + sign *= -1; + } + return abs(res); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {number} + */ + minimumOneBitOperations(n) { + let res = 0, sign = 1; + while (n !== 0) { + res += sign * (n ^ (n - 1)); + n &= (n - 1); + sign *= -1; + } + return Math.abs(res); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(\log n)$ +* Space complexity: $O(1)$ + +--- + +## 4. Math (Grey Code) + +::tabs-start + +```python +class Solution: + def minimumOneBitOperations(self, n: int) -> int: + res = n + while n: + n >>= 1 + res ^= n + return res +``` + +```java +public class Solution { + public int minimumOneBitOperations(int n) { + int res = n; + while (n != 0) { + n >>= 1; + res ^= n; + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int minimumOneBitOperations(int n) { + int res = n; + while (n != 0) { + n >>= 1; + res ^= n; + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {number} + */ + minimumOneBitOperations(n) { + let res = n; + while (n !== 0) { + n >>= 1; + res ^= n; + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(\log n)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/number-of-good-paths.md b/articles/number-of-good-paths.md new file mode 100644 index 000000000..f0e16529f --- /dev/null +++ b/articles/number-of-good-paths.md @@ -0,0 +1,861 @@ +## 1. Brute Force (DFS) + +::tabs-start + +```python +class Solution: + def numberOfGoodPaths(self, vals: List[int], edges: List[List[int]]) -> int: + n = len(vals) + adj = [[] for _ in range(n)] + for u, v in edges: + adj[u].append(v) + adj[v].append(u) + + def dfs(node, startNode, parent): + if vals[node] > vals[startNode]: + return 0 + + res = 0 + if vals[node] == vals[startNode] and node >= startNode: + res += 1 + + for child in adj[node]: + if child == parent: + continue + res += dfs(child, startNode, node) + + return res + + + res = 0 + for node in range(n): + res += dfs(node, node, -1) + return res +``` + +```java +public class Solution { + public int numberOfGoodPaths(int[] vals, int[][] edges) { + int n = vals.length; + List[] adj = new ArrayList[n]; + for (int i = 0; i < n; i++) { + adj[i] = new ArrayList<>(); + } + for (int[] edge : edges) { + adj[edge[0]].add(edge[1]); + adj[edge[1]].add(edge[0]); + } + + int res = 0; + for (int node = 0; node < n; node++) { + res += dfs(node, node, -1, vals, adj); + } + return res; + } + + private int dfs(int node, int startNode, int parent, int[] vals, List[] adj) { + if (vals[node] > vals[startNode]) { + return 0; + } + + int res = 0; + if (vals[node] == vals[startNode] && node >= startNode) { + res += 1; + } + + for (int child : adj[node]) { + if (child == parent) { + continue; + } + res += dfs(child, startNode, node, vals, adj); + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int numberOfGoodPaths(vector& vals, vector>& edges) { + int n = vals.size(); + vector> adj(n); + for (auto& edge : edges) { + adj[edge[0]].push_back(edge[1]); + adj[edge[1]].push_back(edge[0]); + } + + int res = 0; + for (int node = 0; node < n; node++) { + res += dfs(node, node, -1, vals, adj); + } + return res; + } + +private: + int dfs(int node, int startNode, int parent, vector& vals, vector>& adj) { + if (vals[node] > vals[startNode]) { + return 0; + } + + int res = 0; + if (vals[node] == vals[startNode] && node >= startNode) { + res += 1; + } + + for (int child : adj[node]) { + if (child == parent) { + continue; + } + res += dfs(child, startNode, node, vals, adj); + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} vals + * @param {number[][]} edges + * @return {number} + */ + numberOfGoodPaths(vals, edges) { + const n = vals.length; + const adj = Array.from({ length: n }, () => []); + + for (const [u, v] of edges) { + adj[u].push(v); + adj[v].push(u); + } + + const dfs = (node, startNode, parent) => { + if (vals[node] > vals[startNode]) { + return 0; + } + + let res = 0; + if (vals[node] === vals[startNode] && node >= startNode) { + res += 1; + } + + for (const child of adj[node]) { + if (child === parent) continue; + res += dfs(child, startNode, node); + } + return res; + }; + + let res = 0; + for (let node = 0; node < n; node++) { + res += dfs(node, node, -1); + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n)$ for recursion stack. + +--- + +## 2. Brute Force (BFS) + +::tabs-start + +```python +class Solution: + def numberOfGoodPaths(self, vals, edges): + n = len(vals) + adj = [[] for _ in range(n)] + for u, v in edges: + adj[u].append(v) + adj[v].append(u) + + res = 0 + for startNode in range(n): + q = deque([startNode]) + visited = set([startNode]) + count = 0 + + while q: + node = q.popleft() + if vals[node] == vals[startNode] and node >= startNode: + count += 1 + + for child in adj[node]: + if child not in visited and vals[child] <= vals[startNode]: + visited.add(child) + q.append(child) + + res += count + + return res +``` + +```java +public class Solution { + public int numberOfGoodPaths(int[] vals, int[][] edges) { + int n = vals.length; + List[] adj = new ArrayList[n]; + for (int i = 0; i < n; i++) { + adj[i] = new ArrayList<>(); + } + for (int[] edge : edges) { + adj[edge[0]].add(edge[1]); + adj[edge[1]].add(edge[0]); + } + + int res = 0; + for (int startNode = 0; startNode < n; startNode++) { + Queue q = new LinkedList<>(); + Set visited = new HashSet<>(); + q.offer(startNode); + visited.add(startNode); + int count = 0; + + while (!q.isEmpty()) { + int node = q.poll(); + if (vals[node] == vals[startNode] && node >= startNode) { + count++; + } + + for (int child : adj[node]) { + if (!visited.contains(child) && vals[child] <= vals[startNode]) { + visited.add(child); + q.offer(child); + } + } + } + + res += count; + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int numberOfGoodPaths(vector& vals, vector>& edges) { + int n = vals.size(); + vector> adj(n); + for (auto& edge : edges) { + adj[edge[0]].push_back(edge[1]); + adj[edge[1]].push_back(edge[0]); + } + + int res = 0; + for (int startNode = 0; startNode < n; startNode++) { + queue q; + unordered_set visited; + q.push(startNode); + visited.insert(startNode); + int count = 0; + + while (!q.empty()) { + int node = q.front(); + q.pop(); + if (vals[node] == vals[startNode] && node >= startNode) { + count++; + } + + for (int child : adj[node]) { + if (visited.find(child) == visited.end() && vals[child] <= vals[startNode]) { + visited.insert(child); + q.push(child); + } + } + } + + res += count; + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} vals + * @param {number[][]} edges + * @return {number} + */ + numberOfGoodPaths(vals, edges) { + const n = vals.length; + const adj = Array.from({ length: n }, () => []); + + for (const [u, v] of edges) { + adj[u].push(v); + adj[v].push(u); + } + + let res = 0; + for (let startNode = 0; startNode < n; startNode++) { + const q = new Queue([startNode]); + const visited = new Set([startNode]); + let count = 0; + + while (q.length) { + let node = q.shift(); + if (vals[node] === vals[startNode] && node >= startNode) { + count++; + } + + for (const child of adj[node]) { + if (!visited.has(child) && vals[child] <= vals[startNode]) { + visited.add(child); + q.push(child); + } + } + } + + res += count; + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n)$ + +--- + +## 3. Disjoint Set Union + +::tabs-start + +```python +class DSU: + def __init__(self, n): + self.Parent = list(range(n + 1)) + self.Size = [1] * (n + 1) + + def find(self, node): + if self.Parent[node] != node: + self.Parent[node] = self.find(self.Parent[node]) + return self.Parent[node] + + def union(self, u, v): + pu, pv = self.find(u), self.find(v) + if pu == pv: + return False + if self.Size[pu] >= self.Size[pv]: + self.Size[pu] += self.Size[pv] + self.Parent[pv] = pu + else: + self.Size[pv] += self.Size[pu] + self.Parent[pu] = pv + return True + +class Solution: + def numberOfGoodPaths(self, vals: List[int], edges: List[List[int]]) -> int: + adj = collections.defaultdict(list) + for a, b in edges: + adj[a].append(b) + adj[b].append(a) + + valToIndex = collections.defaultdict(list) + for i, val in enumerate(vals): + valToIndex[val].append(i) + + res = 0 + uf = DSU(len(vals)) + + for val in sorted(valToIndex.keys()): + for i in valToIndex[val]: + for nei in adj[i]: + if vals[nei] <= vals[i]: + uf.union(nei, i) + + count = collections.defaultdict(int) + for i in valToIndex[val]: + count[uf.find(i)] += 1 + res += count[uf.find(i)] + + return res +``` + +```java +class DSU { + private int[] parent, size; + + public DSU(int n) { + parent = new int[n + 1]; + size = new int[n + 1]; + for (int i = 0; i <= n; i++) { + parent[i] = i; + size[i] = 1; + } + } + + public int find(int node) { + if (parent[node] != node) { + parent[node] = find(parent[node]); + } + return parent[node]; + } + + public boolean union(int u, int v) { + int pu = find(u), pv = find(v); + if (pu == pv) return false; + if (size[pu] >= size[pv]) { + size[pu] += size[pv]; + parent[pv] = pu; + } else { + size[pv] += size[pu]; + parent[pu] = pv; + } + return true; + } +} + +public class Solution { + public int numberOfGoodPaths(int[] vals, int[][] edges) { + int n = vals.length; + List[] adj = new ArrayList[n]; + for (int i = 0; i < n; i++) adj[i] = new ArrayList<>(); + + for (int[] edge : edges) { + adj[edge[0]].add(edge[1]); + adj[edge[1]].add(edge[0]); + } + + TreeMap> valToIndex = new TreeMap<>(); + for (int i = 0; i < n; i++) { + valToIndex.putIfAbsent(vals[i], new ArrayList<>()); + valToIndex.get(vals[i]).add(i); + } + + DSU dsu = new DSU(n); + int res = 0; + + for (int val : valToIndex.keySet()) { + for (int i : valToIndex.get(val)) { + for (int nei : adj[i]) { + if (vals[nei] <= vals[i]) dsu.union(nei, i); + } + } + + Map count = new HashMap<>(); + for (int i : valToIndex.get(val)) { + int root = dsu.find(i); + count.put(root, count.getOrDefault(root, 0) + 1); + res += count.get(root); + } + } + return res; + } +} +``` + +```cpp +class DSU { +public: + vector parent, size; + + DSU(int n) { + parent.resize(n + 1); + size.resize(n + 1, 1); + for (int i = 0; i <= n; i++) parent[i] = i; + } + + int find(int node) { + if (parent[node] != node) parent[node] = find(parent[node]); + return parent[node]; + } + + bool unite(int u, int v) { + int pu = find(u), pv = find(v); + if (pu == pv) return false; + if (size[pu] >= size[pv]) { + size[pu] += size[pv]; + parent[pv] = pu; + } else { + size[pv] += size[pu]; + parent[pu] = pv; + } + return true; + } +}; + +class Solution { +public: + int numberOfGoodPaths(vector& vals, vector>& edges) { + int n = vals.size(); + vector> adj(n); + for (auto& edge : edges) { + adj[edge[0]].push_back(edge[1]); + adj[edge[1]].push_back(edge[0]); + } + + map> valToIndex; + for (int i = 0; i < n; i++) valToIndex[vals[i]].push_back(i); + + DSU dsu(n); + int res = 0; + + for (auto& [val, nodes] : valToIndex) { + for (int& i : nodes) { + for (int& nei : adj[i]) { + if (vals[nei] <= vals[i]) dsu.unite(nei, i); + } + } + + unordered_map count; + for (int& i : nodes) { + int root = dsu.find(i); + count[root]++; + res += count[root]; + } + } + return res; + } +}; +``` + +```javascript +class DSU { + /** + * @constructor + * @param {number} n + */ + constructor(n) { + this.parent = Array.from({ length: n + 1 }, (_, i) => i); + this.size = new Array(n + 1).fill(1); + } + + /** + * @param {number} node + * @return {number} + */ + find(node) { + if (this.parent[node] !== node) { + this.parent[node] = this.find(this.parent[node]); + } + return this.parent[node]; + } + + /** + * @param {number} u + * @param {number} u=v + * @return {boolean} + */ + union(u, v) { + let pu = this.find(u), pv = this.find(v); + if (pu === pv) return false; + if (this.size[pu] >= this.size[pv]) { + this.size[pu] += this.size[pv]; + this.parent[pv] = pu; + } else { + this.size[pv] += this.size[pu]; + this.parent[pu] = pv; + } + return true; + } +} + +class Solution { + /** + * @param {number[]} vals + * @param {number[][]} edges + * @return {number} + */ + numberOfGoodPaths(vals, edges) { + const n = vals.length; + const adj = Array.from({ length: n }, () => []); + + for (const [u, v] of edges) { + adj[u].push(v); + adj[v].push(u); + } + + const valToIndex = new Map(); + for (let i = 0; i < n; i++) { + if (!valToIndex.has(vals[i])) valToIndex.set(vals[i], []); + valToIndex.get(vals[i]).push(i); + } + + const dsu = new DSU(n); + let res = 0; + + for (const [val, nodes] of [...valToIndex.entries()].sort((a, b) => a[0] - b[0])) { + for (const i of nodes) { + for (const nei of adj[i]) { + if (vals[nei] <= vals[i]) dsu.union(nei, i); + } + } + + const count = new Map(); + for (const i of nodes) { + const root = dsu.find(i); + count.set(root, (count.get(root) || 0) + 1); + res += count.get(root); + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ + +--- + +## 4. Disjoint Set Union (Union By Value) + +::tabs-start + +```python +class DSU: + def __init__(self, n, vals): + self.parent = list(range(n)) + self.vals = vals + self.count = [1] * n # count of nodes with max value of the component + + def find(self, node): + if self.parent[node] != node: + self.parent[node] = self.find(self.parent[node]) + return self.parent[node] + + def union(self, u, v): + pu, pv = self.find(u), self.find(v) + if pu == pv: + return 0 + if self.vals[pu] < self.vals[pv]: + self.parent[pu] = pv + elif self.vals[pu] > self.vals[pv]: + self.parent[pv] = pu + else: + self.parent[pv] = pu + result = self.count[pu] * self.count[pv] + self.count[pu] += self.count[pv] + return result + + return 0 + + +class Solution: + def numberOfGoodPaths(self, vals: List[int], edges: List[List[int]]) -> int: + n = len(vals) + dsu = DSU(n, vals) + + # Sort edges based on max value of the two nodes + edges.sort(key=lambda edge: max(vals[edge[0]], vals[edge[1]])) + + res = n # Each node alone is a good path + for u, v in edges: + res += dsu.union(u, v) + return res +``` + +```java +class DSU { + private int[] parent, count, vals; + + public DSU(int n, int[] vals) { + this.parent = new int[n]; + this.vals = vals; + this.count = new int[n]; // count of nodes with max value of the component + Arrays.fill(count, 1); + + for (int i = 0; i < n; i++) { + parent[i] = i; + } + } + + public int find(int node) { + if (parent[node] != node) { + parent[node] = find(parent[node]); + } + return parent[node]; + } + + public int union(int u, int v) { + int pu = find(u), pv = find(v); + if (pu == pv) { + return 0; + } + if (vals[pu] < vals[pv]) { + parent[pu] = pv; + } else if (vals[pu] > vals[pv]) { + parent[pv] = pu; + } else { + parent[pv] = pu; + int result = count[pu] * count[pv]; + count[pu] += count[pv]; + return result; + } + return 0; + } +} + +public class Solution { + public int numberOfGoodPaths(int[] vals, int[][] edges) { + int n = vals.length; + DSU dsu = new DSU(n, vals); + + // Sort edges based on max value of the two nodes + Arrays.sort(edges, + Comparator.comparingInt(edge -> Math.max(vals[edge[0]], vals[edge[1]])) + ); + + int res = n; // Each node alone is a good path + for (int[] edge : edges) { + res += dsu.union(edge[0], edge[1]); + } + return res; + } +} +``` + +```cpp +class DSU { + vector parent, count, vals; + +public: + DSU(int n, vector& vals) : vals(vals), parent(n), count(n, 1) { + for (int i = 0; i < n; i++) parent[i] = i; + } + + int find(int node) { + if (parent[node] != node) { + parent[node] = find(parent[node]); + } + return parent[node]; + } + + int unionNodes(int u, int v) { + int pu = find(u), pv = find(v); + if (pu == pv) { + return 0; + } + if (vals[pu] < vals[pv]) { + parent[pu] = pv; + } else if (vals[pu] > vals[pv]) { + parent[pv] = pu; + } else { + parent[pv] = pu; + int result = count[pu] * count[pv]; + count[pu] += count[pv]; + return result; + } + return 0; + } +}; + +class Solution { +public: + int numberOfGoodPaths(vector& vals, vector>& edges) { + int n = vals.size(); + DSU dsu(n, vals); + + // Sort edges based on max value of the two nodes + sort(edges.begin(), edges.end(), [&](auto& a, auto& b) { + return max(vals[a[0]], vals[a[1]]) < max(vals[b[0]], vals[b[1]]); + }); + + int res = n; // Each node alone is a good path + for (auto& edge : edges) { + res += dsu.unionNodes(edge[0], edge[1]); + } + return res; + } +}; +``` + +```javascript +class DSU { + /** + * @param {number} n + * @param {number[]} vals + */ + constructor(n, vals) { + this.parent = Array(n).fill(0).map((_, i) => i); + this.vals = vals; + this.count = Array(n).fill(1); // count of nodes with max value of the component + } + + /** + * @param {number} node + * @return {number} + */ + find(node) { + if (this.parent[node] !== node) { + this.parent[node] = this.find(this.parent[node]); + } + return this.parent[node]; + } + + /** + * @param {number} u + * @param {number} v + * @return {number} + */ + union(u, v) { + let pu = this.find(u), pv = this.find(v); + if (pu === pv) { + return 0; + } + if (this.vals[pu] < this.vals[pv]) { + this.parent[pu] = pv; + } else if (this.vals[pu] > this.vals[pv]) { + this.parent[pv] = pu; + } else { + this.parent[pv] = pu; + let result = this.count[pu] * this.count[pv]; + this.count[pu] += this.count[pv]; + return result; + } + return 0; + } +} + +class Solution { + /** + * @param {number[]} vals + * @param {number[][]} edges + * @return {number} + */ + numberOfGoodPaths(vals, edges) { + let n = vals.length; + let dsu = new DSU(n, vals); + + // Sort edges based on max value of the two nodes + edges.sort((a, b) => + Math.max(vals[a[0]], vals[a[1]]) - Math.max(vals[b[0]], vals[b[1]]) + ); + + let res = n; // Each node alone is a good path + for (let [u, v] of edges) { + res += dsu.union(u, v); + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n\log n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/number-of-laser-beams-in-a-bank.md b/articles/number-of-laser-beams-in-a-bank.md new file mode 100644 index 000000000..598d7fbb2 --- /dev/null +++ b/articles/number-of-laser-beams-in-a-bank.md @@ -0,0 +1,110 @@ +## 1. Counting + +::tabs-start + +```python +class Solution: + def numberOfBeams(self, bank: List[str]) -> int: + prev = bank[0].count("1") + res = 0 + + for i in range(1, len(bank)): + curr = bank[i].count("1") + if curr: + res += prev * curr + prev = curr + + return res +``` + +```java +public class Solution { + public int numberOfBeams(String[] bank) { + int prev = countOnes(bank[0]); + int res = 0; + + for (int i = 1; i < bank.length; i++) { + int curr = countOnes(bank[i]); + if (curr > 0) { + res += prev * curr; + prev = curr; + } + } + + return res; + } + + private int countOnes(String s) { + int count = 0; + for (int i = 0; i < s.length(); i++) { + if (s.charAt(i) == '1') count++; + } + return count; + } +} +``` + +```cpp +class Solution { +public: + int numberOfBeams(vector& bank) { + int prev = countOnes(bank[0]); + int res = 0; + + for (int i = 1; i < bank.size(); i++) { + int curr = countOnes(bank[i]); + if (curr > 0) { + res += prev * curr; + prev = curr; + } + } + + return res; + } + +private: + int countOnes(const string& s) { + return count(s.begin(), s.end(), '1'); + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} bank + * @return {number} + */ + numberOfBeams(bank) { + const countOnes = (s) => { + let cnt = 0; + for (let c of s) { + if (c === '1') cnt += 1; + } + return cnt; + }; + + let prev = countOnes(bank[0]); + let res = 0; + + for (let i = 1; i < bank.length; i++) { + let curr = countOnes(bank[i]); + if (curr > 0) { + res += prev * curr; + prev = curr; + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n)$ +* Space complexity: $O(1)$ extra space. + +> Where $m$ is the number of rows and $n$ is the number of columns. \ No newline at end of file diff --git a/articles/number-of-ways-to-divide-a-long-corridor.md b/articles/number-of-ways-to-divide-a-long-corridor.md new file mode 100644 index 000000000..cda191a27 --- /dev/null +++ b/articles/number-of-ways-to-divide-a-long-corridor.md @@ -0,0 +1,608 @@ +## 1. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def numberOfWays(self, corridor: str) -> int: + mod = 10**9 + 7 + cache = [[-1] * 3 for i in range(len(corridor))] # (i, seats) -> count + + def dfs(i, seats): + if i == len(corridor): + return 1 if seats == 2 else 0 + if cache[i][seats] != -1: + return cache[i][seats] + + res = 0 + if seats == 2: + if corridor[i] == "S": + res = dfs(i + 1, 1) + else: + res = (dfs(i + 1, 0) + dfs(i + 1, 2)) % mod + else: + if corridor[i] == "S": + res = dfs(i + 1, seats + 1) + else: + res = dfs(i + 1, seats) + + cache[i][seats] = res + return res + + return dfs(0, 0) +``` + +```java +public class Solution { + private static final int MOD = 1_000_000_007; + private int[][] dp; + + public int numberOfWays(String corridor) { + int n = corridor.length(); + dp = new int[n][3]; + for (int i = 0; i < n; i++) { + Arrays.fill(dp[i], -1); + } + return dfs(0, 0, corridor); + } + + private int dfs(int i, int seats, String corridor) { + if (i == corridor.length()) { + return seats == 2 ? 1 : 0; + } + if (dp[i][seats] != -1) { + return dp[i][seats]; + } + + int res = 0; + if (seats == 2) { + if (corridor.charAt(i) == 'S') { + res = dfs(i + 1, 1, corridor); + } else { + res = (dfs(i + 1, 0, corridor) + dfs(i + 1, 2, corridor)) % MOD; + } + } else { + if (corridor.charAt(i) == 'S') { + res = dfs(i + 1, seats + 1, corridor); + } else { + res = dfs(i + 1, seats, corridor); + } + } + + return dp[i][seats] = res; + } +} +``` + +```cpp +class Solution { +public: + static constexpr int MOD = 1'000'000'007; + vector> dp; + + int numberOfWays(string corridor) { + int n = corridor.size(); + dp.assign(n, vector(3, -1)); + return dfs(0, 0, corridor); + } + + int dfs(int i, int seats, string& corridor) { + if (i == corridor.size()) { + return seats == 2 ? 1 : 0; + } + if (dp[i][seats] != -1) { + return dp[i][seats]; + } + + int res = 0; + if (seats == 2) { + if (corridor[i] == 'S') { + res = dfs(i + 1, 1, corridor); + } else { + res = (dfs(i + 1, 0, corridor) + dfs(i + 1, 2, corridor)) % MOD; + } + } else { + if (corridor[i] == 'S') { + res = dfs(i + 1, seats + 1, corridor); + } else { + res = dfs(i + 1, seats, corridor); + } + } + + return dp[i][seats] = res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} corridor + * @return {number} + */ + numberOfWays(corridor) { + const MOD = 1_000_000_007; + const n = corridor.length; + const dp = Array.from({ length: n }, () => Array(3).fill(-1)); + + const dfs = (i, seats) => { + if (i === n) return seats === 2 ? 1 : 0; + if (dp[i][seats] !== -1) return dp[i][seats]; + + let res = 0; + if (seats === 2) { + if (corridor[i] === 'S') { + res = dfs(i + 1, 1); + } else { + res = (dfs(i + 1, 0) + dfs(i + 1, 2)) % MOD; + } + } else { + if (corridor[i] === 'S') { + res = dfs(i + 1, seats + 1); + } else { + res = dfs(i + 1, seats); + } + } + + return (dp[i][seats] = res); + }; + + return dfs(0, 0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def numberOfWays(self, corridor: str) -> int: + MOD = 1000000007 + n = len(corridor) + dp = [[0] * 3 for _ in range(n + 1)] + dp[n][2] = 1 + + for i in range(n - 1, -1, -1): + for seats in range(3): + if seats == 2: + if corridor[i] == 'S': + dp[i][seats] = dp[i + 1][1] + else: + dp[i][seats] = (dp[i + 1][0] + dp[i + 1][2]) % MOD + else: + if corridor[i] == 'S': + dp[i][seats] = dp[i + 1][seats + 1] + else: + dp[i][seats] = dp[i + 1][seats] + + return dp[0][0] +``` + +```java +public class Solution { + public int numberOfWays(String corridor) { + int MOD = 1000000007; + int n = corridor.length(); + int[][] dp = new int[n + 1][3]; + dp[n][2] = 1; + + for (int i = n - 1; i >= 0; i--) { + for (int seats = 0; seats < 3; seats++) { + if (seats == 2) { + if (corridor.charAt(i) == 'S') { + dp[i][seats] = dp[i + 1][1]; + } else { + dp[i][seats] = (dp[i + 1][0] + dp[i + 1][2]) % MOD; + } + } else { + if (corridor.charAt(i) == 'S') { + dp[i][seats] = dp[i + 1][seats + 1]; + } else { + dp[i][seats] = dp[i + 1][seats]; + } + } + } + } + return dp[0][0]; + } +} +``` + +```cpp +class Solution { +public: + int numberOfWays(string corridor) { + int MOD = 1000000007; + int n = corridor.size(); + vector> dp(n + 1, vector(3, 0)); + dp[n][2] = 1; + + for (int i = n - 1; i >= 0; i--) { + for (int seats = 0; seats < 3; seats++) { + if (seats == 2) { + if (corridor[i] == 'S') { + dp[i][seats] = dp[i + 1][1]; + } else { + dp[i][seats] = (dp[i + 1][0] + dp[i + 1][2]) % MOD; + } + } else { + if (corridor[i] == 'S') { + dp[i][seats] = dp[i + 1][seats + 1]; + } else { + dp[i][seats] = dp[i + 1][seats]; + } + } + } + } + return dp[0][0]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} corridor + * @return {number} + */ + numberOfWays(corridor) { + const MOD = 1000000007; + const n = corridor.length; + let dp = Array.from({ length: n + 1 }, () => Array(3).fill(0)); + dp[n][2] = 1; + + for (let i = n - 1; i >= 0; i--) { + for (let seats = 0; seats < 3; seats++) { + if (seats === 2) { + if (corridor[i] === 'S') { + dp[i][seats] = dp[i + 1][1]; + } else { + dp[i][seats] = (dp[i + 1][0] + dp[i + 1][2]) % MOD; + } + } else { + if (corridor[i] === 'S') { + dp[i][seats] = dp[i + 1][seats + 1]; + } else { + dp[i][seats] = dp[i + 1][seats]; + } + } + } + } + return dp[0][0]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Dynamic Programming (Space Optimized) + +::tabs-start + +```python +class Solution: + def numberOfWays(self, corridor: str) -> int: + MOD = 1000000007 + dp = [0, 0, 1] + + for i in reversed(corridor): + new_dp = [0] * 3 + for seats in range(3): + if seats == 2: + new_dp[seats] = dp[1] if i == 'S' else (dp[0] + dp[2]) % MOD + else: + new_dp[seats] = dp[seats + 1] if i == 'S' else dp[seats] + dp = new_dp + + return dp[0] +``` + +```java +public class Solution { + public int numberOfWays(String corridor) { + int MOD = 1000000007; + int[] dp = {0, 0, 1}; + + for (int i = corridor.length() - 1; i >= 0; i--) { + int[] new_dp = new int[3]; + for (int seats = 0; seats < 3; seats++) { + if (seats == 2) { + new_dp[seats] = corridor.charAt(i) == 'S' ? dp[1] : (dp[0] + dp[2]) % MOD; + } else { + new_dp[seats] = corridor.charAt(i) == 'S' ? dp[seats + 1] : dp[seats]; + } + } + dp = new_dp; + } + return dp[0]; + } +} +``` + +```cpp +class Solution { +public: + int numberOfWays(string corridor) { + const int MOD = 1000000007; + vector dp = {0, 0, 1}; + + for (int i = corridor.length() - 1; i >= 0; i--) { + vector new_dp(3, 0); + for (int seats = 0; seats < 3; seats++) { + if (seats == 2) { + new_dp[seats] = (corridor[i] == 'S') ? dp[1] : (dp[0] + dp[2]) % MOD; + } else { + new_dp[seats] = (corridor[i] == 'S') ? dp[seats + 1] : dp[seats]; + } + } + dp = new_dp; + } + return dp[0]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} corridor + * @return {number} + */ + numberOfWays(corridor) { + const MOD = 1000000007; + let dp = [0, 0, 1]; + + for (let i = corridor.length - 1; i >= 0; i--) { + let new_dp = [0, 0, 0]; + for (let seats = 0; seats < 3; seats++) { + if (seats === 2) { + new_dp[seats] = corridor[i] === 'S' ? dp[1] : (dp[0] + dp[2]) % MOD; + } else { + new_dp[seats] = corridor[i] === 'S' ? dp[seats + 1] : dp[seats]; + } + } + dp = new_dp; + } + return dp[0]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ + +--- + +## 4. Combinatorics + +::tabs-start + +```python +class Solution: + def numberOfWays(self, corridor: str) -> int: + mod = 10**9 + 7 + seats = [i for i, c in enumerate(corridor) if c == "S"] + + length = len(seats) + if length < 2 or length % 2 == 1: + return 0 + + res = 1 + for i in range(1, length - 1, 2): + res = (res * (seats[i + 1] - seats[i])) % mod + + return res +``` + +```java +public class Solution { + public int numberOfWays(String corridor) { + int mod = 1_000_000_007; + List seats = new ArrayList<>(); + + for (int i = 0; i < corridor.length(); i++) { + if (corridor.charAt(i) == 'S') { + seats.add(i); + } + } + + int length = seats.size(); + if (length < 2 || length % 2 == 1) { + return 0; + } + + long res = 1; + for (int i = 1; i < length - 1; i += 2) { + res = (res * (seats.get(i + 1) - seats.get(i))) % mod; + } + + return (int) res; + } +} +``` + +```cpp +class Solution { +public: + int numberOfWays(string corridor) { + int mod = 1'000'000'007; + vector seats; + + for (int i = 0; i < corridor.size(); i++) { + if (corridor[i] == 'S') { + seats.push_back(i); + } + } + + int length = seats.size(); + if (length < 2 || length % 2 == 1) { + return 0; + } + + long long res = 1; + for (int i = 1; i < length - 1; i += 2) { + res = (res * (seats[i + 1] - seats[i])) % mod; + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} corridor + * @return {number} + */ + numberOfWays(corridor) { + const mod = 1_000_000_007; + const seats = []; + + for (let i = 0; i < corridor.length; i++) { + if (corridor[i] === 'S') { + seats.push(i); + } + } + + const length = seats.length; + if (length < 2 || length % 2 === 1) { + return 0; + } + + let res = 1; + for (let i = 1; i < length - 1; i += 2) { + res = (res * (seats[i + 1] - seats[i])) % mod; + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 5. Combinatorics (Optimal) + +::tabs-start + +```python +class Solution: + def numberOfWays(self, corridor: str) -> int: + mod = 1_000_000_007 + count = 0 + res = 1 + prev = -1 + + for i, c in enumerate(corridor): + if c == 'S': + count += 1 + if count > 2 and count % 2 == 1: + res = (res * (i - prev)) % mod + prev = i + + return res if count >= 2 and count % 2 == 0 else 0 +``` + +```java +public class Solution { + public int numberOfWays(String corridor) { + int mod = 1_000_000_007; + int count = 0, res = 1, prev = -1; + + for (int i = 0; i < corridor.length(); i++) { + if (corridor.charAt(i) == 'S') { + count++; + if (count > 2 && count % 2 == 1) { + res = (int)((res * (long)(i - prev)) % mod); + } + prev = i; + } + } + + return (count >= 2 && count % 2 == 0) ? res : 0; + } +} +``` + +```cpp +class Solution { +public: + int numberOfWays(string corridor) { + int mod = 1'000'000'007, count = 0, res = 1, prev = -1; + + for (int i = 0; i < corridor.size(); i++) { + if (corridor[i] == 'S') { + count++; + if (count > 2 && count % 2 == 1) { + res = (1LL * res * (i - prev)) % mod; + } + prev = i; + } + } + + return (count >= 2 && count % 2 == 0) ? res : 0; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} corridor + * @return {number} + */ + numberOfWays(corridor) { + const mod = 1_000_000_007; + let count = 0, res = 1, prev = -1; + + for (let i = 0; i < corridor.length; i++) { + if (corridor[i] === 'S') { + count++; + if (count > 2 && count % 2 === 1) { + res = (res * (i - prev)) % mod; + } + prev = i; + } + } + + return count >= 2 && count % 2 === 0 ? res : 0; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/operations-on-tree.md b/articles/operations-on-tree.md new file mode 100644 index 000000000..682f18874 --- /dev/null +++ b/articles/operations-on-tree.md @@ -0,0 +1,806 @@ +## 1. Depth First Search + +::tabs-start + +```python +class LockingTree: + + def __init__(self, parent: List[int]): + self.parent = parent + self.child = [[] for _ in range(len(parent))] + self.locked = [0] * len(parent) + for node in range(1, len(parent)): + self.child[parent[node]].append(node) + + def lock(self, num: int, user: int) -> bool: + if self.locked[num]: + return False + self.locked[num] = user + return True + + def unlock(self, num: int, user: int) -> bool: + if self.locked[num] != user: + return False + self.locked[num] = 0 + return True + + def upgrade(self, num: int, user: int) -> bool: + node = num + while node != -1: + if self.locked[node]: + return False + node = self.parent[node] + + def dfs(node): + lockedCount = 0 + if self.locked[node]: + lockedCount += 1 + self.locked[node] = 0 + + for nei in self.child[node]: + lockedCount += dfs(nei) + return lockedCount + + if dfs(num) > 0: + self.locked[num] = user + return True + return False +``` + +```java +public class LockingTree { + private int[] parent; + private List[] child; + private int[] locked; + + public LockingTree(int[] parent) { + this.parent = parent; + int n = parent.length; + child = new ArrayList[n]; + locked = new int[n]; + + for (int i = 0; i < n; i++) { + child[i] = new ArrayList<>(); + } + for (int node = 1; node < n; node++) { + child[parent[node]].add(node); + } + } + + public boolean lock(int num, int user) { + if (locked[num] != 0) { + return false; + } + locked[num] = user; + return true; + } + + public boolean unlock(int num, int user) { + if (locked[num] != user) { + return false; + } + locked[num] = 0; + return true; + } + + public boolean upgrade(int num, int user) { + int node = num; + while (node != -1) { + if (locked[node] != 0) { + return false; + } + node = parent[node]; + } + + int lockedCount = dfs(num); + if (lockedCount > 0) { + locked[num] = user; + return true; + } + return false; + } + + private int dfs(int node) { + int lockedCount = 0; + if (locked[node] != 0) { + lockedCount++; + locked[node] = 0; + } + for (int nei : child[node]) { + lockedCount += dfs(nei); + } + return lockedCount; + } +} +``` + +```cpp +class LockingTree { +private: + vector parent; + vector> child; + vector locked; + +public: + LockingTree(vector& parent) : parent(parent), locked(parent.size()) { + int n = parent.size(); + child.resize(n); + for (int node = 1; node < n; node++) { + child[parent[node]].push_back(node); + } + } + + bool lock(int num, int user) { + if (locked[num]) { + return false; + } + locked[num] = user; + return true; + } + + bool unlock(int num, int user) { + if (locked[num] != user) { + return false; + } + locked[num] = 0; + return true; + } + + bool upgrade(int num, int user) { + int node = num; + while (node != -1) { + if (locked[node]) { + return false; + } + node = parent[node]; + } + + int lockedCount = dfs(num); + if (lockedCount > 0) { + locked[num] = user; + return true; + } + return false; + } + +private: + int dfs(int node) { + int lockedCount = 0; + if (locked[node]) { + lockedCount++; + locked[node] = 0; + } + for (int& nei : child[node]) { + lockedCount += dfs(nei); + } + return lockedCount; + } +}; +``` + +```javascript +class LockingTree { + /** + * @constructor + * @param {number[]} parent + */ + constructor(parent) { + this.parent = parent; + this.child = Array.from({ length: parent.length }, () => []); + this.locked = new Array(parent.length).fill(0); + + for (let node = 1; node < parent.length; node++) { + this.child[parent[node]].push(node); + } + } + + /** + * @param {number} num + * @param {number} user + * @return {boolean} + */ + lock(num, user) { + if (this.locked[num] !== 0) { + return false; + } + this.locked[num] = user; + return true; + } + + /** + * @param {number} num + * @param {number} user + * @return {boolean} + */ + unlock(num, user) { + if (this.locked[num] !== user) { + return false; + } + this.locked[num] = 0; + return true; + } + + /** + * @param {number} num + * @param {number} user + * @return {boolean} + */ + upgrade(num, user) { + let node = num; + while (node !== -1) { + if (this.locked[node] !== 0) { + return false; + } + node = this.parent[node]; + } + + const dfs = (node) => { + let lockedCount = 0; + if (this.locked[node] !== 0) { + lockedCount++; + this.locked[node] = 0; + } + for (let nei of this.child[node]) { + lockedCount += dfs(nei); + } + return lockedCount; + }; + + if (dfs(num) > 0) { + this.locked[num] = user; + return true; + } + return false; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: + * $O(n)$ time for initialization. + * $O(1)$ time for each $lock()$ and $unlock()$ function call. + * $O(n)$ time for each $upgrade()$ function call. +* Space complexity: $O(n)$ + +--- + +## 2. Breadth First Search + +::tabs-start + +```python +class LockingTree: + + def __init__(self, parent: List[int]): + self.parent = parent + self.child = [[] for _ in range(len(parent))] + self.locked = [0] * len(parent) + for node in range(1, len(parent)): + self.child[parent[node]].append(node) + + def lock(self, num: int, user: int) -> bool: + if self.locked[num]: + return False + self.locked[num] = user + return True + + def unlock(self, num: int, user: int) -> bool: + if self.locked[num] != user: + return False + self.locked[num] = 0 + return True + + def upgrade(self, num: int, user: int) -> bool: + node = num + while node != -1: + if self.locked[node]: + return False + node = self.parent[node] + + lockedCount, q = 0, deque([num]) + while q: + node = q.popleft() + if self.locked[node]: + self.locked[node] = 0 + lockedCount += 1 + q.extend(self.child[node]) + + if lockedCount: + self.locked[num] = user + return True + return False +``` + +```java +public class LockingTree { + private int[] parent; + private List[] child; + private int[] locked; + + public LockingTree(int[] parent) { + this.parent = parent; + int n = parent.length; + child = new ArrayList[n]; + locked = new int[n]; + + for (int i = 0; i < n; i++) { + child[i] = new ArrayList<>(); + } + for (int node = 1; node < n; node++) { + child[parent[node]].add(node); + } + } + + public boolean lock(int num, int user) { + if (locked[num] != 0) { + return false; + } + locked[num] = user; + return true; + } + + public boolean unlock(int num, int user) { + if (locked[num] != user) { + return false; + } + locked[num] = 0; + return true; + } + + public boolean upgrade(int num, int user) { + int node = num; + while (node != -1) { + if (locked[node] != 0) { + return false; + } + node = parent[node]; + } + + int lockedCount = 0; + Queue q = new LinkedList<>(); + q.offer(num); + + while (!q.isEmpty()) { + node = q.poll(); + if (locked[node] != 0) { + locked[node] = 0; + lockedCount++; + } + q.addAll(child[node]); + } + + if (lockedCount > 0) { + locked[num] = user; + return true; + } + return false; + } +} +``` + +```cpp +class LockingTree { +private: + vector parent; + vector> child; + vector locked; + +public: + LockingTree(vector& parent) : parent(parent), locked(parent.size()) { + int n = parent.size(); + child.resize(n); + for (int node = 1; node < n; node++) { + child[parent[node]].push_back(node); + } + } + + bool lock(int num, int user) { + if (locked[num]) { + return false; + } + locked[num] = user; + return true; + } + + bool unlock(int num, int user) { + if (locked[num] != user) { + return false; + } + locked[num] = 0; + return true; + } + + bool upgrade(int num, int user) { + int node = num; + while (node != -1) { + if (locked[node]) { + return false; + } + node = parent[node]; + } + + int lockedCount = 0; + queue q; + q.push(num); + + while (!q.empty()) { + node = q.front(); q.pop(); + if (locked[node]) { + locked[node] = 0; + lockedCount++; + } + for (int nei : child[node]) { + q.push(nei); + } + } + + if (lockedCount > 0) { + locked[num] = user; + return true; + } + return false; + } +}; +``` + +```javascript +class LockingTree { + /** + * @constructor + * @param {number[]} parent + */ + constructor(parent) { + this.parent = parent; + this.child = Array.from({ length: parent.length }, () => []); + this.locked = new Array(parent.length).fill(0); + + for (let node = 1; node < parent.length; node++) { + this.child[parent[node]].push(node); + } + } + + /** + * @param {number} num + * @param {number} user + * @return {boolean} + */ + lock(num, user) { + if (this.locked[num] !== 0) { + return false; + } + this.locked[num] = user; + return true; + } + + /** + * @param {number} num + * @param {number} user + * @return {boolean} + */ + unlock(num, user) { + if (this.locked[num] !== user) { + return false; + } + this.locked[num] = 0; + return true; + } + + /** + * @param {number} num + * @param {number} user + * @return {boolean} + */ + upgrade(num, user) { + let node = num; + while (node !== -1) { + if (this.locked[node] !== 0) { + return false; + } + node = this.parent[node]; + } + + let lockedCount = 0; + const q = new Queue([num]); + + while (!q.isEmpty()) { + node = q.pop(); + if (this.locked[node]) { + this.locked[node] = 0; + lockedCount++; + } + for (let nei of this.child[node]) { + q.push(nei); + } + } + + if (lockedCount > 0) { + this.locked[num] = user; + return true; + } + return false; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: + * $O(n)$ time for initialization. + * $O(1)$ time for each $lock()$ and $unlock()$ function call. + * $O(n)$ time for each $upgrade()$ function call. +* Space complexity: $O(n)$ + +--- + +## 3. Iterative DFS + +::tabs-start + +```python +class LockingTree: + + def __init__(self, parent: List[int]): + self.parent = parent + self.child = [[] for _ in range(len(parent))] + self.locked = [0] * len(parent) + for node in range(1, len(parent)): + self.child[parent[node]].append(node) + + def lock(self, num: int, user: int) -> bool: + if self.locked[num]: + return False + self.locked[num] = user + return True + + def unlock(self, num: int, user: int) -> bool: + if self.locked[num] != user: + return False + self.locked[num] = 0 + return True + + def upgrade(self, num: int, user: int) -> bool: + node = num + while node != -1: + if self.locked[node]: + return False + node = self.parent[node] + + lockedCount, stack = 0, [num] + while stack: + node = stack.pop() + if self.locked[node]: + self.locked[node] = 0 + lockedCount += 1 + stack.extend(self.child[node]) + + if lockedCount: + self.locked[num] = user + return True + return False +``` + +```java +public class LockingTree { + private int[] parent; + private List[] child; + private int[] locked; + + public LockingTree(int[] parent) { + this.parent = parent; + int n = parent.length; + child = new ArrayList[n]; + locked = new int[n]; + + for (int i = 0; i < n; i++) { + child[i] = new ArrayList<>(); + } + for (int node = 1; node < n; node++) { + child[parent[node]].add(node); + } + } + + public boolean lock(int num, int user) { + if (locked[num] != 0) { + return false; + } + locked[num] = user; + return true; + } + + public boolean unlock(int num, int user) { + if (locked[num] != user) { + return false; + } + locked[num] = 0; + return true; + } + + public boolean upgrade(int num, int user) { + int node = num; + while (node != -1) { + if (locked[node] != 0) { + return false; + } + node = parent[node]; + } + + int lockedCount = 0; + Stack stack = new Stack<>(); + stack.push(num); + + while (!stack.isEmpty()) { + node = stack.pop(); + if (locked[node] != 0) { + locked[node] = 0; + lockedCount++; + } + for (int nei : child[node]) { + stack.push(nei); + } + } + + if (lockedCount > 0) { + locked[num] = user; + return true; + } + return false; + } +} +``` + +```cpp +class LockingTree { +private: + vector parent; + vector> child; + vector locked; + +public: + LockingTree(vector& parent) : parent(parent), locked(parent.size()) { + int n = parent.size(); + child.resize(n); + for (int node = 1; node < n; node++) { + child[parent[node]].push_back(node); + } + } + + bool lock(int num, int user) { + if (locked[num]) { + return false; + } + locked[num] = user; + return true; + } + + bool unlock(int num, int user) { + if (locked[num] != user) { + return false; + } + locked[num] = 0; + return true; + } + + bool upgrade(int num, int user) { + int node = num; + while (node != -1) { + if (locked[node]) { + return false; + } + node = parent[node]; + } + + int lockedCount = 0; + stack stk; + stk.push(num); + + while (!stk.empty()) { + node = stk.top(); stk.pop(); + if (locked[node]) { + locked[node] = 0; + lockedCount++; + } + for (int& nei : child[node]) { + stk.push(nei); + } + } + + if (lockedCount > 0) { + locked[num] = user; + return true; + } + return false; + } +}; +``` + +```javascript +class LockingTree { + /** + * @constructor + * @param {number[]} parent + */ + constructor(parent) { + this.parent = parent; + this.child = Array.from({ length: parent.length }, () => []); + this.locked = new Array(parent.length).fill(0); + + for (let node = 1; node < parent.length; node++) { + this.child[parent[node]].push(node); + } + } + + /** + * @param {number} num + * @param {number} user + * @return {boolean} + */ + lock(num, user) { + if (this.locked[num] !== 0) { + return false; + } + this.locked[num] = user; + return true; + } + + /** + * @param {number} num + * @param {number} user + * @return {boolean} + */ + unlock(num, user) { + if (this.locked[num] !== user) { + return false; + } + this.locked[num] = 0; + return true; + } + + /** + * @param {number} num + * @param {number} user + * @return {boolean} + */ + upgrade(num, user) { + let node = num; + while (node !== -1) { + if (this.locked[node] !== 0) { + return false; + } + node = this.parent[node]; + } + + let lockedCount = 0; + let stack = [num]; + + while (stack.length) { + node = stack.pop(); + if (this.locked[node]) { + this.locked[node] = 0; + lockedCount++; + } + stack.push(...this.child[node]); + } + + if (lockedCount > 0) { + this.locked[num] = user; + return true; + } + return false; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: + * $O(n)$ time for initialization. + * $O(1)$ time for each $lock()$ and $unlock()$ function call. + * $O(n)$ time for each $upgrade()$ function call. +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/parallel-courses-iii.md b/articles/parallel-courses-iii.md new file mode 100644 index 000000000..ca66a0769 --- /dev/null +++ b/articles/parallel-courses-iii.md @@ -0,0 +1,481 @@ +## 1. Depth First Search + +::tabs-start + +```python +class Solution: + def minimumTime(self, n: int, relations: list[list[int]], time: list[int]) -> int: + adj = defaultdict(list) + for src, dst in relations: + adj[src].append(dst) + + max_time = {} + + def dfs(src): + if src in max_time: + return max_time[src] + res = time[src - 1] + for nei in adj[src]: + res = max(res, time[src - 1] + dfs(nei)) + max_time[src] = res + return res + + for i in range(1, n + 1): + dfs(i) + + return max(max_time.values()) +``` + +```java +public class Solution { + private Map maxTime; + private List[] adj; + private int[] time; + + public int minimumTime(int n, int[][] relations, int[] time) { + this.time = time; + this.maxTime = new HashMap<>(); + this.adj = new ArrayList[n + 1]; + + for (int i = 1; i <= n; i++) { + adj[i] = new ArrayList<>(); + } + for (int[] relation : relations) { + adj[relation[0]].add(relation[1]); + } + + for (int i = 1; i <= n; i++) { + dfs(i); + } + + return Collections.max(maxTime.values()); + } + + private int dfs(int src) { + if (maxTime.containsKey(src)) { + return maxTime.get(src); + } + + int res = time[src - 1]; + for (int nei : adj[src]) { + res = Math.max(res, time[src - 1] + dfs(nei)); + } + maxTime.put(src, res); + return res; + } +} +``` + +```cpp +class Solution { +public: + unordered_map maxTime; + vector> adj; + vector time; + + int minimumTime(int n, vector>& relations, vector& time) { + this->time = time; + adj.resize(n + 1); + + for (auto& relation : relations) { + adj[relation[0]].push_back(relation[1]); + } + + for (int i = 1; i <= n; i++) { + dfs(i); + } + + int res = 0; + for (auto& [key, value] : maxTime) { + res = max(res, value); + } + return res; + } + +private: + int dfs(int src) { + if (maxTime.count(src)) { + return maxTime[src]; + } + + int res = time[src - 1]; + for (int nei : adj[src]) { + res = max(res, time[src - 1] + dfs(nei)); + } + maxTime[src] = res; + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number[][]} relations + * @param {number[]} time + * @return {number} + */ + minimumTime(n, relations, time) { + let adj = Array.from({ length: n + 1 }, () => []); + let maxTime = new Map(); + + for (let [src, dst] of relations) { + adj[src].push(dst); + } + + const dfs = (src) => { + if (maxTime.has(src)) { + return maxTime.get(src); + } + + let res = time[src - 1]; + for (let nei of adj[src]) { + res = Math.max(res, time[src - 1] + dfs(nei)); + } + maxTime.set(src, res); + return res; + }; + + for (let i = 1; i <= n; i++) { + dfs(i); + } + + return Math.max(...maxTime.values()); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(V + E)$ +* Space complexity: $O(V + E)$ + +> Where $V$ is the number of courses and $E$ is the number of prerequisites. + +--- + +## 2. Iterative DFS + +::tabs-start + +```python +class Solution: + def minimumTime(self, n: int, relations: list[list[int]], time: list[int]) -> int: + adj = [[] for _ in range(n)] + for src, dst in relations: + adj[src - 1].append(dst - 1) + + maxTime = [-1] * n + processed = [False] * n + + for i in range(n): + if maxTime[i] == -1: + stack = [i] + while stack: + node = stack.pop() + if processed[node]: + best = 0 + for nei in adj[node]: + best = max(best, maxTime[nei]) + maxTime[node] = time[node] + best + else: + processed[node] = True + stack.append(node) + for nei in adj[node]: + if maxTime[nei] == -1: + stack.append(nei) + return max(maxTime) +``` + +```java +public class Solution { + public int minimumTime(int n, int[][] relations, int[] time) { + List[] adj = new ArrayList[n]; + for (int i = 0; i < n; i++) adj[i] = new ArrayList<>(); + for (int[] rel : relations) { + adj[rel[0] - 1].add(rel[1] - 1); + } + + int[] maxTime = new int[n]; + Arrays.fill(maxTime, -1); + boolean[] processed = new boolean[n]; + + for (int i = 0; i < n; i++) { + if (maxTime[i] == -1) { + Stack stack = new Stack<>(); + stack.push(i); + while (!stack.isEmpty()) { + int node = stack.pop(); + if (processed[node]) { + int best = 0; + for (int nei : adj[node]) { + best = Math.max(best, maxTime[nei]); + } + maxTime[node] = time[node] + best; + } else { + processed[node] = true; + stack.push(node); + for (int nei : adj[node]) { + if (maxTime[nei] == -1) { + stack.push(nei); + } + } + } + } + } + } + return Arrays.stream(maxTime).max().getAsInt(); + } +} +``` + +```cpp +class Solution { +public: + int minimumTime(int n, vector>& relations, vector& time) { + vector> adj(n); + for (auto& rel : relations) { + adj[rel[0] - 1].push_back(rel[1] - 1); + } + + vector maxTime(n, -1); + vector processed(n, false); + + for (int i = 0; i < n; i++) { + if (maxTime[i] == -1) { + stack stk; + stk.push(i); + while (!stk.empty()) { + int node = stk.top(); stk.pop(); + if (processed[node]) { + int best = 0; + for (int nei : adj[node]) { + best = max(best, maxTime[nei]); + } + maxTime[node] = time[node] + best; + } else { + processed[node] = true; + stk.push(node); + for (int nei : adj[node]) { + if (maxTime[nei] == -1) { + stk.push(nei); + } + } + } + } + } + } + return *max_element(maxTime.begin(), maxTime.end()); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number[][]} relations + * @param {number[]} time + * @return {number} + */ + minimumTime(n, relations, time) { + let adj = Array.from({ length: n }, () => []); + for (let [src, dst] of relations) { + adj[src - 1].push(dst - 1); + } + + let maxTime = Array(n).fill(-1); + let processed = Array(n).fill(false); + + for (let i = 0; i < n; i++) { + if (maxTime[i] === -1) { + let stack = [i]; + while (stack.length > 0) { + let node = stack.pop(); + if (processed[node]) { + let best = 0; + for (let nei of adj[node]) { + best = Math.max(best, maxTime[nei]); + } + maxTime[node] = time[node] + best; + } else { + processed[node] = true; + stack.push(node); + for (let nei of adj[node]) { + if (maxTime[nei] === -1) { + stack.push(nei); + } + } + } + } + } + } + return Math.max(...maxTime); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(V + E)$ +* Space complexity: $O(V + E)$ + +> Where $V$ is the number of courses and $E$ is the number of prerequisites. + +--- + +## 3. Topological Sort (Kahn's Algorithm) + +::tabs-start + +```python +class Solution: + def minimumTime(self, n: int, relations: list[list[int]], time: list[int]) -> int: + adj = [[] for _ in range(n)] + indegree = [0] * n + maxTime = time[:] + + for src, dst in relations: + adj[src - 1].append(dst - 1) + indegree[dst - 1] += 1 + + queue = deque([i for i in range(n) if indegree[i] == 0]) + while queue: + node = queue.popleft() + for nei in adj[node]: + maxTime[nei] = max(maxTime[nei], maxTime[node] + time[nei]) + indegree[nei] -= 1 + if indegree[nei] == 0: + queue.append(nei) + + return max(maxTime) +``` + +```java +public class Solution { + public int minimumTime(int n, int[][] relations, int[] time) { + List> adj = new ArrayList<>(); + int[] indegree = new int[n]; + int[] maxTime = Arrays.copyOf(time, n); + + for (int i = 0; i < n; i++) { + adj.add(new ArrayList<>()); + } + + for (int[] relation : relations) { + int src = relation[0] - 1, dst = relation[1] - 1; + adj.get(src).add(dst); + indegree[dst]++; + } + + Queue queue = new LinkedList<>(); + for (int i = 0; i < n; i++) { + if (indegree[i] == 0) { + queue.add(i); + } + } + + while (!queue.isEmpty()) { + int node = queue.poll(); + for (int nei : adj.get(node)) { + maxTime[nei] = Math.max(maxTime[nei], maxTime[node] + time[nei]); + if (--indegree[nei] == 0) { + queue.add(nei); + } + } + } + + return Arrays.stream(maxTime).max().getAsInt(); + } +} +``` + +```cpp +class Solution { +public: + int minimumTime(int n, vector>& relations, vector& time) { + vector> adj(n); + vector indegree(n, 0); + vector maxTime(time.begin(), time.end()); + + for (auto& relation : relations) { + int src = relation[0] - 1, dst = relation[1] - 1; + adj[src].push_back(dst); + indegree[dst]++; + } + + queue queue; + for (int i = 0; i < n; i++) { + if (indegree[i] == 0) { + queue.push(i); + } + } + + while (!queue.empty()) { + int node = queue.front(); queue.pop(); + for (int nei : adj[node]) { + maxTime[nei] = max(maxTime[nei], maxTime[node] + time[nei]); + if (--indegree[nei] == 0) { + queue.push(nei); + } + } + } + + return *max_element(maxTime.begin(), maxTime.end()); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number[][]} relations + * @param {number[]} time + * @return {number} + */ + minimumTime(n, relations, time) { + let adj = Array.from({ length: n }, () => []); + let indegree = Array(n).fill(0); + let maxTime = [...time]; + + for (let [src, dst] of relations) { + adj[src - 1].push(dst - 1); + indegree[dst - 1]++; + } + + let queue = new Queue(); + for (let i = 0; i < n; i++) { + if (indegree[i] === 0) { + queue.push(i); + } + } + + while (!queue.isEmpty()) { + let node = queue.pop(); + for (let nei of adj[node]) { + maxTime[nei] = Math.max(maxTime[nei], maxTime[node] + time[nei]); + if (--indegree[nei] === 0) { + queue.push(nei); + } + } + } + + return Math.max(...maxTime); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(V + E)$ +* Space complexity: $O(V + E)$ + +> Where $V$ is the number of courses and $E$ is the number of prerequisites. \ No newline at end of file diff --git a/articles/partition-array-for-maximum-sum.md b/articles/partition-array-for-maximum-sum.md new file mode 100644 index 000000000..a2fb52393 --- /dev/null +++ b/articles/partition-array-for-maximum-sum.md @@ -0,0 +1,446 @@ +## 1. Recursion + +::tabs-start + +```python +class Solution: + def maxSumAfterPartitioning(self, arr: List[int], k: int) -> int: + def dfs(i): + if i >= len(arr): + return 0 + + cur_max = 0 + res = 0 + for j in range(i, min(len(arr), i + k)): + cur_max = max(cur_max, arr[j]) + window_size = j - i + 1 + res = max(res, dfs(j + 1) + cur_max * window_size) + + return res + + return dfs(0) +``` + +```java +public class Solution { + public int maxSumAfterPartitioning(int[] arr, int k) { + return dfs(0, arr, k); + } + + private int dfs(int i, int[] arr, int k) { + if (i >= arr.length) { + return 0; + } + + int cur_max = 0; + int res = 0; + for (int j = i; j < Math.min(arr.length, i + k); j++) { + cur_max = Math.max(cur_max, arr[j]); + int window_size = j - i + 1; + res = Math.max(res, dfs(j + 1, arr, k) + cur_max * window_size); + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int maxSumAfterPartitioning(vector& arr, int k) { + return dfs(0, arr, k); + } + +private: + int dfs(int i, vector& arr, int k) { + if (i >= arr.size()) { + return 0; + } + + int cur_max = 0, res = 0; + for (int j = i; j < min((int)arr.size(), i + k); j++) { + cur_max = max(cur_max, arr[j]); + int window_size = j - i + 1; + res = max(res, dfs(j + 1, arr, k) + cur_max * window_size); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} arr + * @param {number} k + * @return {number} + */ + maxSumAfterPartitioning(arr, k) { + const dfs = (i) => { + if (i >= arr.length) { + return 0; + } + + let cur_max = 0, res = 0; + for (let j = i; j < Math.min(arr.length, i + k); j++) { + cur_max = Math.max(cur_max, arr[j]); + let window_size = j - i + 1; + res = Math.max(res, dfs(j + 1) + cur_max * window_size); + } + + return res; + }; + + return dfs(0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(k ^ n)$ +* Space complexity: $O(n)$ for recursion stack. + +> Where $k$ is the maximum length of the subarray and $n$ is the size of the array $arr$. + +--- + +## 2. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def maxSumAfterPartitioning(self, arr: List[int], k: int) -> int: + cache = { len(arr) : 0 } + + def dfs(i): + if i in cache: + return cache[i] + + cur_max = 0 + res = 0 + for j in range(i, min(len(arr), i + k)): + cur_max = max(cur_max, arr[j]) + window_size = j - i + 1 + res = max(res, dfs(j + 1) + cur_max * window_size) + + cache[i] = res + return res + + return dfs(0) +``` + +```java +public class Solution { + public int maxSumAfterPartitioning(int[] arr, int k) { + int[] cache = new int[arr.length + 1]; + Arrays.fill(cache, -1); + cache[arr.length] = 0; + return dfs(0, arr, k, cache); + } + + private int dfs(int i, int[] arr, int k, int[] cache) { + if (cache[i] != -1) { + return cache[i]; + } + + int cur_max = 0, res = 0; + for (int j = i; j < Math.min(arr.length, i + k); j++) { + cur_max = Math.max(cur_max, arr[j]); + int window_size = j - i + 1; + res = Math.max(res, dfs(j + 1, arr, k, cache) + cur_max * window_size); + } + + cache[i] = res; + return res; + } +} +``` + +```cpp +class Solution { +public: + int maxSumAfterPartitioning(vector& arr, int k) { + vector cache(arr.size() + 1, -1); + cache[arr.size()] = 0; + return dfs(0, arr, k, cache); + } + +private: + int dfs(int i, vector& arr, int k, vector& cache) { + if (cache[i] != -1) { + return cache[i]; + } + + int cur_max = 0, res = 0; + for (int j = i; j < min((int)arr.size(), i + k); j++) { + cur_max = max(cur_max, arr[j]); + int window_size = j - i + 1; + res = max(res, dfs(j + 1, arr, k, cache) + cur_max * window_size); + } + + return cache[i] = res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} arr + * @param {number} k + * @return {number} + */ + maxSumAfterPartitioning(arr, k) { + const cache = new Array(arr.length + 1).fill(-1); + cache[arr.length] = 0; + + const dfs = (i) => { + if (cache[i] !== -1) { + return cache[i]; + } + + let cur_max = 0, res = 0; + for (let j = i; j < Math.min(arr.length, i + k); j++) { + cur_max = Math.max(cur_max, arr[j]); + let window_size = j - i + 1; + res = Math.max(res, dfs(j + 1) + cur_max * window_size); + } + + return (cache[i] = res); + }; + + return dfs(0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * k)$ +* Space complexity: $O(n)$ + +> Where $k$ is the maximum length of the subarray and $n$ is the size of the array $arr$. + +--- + +## 3. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def maxSumAfterPartitioning(self, arr: List[int], k: int) -> int: + n = len(arr) + dp = [0] * (n + 1) + + for i in range(n - 1, -1, -1): + cur_max = 0 + for j in range(i, min(n, i + k)): + cur_max = max(cur_max, arr[j]) + window_size = j - i + 1 + dp[i] = max(dp[i], dp[j + 1] + cur_max * window_size) + + return dp[0] +``` + +```java +public class Solution { + public int maxSumAfterPartitioning(int[] arr, int k) { + int n = arr.length; + int[] dp = new int[n + 1]; + + for (int i = n - 1; i >= 0; i--) { + int cur_max = 0; + for (int j = i; j < Math.min(n, i + k); j++) { + cur_max = Math.max(cur_max, arr[j]); + int window_size = j - i + 1; + dp[i] = Math.max(dp[i], dp[j + 1] + cur_max * window_size); + } + } + + return dp[0]; + } +} +``` + +```cpp +class Solution { +public: + int maxSumAfterPartitioning(vector& arr, int k) { + int n = arr.size(); + vector dp(n + 1, 0); + + for (int i = n - 1; i >= 0; i--) { + int cur_max = 0; + for (int j = i; j < min(n, i + k); j++) { + cur_max = max(cur_max, arr[j]); + int window_size = j - i + 1; + dp[i] = max(dp[i], dp[j + 1] + cur_max * window_size); + } + } + + return dp[0]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} arr + * @param {number} k + * @return {number} + */ + maxSumAfterPartitioning(arr, k) { + let n = arr.length; + let dp = new Array(n + 1).fill(0); + + for (let i = n - 1; i >= 0; i--) { + let cur_max = 0; + for (let j = i; j < Math.min(n, i + k); j++) { + cur_max = Math.max(cur_max, arr[j]); + let window_size = j - i + 1; + dp[i] = Math.max(dp[i], dp[j + 1] + cur_max * window_size); + } + } + + return dp[0]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * k)$ +* Space complexity: $O(n)$ + +> Where $k$ is the maximum length of the subarray and $n$ is the size of the array $arr$. + +--- + +## 4. Dynamic Programming (Space Optimized) + +::tabs-start + +```python +class Solution: + def maxSumAfterPartitioning(self, arr: List[int], k: int) -> int: + dp = [0] * k + dp[0] = arr[0] + + for i in range(1, len(arr)): + cur_max = 0 + max_at_i = 0 + for j in range(i, i - k, -1): + if j < 0: + break + cur_max = max(cur_max, arr[j]) + window_size = i - j + 1 + cur_sum = cur_max * window_size + sub_sum = dp[(j - 1) % k] if j > 0 else 0 + max_at_i = max(max_at_i, cur_sum + sub_sum) + + dp[i % k] = max_at_i + + return dp[(len(arr) - 1) % k] +``` + +```java +public class Solution { + public int maxSumAfterPartitioning(int[] arr, int k) { + int n = arr.length; + int[] dp = new int[k]; + dp[0] = arr[0]; + + for (int i = 1; i < n; i++) { + int cur_max = 0, max_at_i = 0; + for (int j = i; j > i - k; j--) { + if (j < 0) break; + cur_max = Math.max(cur_max, arr[j]); + int window_size = i - j + 1; + int cur_sum = cur_max * window_size; + int sub_sum = (j > 0) ? dp[(j - 1) % k] : 0; + max_at_i = Math.max(max_at_i, cur_sum + sub_sum); + } + dp[i % k] = max_at_i; + } + + return dp[(n - 1) % k]; + } +} +``` + +```cpp +class Solution { +public: + int maxSumAfterPartitioning(vector& arr, int k) { + int n = arr.size(); + vector dp(k); + dp[0] = arr[0]; + + for (int i = 1; i < n; i++) { + int cur_max = 0, max_at_i = 0; + for (int j = i; j > i - k; j--) { + if (j < 0) break; + cur_max = max(cur_max, arr[j]); + int window_size = i - j + 1; + int cur_sum = cur_max * window_size; + int sub_sum = (j > 0) ? dp[(j - 1) % k] : 0; + max_at_i = max(max_at_i, cur_sum + sub_sum); + } + dp[i % k] = max_at_i; + } + + return dp[(n - 1) % k]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} arr + * @param {number} k + * @return {number} + */ + maxSumAfterPartitioning(arr, k) { + const n = arr.length; + const dp = new Array(k).fill(0); + dp[0] = arr[0]; + + for (let i = 1; i < n; i++) { + let cur_max = 0, max_at_i = 0; + for (let j = i; j > i - k; j--) { + if (j < 0) break; + cur_max = Math.max(cur_max, arr[j]); + let window_size = i - j + 1; + let cur_sum = cur_max * window_size; + let sub_sum = (j > 0) ? dp[(j - 1) % k] : 0; + max_at_i = Math.max(max_at_i, cur_sum + sub_sum); + } + dp[i % k] = max_at_i; + } + + return dp[(n - 1) % k]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * k)$ +* Space complexity: $O(k)$ + +> Where $k$ is the maximum length of the subarray and $n$ is the size of the array $arr$. \ No newline at end of file diff --git a/articles/path-with-maximum-gold.md b/articles/path-with-maximum-gold.md new file mode 100644 index 000000000..81c3d201d --- /dev/null +++ b/articles/path-with-maximum-gold.md @@ -0,0 +1,508 @@ +## 1. Backtracking (DFS) - I + +::tabs-start + +```python +class Solution: + def getMaximumGold(self, grid: list[list[int]]) -> int: + ROWS, COLS = len(grid), len(grid[0]) + directions = [(1, 0), (-1, 0), (0, 1), (0, -1)] + + def dfs(r, c, visit): + if min(r, c) < 0 or r == ROWS or c == COLS or grid[r][c] == 0 or (r, c) in visit: + return 0 + + visit.add((r, c)) + res = grid[r][c] + + for dr, dc in directions: + res = max(res, grid[r][c] + dfs(r + dr, c + dc, visit)) + + visit.remove((r, c)) + return res + + res = 0 + for r in range(ROWS): + for c in range(COLS): + if grid[r][c] != 0: + res = max(res, dfs(r, c, set())) + return res +``` + +```java +public class Solution { + private int ROWS, COLS; + private int[][] directions = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}}; + + public int getMaximumGold(int[][] grid) { + ROWS = grid.length; + COLS = grid[0].length; + int res = 0; + + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + if (grid[r][c] != 0) { + res = Math.max(res, dfs(grid, r, c, new boolean[ROWS][COLS])); + } + } + } + return res; + } + + private int dfs(int[][] grid, int r, int c, boolean[][] visit) { + if (r < 0 || c < 0 || r >= ROWS || c >= COLS || + grid[r][c] == 0 || visit[r][c]) { + return 0; + } + + visit[r][c] = true; + int res = grid[r][c]; + + for (int[] d : directions) { + res = Math.max(res, grid[r][c] + dfs(grid, r + d[0], c + d[1], visit)); + } + + visit[r][c] = false; + return res; + } +} +``` + +```cpp +class Solution { +public: + int ROWS, COLS; + vector> directions = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}}; + + int getMaximumGold(vector>& grid) { + ROWS = grid.size(); + COLS = grid[0].size(); + int res = 0; + + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + if (grid[r][c] != 0) { + vector> visit(ROWS, vector(COLS, false)); + res = max(res, dfs(grid, r, c, visit)); + } + } + } + return res; + } + +private: + int dfs(vector>& grid, int r, int c, vector>& visit) { + if (r < 0 || c < 0 || r >= ROWS || c >= COLS || grid[r][c] == 0 || visit[r][c]) { + return 0; + } + + visit[r][c] = true; + int res = grid[r][c]; + + for (auto& d : directions) { + res = max(res, grid[r][c] + dfs(grid, r + d[0], c + d[1], visit)); + } + + visit[r][c] = false; + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} grid + * @return {number} + */ + getMaximumGold(grid) { + const ROWS = grid.length, COLS = grid[0].length; + const directions = [[1, 0], [-1, 0], [0, 1], [0, -1]]; + + const dfs = (r, c, visit) => { + if (r < 0 || c < 0 || r >= ROWS || c >= COLS || grid[r][c] === 0 || visit[r][c]) { + return 0; + } + + visit[r][c] = true; + let res = grid[r][c]; + + for (const [dr, dc] of directions) { + res = Math.max(res, grid[r][c] + dfs(r + dr, c + dc, visit)); + } + + visit[r][c] = false; + return res; + }; + + let res = 0; + for (let r = 0; r < ROWS; r++) { + for (let c = 0; c < COLS; c++) { + if (grid[r][c] !== 0) { + let visit = Array.from({ length: ROWS }, () => Array(COLS).fill(false)); + res = Math.max(res, dfs(r, c, visit)); + } + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(N * 3 ^ N)$ +* Space complexity: $O(N)$ + +> Where $N$ is the number of cells which contain gold. + +--- + +## 2. Backtracking (DFS) - II + +::tabs-start + +```python +class Solution: + def getMaximumGold(self, grid: list[list[int]]) -> int: + ROWS, COLS = len(grid), len(grid[0]) + directions = [(1, 0), (-1, 0), (0, 1), (0, -1)] + + def dfs(r, c): + if min(r, c) < 0 or r == ROWS or c == COLS or grid[r][c] == 0: + return 0 + + gold = grid[r][c] + grid[r][c] = 0 + res = 0 + + for dr, dc in directions: + res = max(res, dfs(r + dr, c + dc)) + + grid[r][c] = gold + return gold + res + + res = 0 + for r in range(ROWS): + for c in range(COLS): + if grid[r][c] != 0: + res = max(res, dfs(r, c)) + return res +``` + +```java +public class Solution { + private int ROWS, COLS; + private int[][] directions = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}}; + + public int getMaximumGold(int[][] grid) { + ROWS = grid.length; + COLS = grid[0].length; + int res = 0; + + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + if (grid[r][c] != 0) { + res = Math.max(res, dfs(grid, r, c)); + } + } + } + return res; + } + + private int dfs(int[][] grid, int r, int c) { + if (r < 0 || c < 0 || r >= ROWS || c >= COLS || grid[r][c] == 0) { + return 0; + } + + int gold = grid[r][c]; + grid[r][c] = 0; + int res = 0; + + for (int[] d : directions) { + res = Math.max(res, dfs(grid, r + d[0], c + d[1])); + } + + grid[r][c] = gold; + return gold + res; + } +} +``` + +```cpp +class Solution { +public: + int ROWS, COLS; + vector> directions = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}}; + + int getMaximumGold(vector>& grid) { + ROWS = grid.size(); + COLS = grid[0].size(); + int res = 0; + + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + if (grid[r][c] != 0) { + res = max(res, dfs(grid, r, c)); + } + } + } + return res; + } + +private: + int dfs(vector>& grid, int r, int c) { + if (r < 0 || c < 0 || r >= ROWS || c >= COLS || grid[r][c] == 0) { + return 0; + } + + int gold = grid[r][c]; + grid[r][c] = 0; + int res = 0; + + for (auto& d : directions) { + res = max(res, dfs(grid, r + d[0], c + d[1])); + } + + grid[r][c] = gold; + return gold + res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} grid + * @return {number} + */ + getMaximumGold(grid) { + const ROWS = grid.length, COLS = grid[0].length; + const directions = [[1, 0], [-1, 0], [0, 1], [0, -1]]; + + const dfs = (r, c) => { + if (r < 0 || c < 0 || r >= ROWS || c >= COLS || grid[r][c] === 0) { + return 0; + } + + let gold = grid[r][c]; + grid[r][c] = 0; + let res = 0; + + for (const [dr, dc] of directions) { + res = Math.max(res, dfs(r + dr, c + dc)); + } + + grid[r][c] = gold; + return gold + res; + }; + + let res = 0; + for (let r = 0; r < ROWS; r++) { + for (let c = 0; c < COLS; c++) { + if (grid[r][c] !== 0) { + res = Math.max(res, dfs(r, c)); + } + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(N * 3 ^ N)$ +* Space complexity: $O(N)$ for recursion stack. + +> Where $N$ is the number of cells which contain gold. + +--- + +## 3. Backtracking (BFS) + +::tabs-start + +```python +class Solution: + def getMaximumGold(self, grid: list[list[int]]) -> int: + ROWS, COLS = len(grid), len(grid[0]) + directions = [1, 0, -1, 0, 1] + index = [[0] * COLS for _ in range(ROWS)] + idx = 0 + + for r in range(ROWS): + for c in range(COLS): + if grid[r][c] != 0: + index[r][c] = idx + idx += 1 + + res = 0 + for r in range(ROWS): + for c in range(COLS): + if grid[r][c] > 0: + q = deque([(r, c, grid[r][c], 1 << index[r][c])]) + while q: + row, col, gold, mask = q.popleft() + res = max(res, gold) + + for i in range(4): + nr, nc = row + directions[i], col + directions[i + 1] + if 0 <= nr < ROWS and 0 <= nc < COLS and grid[nr][nc] > 0: + idx = index[nr][nc] + if not (mask & (1 << idx)): + q.append((nr, nc, gold + grid[nr][nc], mask | (1 << idx))) + + return res +``` + +```java +public class Solution { + public int getMaximumGold(int[][] grid) { + int ROWS = grid.length, COLS = grid[0].length; + int[][] index = new int[ROWS][COLS]; + int idx = 0; + int[] directions = {1, 0, -1, 0, 1}; + + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + if (grid[r][c] != 0) { + index[r][c] = idx++; + } + } + } + + int res = 0; + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + if (grid[r][c] > 0) { + Queue q = new LinkedList<>(); + q.offer(new int[]{r, c, grid[r][c], 1 << index[r][c]}); + + while (!q.isEmpty()) { + int[] cur = q.poll(); + int row = cur[0], col = cur[1], gold = cur[2], mask = cur[3]; + res = Math.max(res, gold); + + for (int i = 0; i < 4; i++) { + int nr = row + directions[i], nc = col + directions[i + 1]; + if (nr >= 0 && nr < ROWS && nc >= 0 && nc < COLS && grid[nr][nc] > 0) { + int newIdx = index[nr][nc]; + if ((mask & (1 << newIdx)) == 0) { + q.offer(new int[]{nr, nc, gold + grid[nr][nc], mask | (1 << newIdx)}); + } + } + } + } + } + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int getMaximumGold(vector>& grid) { + int ROWS = grid.size(), COLS = grid[0].size(); + vector> index(ROWS, vector(COLS, 0)); + int idx = 0; + int directions[] = {1, 0, -1, 0, 1}; + + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + if (grid[r][c] != 0) { + index[r][c] = idx++; + } + } + } + + int res = 0; + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + if (grid[r][c] > 0) { + queue> q; + q.push({r, c, grid[r][c], 1 << index[r][c]}); + + while (!q.empty()) { + auto [row, col, gold, mask] = q.front();q.pop(); + res = max(res, gold); + for (int i = 0; i < 4; i++) { + int nr = row + directions[i], nc = col + directions[i + 1]; + if (nr >= 0 && nr < ROWS && nc >= 0 && nc < COLS && grid[nr][nc] > 0) { + int newIdx = index[nr][nc]; + if ((mask & (1 << newIdx)) == 0) { + q.push({nr, nc, gold + grid[nr][nc], mask | (1 << newIdx)}); + } + } + } + } + } + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} grid + * @return {number} + */ + getMaximumGold(grid) { + const ROWS = grid.length, COLS = grid[0].length; + const index = Array.from({ length: ROWS }, () => Array(COLS).fill(0)); + let idx = 0; + const directions = [1, 0, -1, 0, 1]; + + for (let r = 0; r < ROWS; r++) { + for (let c = 0; c < COLS; c++) { + if (grid[r][c] !== 0) { + index[r][c] = idx++; + } + } + } + + let res = 0; + for (let r = 0; r < ROWS; r++) { + for (let c = 0; c < COLS; c++) { + if (grid[r][c] > 0) { + const q = new Queue([[r, c, grid[r][c], 1 << index[r][c]]]); + + while (!q.isEmpty()) { + const [row, col, gold, mask] = q.pop(); + res = Math.max(res, gold); + for (let i = 0; i < 4; i++) { + const nr = row + directions[i], nc = col + directions[i + 1]; + if (nr >= 0 && nr < ROWS && nc >= 0 && nc < COLS && grid[nr][nc] > 0) { + const newIdx = index[nr][nc]; + if (!(mask & (1 << newIdx))) { + q.push([nr, nc, gold + grid[nr][nc], mask | (1 << newIdx)]); + } + } + } + } + } + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(N * 3 ^ N)$ +* Space complexity: $O(N)$ + +> Where $N$ is the number of cells which contain gold. \ No newline at end of file diff --git a/articles/path-with-maximum-probability.md b/articles/path-with-maximum-probability.md new file mode 100644 index 000000000..b16d1e43b --- /dev/null +++ b/articles/path-with-maximum-probability.md @@ -0,0 +1,632 @@ +## 1. Dijkstra's Algorithm - I + +::tabs-start + +```python +class Solution: + def maxProbability(self, n: int, edges: List[List[int]], succProb: List[float], start_node: int, end_node: int) -> float: + adj = collections.defaultdict(list) + for i in range(len(edges)): + src, dst = edges[i] + adj[src].append((dst, succProb[i])) + adj[dst].append((src, succProb[i])) + + pq = [(-1, start_node)] + visit = set() + + while pq: + prob, cur = heapq.heappop(pq) + visit.add(cur) + + if cur == end_node: + return -prob + + for nei, edgeProb in adj[cur]: + if nei not in visit: + heapq.heappush(pq, (prob * edgeProb, nei)) + + return 0.0 +``` + +```java +public class Solution { + public double maxProbability(int n, int[][] edges, double[] succProb, int start_node, int end_node) { + List[] adj = new ArrayList[n]; + for (int i = 0; i < n; i++) adj[i] = new ArrayList<>(); + + for (int i = 0; i < edges.length; i++) { + int src = edges[i][0], dst = edges[i][1]; + adj[src].add(new Pair(dst, succProb[i])); + adj[dst].add(new Pair(src, succProb[i])); + } + + double[] maxProb = new double[n]; + maxProb[start_node] = 1.0; + PriorityQueue pq = new PriorityQueue<>((a, b) -> Double.compare(b.prob, a.prob)); + pq.offer(new Pair(start_node, 1.0)); + + while (!pq.isEmpty()) { + Pair top = pq.poll(); + int node = top.node; + double curr_prob = top.prob; + + if (node == end_node) return curr_prob; + if (curr_prob > maxProb[node]) continue; + + for (Pair nei : adj[node]) { + double new_prob = curr_prob * nei.prob; + if (new_prob > maxProb[nei.node]) { + maxProb[nei.node] = new_prob; + pq.offer(new Pair(nei.node, new_prob)); + } + } + } + + return 0.0; + } + + static class Pair { + int node; + double prob; + + Pair(int node, double prob) { + this.node = node; + this.prob = prob; + } + } +} +``` + +```cpp +class Solution { +public: + double maxProbability(int n, vector>& edges, vector& succProb, int start_node, int end_node) { + vector>> adj(n); + for (int i = 0; i < edges.size(); i++) { + int src = edges[i][0], dst = edges[i][1]; + adj[src].emplace_back(dst, succProb[i]); + adj[dst].emplace_back(src, succProb[i]); + } + + vector maxProb(n, 0.0); + maxProb[start_node] = 1.0; + priority_queue> pq; + pq.emplace(1.0, start_node); + + while (!pq.empty()) { + auto [curr_prob, node] = pq.top(); pq.pop(); + + if (node == end_node) return curr_prob; + if (curr_prob > maxProb[node]) continue; + + for (auto& [nei, edge_prob] : adj[node]) { + double new_prob = curr_prob * edge_prob; + if (new_prob > maxProb[nei]) { + maxProb[nei] = new_prob; + pq.emplace(new_prob, nei); + } + } + } + + return 0.0; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number[][]} edges + * @param {number[]} succProb + * @param {number} start_node + * @param {number} end_node + * @return {number} + */ + maxProbability(n, edges, succProb, start_node, end_node) { + let adj = new Map(); + for (let i = 0; i < n; i++) adj.set(i, []); + + for (let i = 0; i < edges.length; i++) { + let [src, dst] = edges[i]; + adj.get(src).push([dst, succProb[i]]); + adj.get(dst).push([src, succProb[i]]); + } + + let pq = new MaxPriorityQueue({ priority: x => x[0] }); + pq.enqueue([1.0, start_node]); + let visited = new Set(); + + while (!pq.isEmpty()) { + let [prob, cur] = pq.dequeue().element; + visited.add(cur); + + if (cur === end_node) return prob; + + for (let [nei, edgeProb] of adj.get(cur)) { + if (!visited.has(nei)) { + pq.enqueue([prob * edgeProb, nei]); + } + } + } + + return 0.0; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O((V + E) \log V)$ +* Space complexity: $O(V + E)$ + +> Where $V$ is the number nodes and $E$ is the number of edges. + +--- + +## 2. Dijkstra's Algorithm - II + +::tabs-start + +```python +class Solution: + def maxProbability(self, n: int, edges: List[List[int]], succProb: List[float], start_node: int, end_node: int) -> float: + adj = [[] for _ in range(n)] + for i in range(len(edges)): + src, dst = edges[i] + adj[src].append((dst, succProb[i])) + adj[dst].append((src, succProb[i])) + + maxProb = [0] * n + maxProb[start_node] = 1.0 + pq = [(-1.0, start_node)] + + while pq: + curr_prob, node = heapq.heappop(pq) + curr_prob *= -1 + + if node == end_node: + return curr_prob + if curr_prob > maxProb[node]: + continue + + for nei, edge_prob in adj[node]: + new_prob = curr_prob * edge_prob + if new_prob > maxProb[nei]: + maxProb[nei] = new_prob + heapq.heappush(pq, (-new_prob, nei)) + + return 0.0 +``` + +```java +public class Solution { + public double maxProbability(int n, int[][] edges, double[] succProb, int start_node, int end_node) { + List[] adj = new ArrayList[n]; + for (int i = 0; i < n; i++) adj[i] = new ArrayList<>(); + + for (int i = 0; i < edges.length; i++) { + int src = edges[i][0], dst = edges[i][1]; + adj[src].add(new Pair(dst, succProb[i])); + adj[dst].add(new Pair(src, succProb[i])); + } + + double[] maxProb = new double[n]; + maxProb[start_node] = 1.0; + PriorityQueue pq = new PriorityQueue<>((a, b) -> Double.compare(b.prob, a.prob)); + pq.offer(new Pair(start_node, 1.0)); + + while (!pq.isEmpty()) { + Pair top = pq.poll(); + int node = top.node; + double curr_prob = top.prob; + + if (node == end_node) return curr_prob; + if (curr_prob > maxProb[node]) continue; + + for (Pair nei : adj[node]) { + double new_prob = curr_prob * nei.prob; + if (new_prob > maxProb[nei.node]) { + maxProb[nei.node] = new_prob; + pq.offer(new Pair(nei.node, new_prob)); + } + } + } + + return 0.0; + } + + static class Pair { + int node; + double prob; + + Pair(int node, double prob) { + this.node = node; + this.prob = prob; + } + } +} +``` + +```cpp +class Solution { +public: + double maxProbability(int n, vector>& edges, vector& succProb, int start_node, int end_node) { + vector>> adj(n); + for (int i = 0; i < edges.size(); i++) { + int src = edges[i][0], dst = edges[i][1]; + adj[src].emplace_back(dst, succProb[i]); + adj[dst].emplace_back(src, succProb[i]); + } + + vector maxProb(n, 0.0); + maxProb[start_node] = 1.0; + priority_queue> pq; + pq.emplace(1.0, start_node); + + while (!pq.empty()) { + auto [curr_prob, node] = pq.top(); pq.pop(); + + if (node == end_node) return curr_prob; + if (curr_prob > maxProb[node]) continue; + + for (auto& [nei, edge_prob] : adj[node]) { + double new_prob = curr_prob * edge_prob; + if (new_prob > maxProb[nei]) { + maxProb[nei] = new_prob; + pq.emplace(new_prob, nei); + } + } + } + + return 0.0; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number[][]} edges + * @param {number[]} succProb + * @param {number} start_node + * @param {number} end_node + * @return {number} + */ + maxProbability(n, edges, succProb, start_node, end_node) { + let adj = Array.from({ length: n }, () => []); + for (let i = 0; i < edges.length; i++) { + let [src, dst] = edges[i]; + adj[src].push([dst, succProb[i]]); + adj[dst].push([src, succProb[i]]); + } + + let maxProb = Array(n).fill(0); + maxProb[start_node] = 1.0; + let pq = new MaxPriorityQueue({ priority: x => x[1] }); + pq.enqueue([start_node, 1.0]); + + while (!pq.isEmpty()) { + let [node, curr_prob] = pq.dequeue().element; + + if (node === end_node) return curr_prob; + if (curr_prob > maxProb[node]) continue; + + for (let [nei, edge_prob] of adj[node]) { + let new_prob = curr_prob * edge_prob; + if (new_prob > maxProb[nei]) { + maxProb[nei] = new_prob; + pq.enqueue([nei, new_prob]); + } + } + } + + return 0.0; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O((V + E) \log V)$ +* Space complexity: $O(V + E)$ + +> Where $V$ is the number nodes and $E$ is the number of edges. + +--- + +## 3. Bellman Ford Algorithm + +::tabs-start + +```python +class Solution: + def maxProbability(self, n: int, edges: List[List[int]], succProb: List[float], start_node: int, end_node: int) -> float: + maxProb = [0.0] * n + maxProb[start_node] = 1.0 + + for i in range(n): + updated = False + for j in range(len(edges)): + src, dst = edges[j] + if maxProb[src] * succProb[j] > maxProb[dst]: + maxProb[dst] = maxProb[src] * succProb[j] + updated = True + + if maxProb[dst] * succProb[j] > maxProb[src]: + maxProb[src] = maxProb[dst] * succProb[j] + updated = True + + if not updated: + break + + return maxProb[end_node] +``` + +```java +public class Solution { + public double maxProbability(int n, int[][] edges, double[] succProb, int start_node, int end_node) { + double[] maxProb = new double[n]; + maxProb[start_node] = 1.0; + + for (int i = 0; i < n; i++) { + boolean updated = false; + for (int j = 0; j < edges.length; j++) { + int src = edges[j][0], dst = edges[j][1]; + + if (maxProb[src] * succProb[j] > maxProb[dst]) { + maxProb[dst] = maxProb[src] * succProb[j]; + updated = true; + } + + if (maxProb[dst] * succProb[j] > maxProb[src]) { + maxProb[src] = maxProb[dst] * succProb[j]; + updated = true; + } + } + if (!updated) break; + } + + return maxProb[end_node]; + } +} +``` + +```cpp +class Solution { +public: + double maxProbability(int n, vector>& edges, vector& succProb, int start_node, int end_node) { + vector maxProb(n, 0.0); + maxProb[start_node] = 1.0; + + for (int i = 0; i < n; i++) { + bool updated = false; + for (int j = 0; j < edges.size(); j++) { + int src = edges[j][0], dst = edges[j][1]; + + if (maxProb[src] * succProb[j] > maxProb[dst]) { + maxProb[dst] = maxProb[src] * succProb[j]; + updated = true; + } + + if (maxProb[dst] * succProb[j] > maxProb[src]) { + maxProb[src] = maxProb[dst] * succProb[j]; + updated = true; + } + } + if (!updated) break; + } + + return maxProb[end_node]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number[][]} edges + * @param {number[]} succProb + * @param {number} start_node + * @param {number} end_node + * @return {number} + */ + maxProbability(n, edges, succProb, start_node, end_node) { + let maxProb = new Array(n).fill(0.0); + maxProb[start_node] = 1.0; + + for (let i = 0; i < n; i++) { + let updated = false; + for (let j = 0; j < edges.length; j++) { + let [src, dst] = edges[j]; + + if (maxProb[src] * succProb[j] > maxProb[dst]) { + maxProb[dst] = maxProb[src] * succProb[j]; + updated = true; + } + + if (maxProb[dst] * succProb[j] > maxProb[src]) { + maxProb[src] = maxProb[dst] * succProb[j]; + updated = true; + } + } + if (!updated) break; + } + + return maxProb[end_node]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(V * E)$ +* Space complexity: $O(V)$ + +> Where $V$ is the number nodes and $E$ is the number of edges. + +--- + +## 4. Shortest Path Faster Algorithm + +::tabs-start + +```python +class Solution: + def maxProbability(self, n: int, edges: List[List[int]], succProb: List[float], start_node: int, end_node: int) -> float: + adj = [[] for _ in range(n)] + for i in range(len(edges)): + src, dst = edges[i] + adj[src].append((dst, succProb[i])) + adj[dst].append((src, succProb[i])) + + maxProb = [0.0] * n + maxProb[start_node] = 1.0 + q = deque([start_node]) + + while q: + node = q.popleft() + + for nei, edge_prob in adj[node]: + new_prob = maxProb[node] * edge_prob + if new_prob > maxProb[nei]: + maxProb[nei] = new_prob + q.append(nei) + + return maxProb[end_node] +``` + +```java +public class Solution { + public double maxProbability(int n, int[][] edges, double[] succProb, int start_node, int end_node) { + List[] adj = new ArrayList[n]; + for (int i = 0; i < n; i++) adj[i] = new ArrayList<>(); + + for (int i = 0; i < edges.length; i++) { + int src = edges[i][0], dst = edges[i][1]; + adj[src].add(new Pair(dst, succProb[i])); + adj[dst].add(new Pair(src, succProb[i])); + } + + double[] maxProb = new double[n]; + maxProb[start_node] = 1.0; + Queue q = new LinkedList<>(); + q.offer(start_node); + + while (!q.isEmpty()) { + int node = q.poll(); + + for (Pair nei : adj[node]) { + double newProb = maxProb[node] * nei.prob; + if (newProb > maxProb[nei.node]) { + maxProb[nei.node] = newProb; + q.offer(nei.node); + } + } + } + + return maxProb[end_node]; + } + + static class Pair { + int node; + double prob; + + Pair(int node, double prob) { + this.node = node; + this.prob = prob; + } + } +} +``` + +```cpp +class Solution { +public: + double maxProbability(int n, vector>& edges, vector& succProb, int start_node, int end_node) { + vector>> adj(n); + + for (int i = 0; i < edges.size(); i++) { + int src = edges[i][0], dst = edges[i][1]; + adj[src].emplace_back(dst, succProb[i]); + adj[dst].emplace_back(src, succProb[i]); + } + + vector maxProb(n, 0.0); + maxProb[start_node] = 1.0; + queue q; + q.push(start_node); + + while (!q.empty()) { + int node = q.front(); + q.pop(); + + for (auto& [nei, edgeProb] : adj[node]) { + double newProb = maxProb[node] * edgeProb; + if (newProb > maxProb[nei]) { + maxProb[nei] = newProb; + q.push(nei); + } + } + } + + return maxProb[end_node]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number[][]} edges + * @param {number[]} succProb + * @param {number} start_node + * @param {number} end_node + * @return {number} + */ + maxProbability(n, edges, succProb, start_node, end_node) { + let adj = Array.from({ length: n }, () => []); + for (let i = 0; i < edges.length; i++) { + let [src, dst] = edges[i]; + adj[src].push([dst, succProb[i]]); + adj[dst].push([src, succProb[i]]); + } + + let maxProb = new Array(n).fill(0.0); + maxProb[start_node] = 1.0; + const q = new Queue([start_node]); + + while (!q.isEmpty()) { + let node = q.pop(); + + for (let [nei, edgeProb] of adj[node]) { + let newProb = maxProb[node] * edgeProb; + if (newProb > maxProb[nei]) { + maxProb[nei] = newProb; + q.push(nei); + } + } + } + + return maxProb[end_node]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(V * E)$ +* Space complexity: $O(V + E)$ + +> Where $V$ is the number nodes and $E$ is the number of edges. \ No newline at end of file diff --git a/articles/remove-colored-pieces-if-both-neighbors-are-the-same-color.md b/articles/remove-colored-pieces-if-both-neighbors-are-the-same-color.md new file mode 100644 index 000000000..156cbb636 --- /dev/null +++ b/articles/remove-colored-pieces-if-both-neighbors-are-the-same-color.md @@ -0,0 +1,322 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def winnerOfGame(self, colors: str) -> bool: + s = list(colors) + + def removeChar(c): + for i in range(1, len(s) - 1): + if s[i] != c: + continue + + if s[i] == s[i + 1] == s[i - 1]: + s.pop(i) + return True + return False + + while True: + if not removeChar('A'): + return False + if not removeChar('B'): + return True + + return False +``` + +```java +public class Solution { + public boolean winnerOfGame(String colors) { + StringBuilder s = new StringBuilder(colors); + + while (true) { + if (!removeChar(s, 'A')) return false; + if (!removeChar(s, 'B')) return true; + } + } + + private boolean removeChar(StringBuilder s, char c) { + for (int i = 1; i < s.length() - 1; i++) { + if (s.charAt(i) != c) continue; + + if (s.charAt(i - 1) == c && s.charAt(i + 1) == c) { + s.deleteCharAt(i); + return true; + } + } + return false; + } +} +``` + +```cpp +class Solution { +public: + bool winnerOfGame(string colors) { + while (true) { + if (!removeChar(colors, 'A')) return false; + if (!removeChar(colors, 'B')) return true; + } + } + +private: + bool removeChar(string& s, char c) { + for (int i = 1; i < s.size() - 1; i++) { + if (s[i] != c) continue; + + if (s[i - 1] == c && s[i + 1] == c) { + s.erase(i, 1); + return true; + } + } + return false; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} colors + * @return {boolean} + */ + winnerOfGame(colors) { + let s = colors.split(""); + + const removeChar = (c) => { + for (let i = 1; i < s.length - 1; i++) { + if (s[i] !== c) continue; + + if (s[i - 1] === c && s[i + 1] === c) { + s.splice(i, 1); + return true; + } + } + return false; + }; + + while (true) { + if (!removeChar('A')) return false; + if (!removeChar('B')) return true; + } + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n)$ + +--- + +## 2. Greedy + Two Pointers + +::tabs-start + +```python +class Solution: + def winnerOfGame(self, colors: str) -> bool: + alice = bob = l = 0 + + for r in range(len(colors)): + if colors[l] != colors[r]: + l = r + + extra = r - l - 1 + if extra > 0: + if colors[l] == 'A': + alice += 1 + else: + bob += 1 + + return alice > bob +``` + +```java +public class Solution { + public boolean winnerOfGame(String colors) { + int alice = 0, bob = 0, l = 0; + + for (int r = 0; r < colors.length(); r++) { + if (colors.charAt(l) != colors.charAt(r)) { + l = r; + } + + int extra = r - l - 1; + if (extra > 0) { + if (colors.charAt(l) == 'A') { + alice++; + } else { + bob++; + } + } + } + + return alice > bob; + } +} +``` + +```cpp +class Solution { +public: + bool winnerOfGame(string colors) { + int alice = 0, bob = 0, l = 0; + + for (int r = 0; r < colors.size(); r++) { + if (colors[l] != colors[r]) { + l = r; + } + + int extra = r - l - 1; + if (extra > 0) { + if (colors[l] == 'A') { + alice++; + } else { + bob++; + } + } + } + + return alice > bob; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} colors + * @return {boolean} + */ + winnerOfGame(colors) { + let alice = 0, bob = 0, l = 0; + + for (let r = 0; r < colors.length; r++) { + if (colors[l] !== colors[r]) { + l = r; + } + + let extra = r - l - 1; + if (extra > 0) { + if (colors[l] === 'A') { + alice++; + } else { + bob++; + } + } + } + + return alice > bob; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. + +--- + +## 3. Greedy + +::tabs-start + +```python +class Solution: + def winnerOfGame(self, colors: str) -> bool: + alice, bob = 0, 0 + + for i in range(1, len(colors) - 1): + if colors[i - 1] == colors[i] == colors[i + 1]: + if colors[i] == 'A': + alice += 1 + if colors[i] == 'B': + bob += 1 + + return alice > bob +``` + +```java +public class Solution { + public boolean winnerOfGame(String colors) { + int alice = 0, bob = 0; + + for (int i = 1; i < colors.length() - 1; i++) { + if (colors.charAt(i - 1) == colors.charAt(i) && + colors.charAt(i) == colors.charAt(i + 1)) { + if (colors.charAt(i) == 'A') { + alice++; + } + if (colors.charAt(i) == 'B') { + bob++; + } + } + } + + return alice > bob; + } +} +``` + +```cpp +class Solution { +public: + bool winnerOfGame(string colors) { + int alice = 0, bob = 0; + + for (int i = 1; i < colors.size() - 1; i++) { + if (colors[i - 1] == colors[i] && colors[i] == colors[i + 1]) { + if (colors[i] == 'A') { + alice++; + } + if (colors[i] == 'B') { + bob++; + } + } + } + + return alice > bob; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} colors + * @return {boolean} + */ + winnerOfGame(colors) { + let alice = 0, bob = 0; + + for (let i = 1; i < colors.length - 1; i++) { + if (colors[i - 1] === colors[i] && colors[i] === colors[i + 1]) { + if (colors[i] === 'A') { + alice++; + } + if (colors[i] === 'B') { + bob++; + } + } + } + + return alice > bob; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. \ No newline at end of file diff --git a/articles/remove-covered-intervals.md b/articles/remove-covered-intervals.md new file mode 100644 index 000000000..f71a0cfcc --- /dev/null +++ b/articles/remove-covered-intervals.md @@ -0,0 +1,275 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def removeCoveredIntervals(self, intervals: List[List[int]]) -> int: + n = len(intervals) + res = n + + for i in range(n): + for j in range(n): + if (i != j and intervals[j][0] <= intervals[i][0] and + intervals[j][1] >= intervals[i][1] + ): + res -= 1 + break + + return res +``` + +```java +public class Solution { + public int removeCoveredIntervals(int[][] intervals) { + int n = intervals.length; + int res = n; + + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { + if (i != j && intervals[j][0] <= intervals[i][0] && + intervals[j][1] >= intervals[i][1]) { + res--; + break; + } + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int removeCoveredIntervals(vector>& intervals) { + int n = intervals.size(); + int res = n; + + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { + if (i != j && intervals[j][0] <= intervals[i][0] && + intervals[j][1] >= intervals[i][1]) { + res--; + break; + } + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} intervals + * @return {number} + */ + removeCoveredIntervals(intervals) { + let n = intervals.length; + let res = n; + + for (let i = 0; i < n; i++) { + for (let j = 0; j < n; j++) { + if (i !== j && intervals[j][0] <= intervals[i][0] && + intervals[j][1] >= intervals[i][1]) { + res--; + break; + } + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(1)$ extra space. + +--- + +## 2. Sorting - I + +::tabs-start + +```python +class Solution: + def removeCoveredIntervals(self, intervals: List[List[int]]) -> int: + intervals.sort(key=lambda x: (x[0], -x[1])) + res = 1 + prevL, prevR = intervals[0][0], intervals[0][1] + for l, r in intervals: + if prevL <= l and prevR >= r: + continue + res += 1 + prevL, prevR = l, r + + return res +``` + +```java +public class Solution { + public int removeCoveredIntervals(int[][] intervals) { + Arrays.sort(intervals, (a, b) -> + a[0] == b[0] ? Integer.compare(b[1], a[1]) : Integer.compare(a[0], b[0]) + ); + int res = 1, prevL = intervals[0][0], prevR = intervals[0][1]; + for (int[] interval : intervals) { + int l = interval[0], r = interval[1]; + if (prevL <= l && prevR >= r) { + continue; + } + res++; + prevL = l; + prevR = r; + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int removeCoveredIntervals(vector>& intervals) { + sort(intervals.begin(), intervals.end(), [](const auto& a, const auto& b) { + return a[0] == b[0] ? b[1] < a[1] : a[0] < b[0]; + }); + + int res = 1, prevL = intervals[0][0], prevR = intervals[0][1]; + for (const auto& interval : intervals) { + int l = interval[0], r = interval[1]; + if (prevL <= l && prevR >= r) { + continue; + } + res++; + prevL = l; + prevR = r; + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} intervals + * @return {number} + */ + removeCoveredIntervals(intervals) { + intervals.sort((a, b) => a[0] === b[0] ? b[1] - a[1] : a[0] - b[0]); + let res = 1, [prevL, prevR] = intervals[0]; + for (const [l, r] of intervals) { + if (prevL <= l && prevR >= r) { + continue; + } + res++; + prevL = l; + prevR = r; + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n\log n)$ +* Space complexity: $O(1)$ or $O(n)$ depending on the sorting algorithm. + +--- + +## 3. Sorting - II + +::tabs-start + +```python +class Solution: + def removeCoveredIntervals(self, intervals: List[List[int]]) -> int: + intervals.sort() + res, start, end = 1, intervals[0][0], intervals[0][1] + + for l, r in intervals: + if start < l and end < r: + start = l + res += 1 + end = max(end, r) + + return res +``` + +```java +public class Solution { + public int removeCoveredIntervals(int[][] intervals) { + Arrays.sort(intervals, (a, b) -> a[0] - b[0]); + int res = 1, start = intervals[0][0], end = intervals[0][1]; + + for (int[] interval : intervals) { + int l = interval[0], r = interval[1]; + if (start < l && end < r) { + start = l; + res++; + } + end = Math.max(end, r); + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int removeCoveredIntervals(vector>& intervals) { + sort(intervals.begin(), intervals.end()); + int res = 1, start = intervals[0][0], end = intervals[0][1]; + + for (const auto& interval : intervals) { + int l = interval[0], r = interval[1]; + if (start < l && end < r) { + start = l; + res++; + } + end = max(end, r); + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} intervals + * @return {number} + */ + removeCoveredIntervals(intervals) { + intervals.sort((a, b) => a[0] - b[0]); + let res = 1, start = intervals[0][0], end = intervals[0][1]; + + for (const [l, r] of intervals) { + if (start < l && end < r) { + start = l; + res++; + } + end = Math.max(end, r); + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(1)$ or $O(n)$ depending on the sorting algorithm. \ No newline at end of file diff --git a/articles/remove-max-number-of-edges-to-keep-graph-fully-traversable.md b/articles/remove-max-number-of-edges-to-keep-graph-fully-traversable.md new file mode 100644 index 000000000..04c3da1ac --- /dev/null +++ b/articles/remove-max-number-of-edges-to-keep-graph-fully-traversable.md @@ -0,0 +1,274 @@ +## 1. Disjoint Set Union + +::tabs-start + +```python +class DSU: + def __init__(self, n): + self.n = n + self.Parent = list(range(n + 1)) + self.Size = [1] * (n + 1) + + def find(self, node): + if self.Parent[node] != node: + self.Parent[node] = self.find(self.Parent[node]) + return self.Parent[node] + + def union(self, u, v): + pu = self.find(u) + pv = self.find(v) + if pu == pv: + return 0 + if self.Size[pu] < self.Size[pv]: + pu, pv = pv, pu + self.Size[pu] += self.Size[pv] + self.Parent[pv] = pu + self.n -= 1 + return 1 + + def isConnected(self): + return self.n == 1 + +class Solution: + def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int: + alice, bob = DSU(n), DSU(n) + cnt = 0 + + for type, src, dst in edges: + if type == 3: + cnt += (alice.union(src, dst) | bob.union(src, dst)) + + for type, src, dst in edges: + if type == 1: + cnt += alice.union(src, dst) + elif type == 2: + cnt += bob.union(src, dst) + + if alice.isConnected() and bob.isConnected(): + return len(edges) - cnt + return -1 +``` + +```java +class DSU { + private int[] parent, size; + private int n; + + public DSU(int n) { + this.n = n; + parent = new int[n + 1]; + size = new int[n + 1]; + for (int i = 0; i <= n; i++) { + parent[i] = i; + size[i] = 1; + } + } + + public int find(int node) { + if (parent[node] != node) { + parent[node] = find(parent[node]); + } + return parent[node]; + } + + public int union(int u, int v) { + int pu = find(u), pv = find(v); + if (pu == pv) { + return 0; + } + if (size[pu] < size[pv]) { + int temp = pu; + pu = pv; + pv = temp; + } + size[pu] += size[pv]; + parent[pv] = pu; + n--; + return 1; + } + + public boolean isConnected() { + return n == 1; + } +} + +public class Solution { + public int maxNumEdgesToRemove(int n, int[][] edges) { + DSU alice = new DSU(n), bob = new DSU(n); + int cnt = 0; + + for (int[] edge : edges) { + if (edge[0] == 3) { + cnt += (alice.union(edge[1], edge[2]) | bob.union(edge[1], edge[2])); + } + } + + for (int[] edge : edges) { + if (edge[0] == 1) { + cnt += alice.union(edge[1], edge[2]); + } else if (edge[0] == 2) { + cnt += bob.union(edge[1], edge[2]); + } + } + + if (alice.isConnected() && bob.isConnected()) { + return edges.length - cnt; + } + return -1; + } +} +``` + +```cpp +class DSU { +private: + vector parent, size; + int n; + +public: + DSU(int n) : n(n), parent(n + 1), size(n + 1, 1) { + for (int i = 0; i <= n; i++) { + parent[i] = i; + } + } + + int find(int node) { + if (parent[node] != node) { + parent[node] = find(parent[node]); + } + return parent[node]; + } + + int unionSets(int u, int v) { + int pu = find(u), pv = find(v); + if (pu == pv) { + return 0; + } + if (size[pu] < size[pv]) { + swap(pu, pv); + } + size[pu] += size[pv]; + parent[pv] = pu; + n--; + return 1; + } + + bool isConnected() { + return n == 1; + } +}; + +class Solution { +public: + int maxNumEdgesToRemove(int n, vector>& edges) { + DSU alice(n), bob(n); + int cnt = 0; + + for (auto& edge : edges) { + if (edge[0] == 3) { + cnt += (alice.unionSets(edge[1], edge[2]) | bob.unionSets(edge[1], edge[2])); + } + } + + for (auto& edge : edges) { + if (edge[0] == 1) { + cnt += alice.unionSets(edge[1], edge[2]); + } else if (edge[0] == 2) { + cnt += bob.unionSets(edge[1], edge[2]); + } + } + + if (alice.isConnected() && bob.isConnected()) { + return edges.size() - cnt; + } + return -1; + } +}; +``` + +```javascript +class DSU { + /** + * @param {number} n + */ + constructor(n) { + this.n = n; + this.parent = Array(n + 1).fill(0).map((_, i) => i); + this.size = Array(n + 1).fill(1); + } + + /** + * @param {number} node + * @return {number} + */ + find(node) { + if (this.parent[node] !== node) { + this.parent[node] = this.find(this.parent[node]); + } + return this.parent[node]; + } + + /** + * @param {number} u + * @param {number} v + * @return {number} + */ + union(u, v) { + let pu = this.find(u), pv = this.find(v); + if (pu === pv) { + return 0; + } + if (this.size[pu] < this.size[pv]) { + [pu, pv] = [pv, pu]; + } + this.size[pu] += this.size[pv]; + this.parent[pv] = pu; + this.n--; + return 1; + } + + /** + * @return {boolean} + */ + isConnected() { + return this.n === 1; + } +} + +class Solution { + /** + * @param {number} n + * @param {number[][]} edges + * @return {number} + */ + maxNumEdgesToRemove(n, edges) { + let alice = new DSU(n), bob = new DSU(n); + let cnt = 0; + + for (let [type, src, dst] of edges) { + if (type === 3) { + cnt += (alice.union(src, dst) | bob.union(src, dst)); + } + } + + for (let [type, src, dst] of edges) { + if (type === 1) { + cnt += alice.union(src, dst); + } else if (type === 2) { + cnt += bob.union(src, dst); + } + } + + return alice.isConnected() && bob.isConnected() ? edges.length - cnt : -1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(E * α(V))$ +* Space complexity: $O(V)$ + +> Where $V$ is the number of verticies and $E$ is the number of edges. \ No newline at end of file diff --git a/articles/reveal-cards-in-increasing-order.md b/articles/reveal-cards-in-increasing-order.md new file mode 100644 index 000000000..4759af658 --- /dev/null +++ b/articles/reveal-cards-in-increasing-order.md @@ -0,0 +1,406 @@ +## 1. Simulation Using Queue - I + +::tabs-start + +```python +class Solution: + def deckRevealedIncreasing(self, deck: List[int]) -> List[int]: + deck.sort() + res = [0] * len(deck) + q = deque(range(len(deck))) + + for num in deck: + i = q.popleft() + res[i] = num + if q: + q.append(q.popleft()) + + return res +``` + +```java +public class Solution { + public int[] deckRevealedIncreasing(int[] deck) { + Arrays.sort(deck); + int n = deck.length; + int[] res = new int[n]; + Queue q = new LinkedList<>(); + + for (int i = 0; i < n; i++) { + q.offer(i); + } + + for (int num : deck) { + int i = q.poll(); + res[i] = num; + if (!q.isEmpty()) { + q.offer(q.poll()); + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector deckRevealedIncreasing(vector& deck) { + sort(deck.begin(), deck.end()); + int n = deck.size(); + vector res(n); + queue q; + + for (int i = 0; i < n; i++) { + q.push(i); + } + + for (int num : deck) { + int i = q.front(); + q.pop(); + res[i] = num; + if (!q.empty()) { + q.push(q.front()); + q.pop(); + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} deck + * @return {number[]} + */ + deckRevealedIncreasing(deck) { + deck.sort((a, b) => a - b); + let n = deck.length; + let res = new Array(n).fill(0); + const q = new Queue(); + + for (let i = 0; i < n; i++) { + q.push(i); + } + + for (let num of deck) { + let i = q.pop(); + res[i] = num; + if (!q.isEmpty()) { + q.push(q.pop()); + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Simuation Using Queue - II + +::tabs-start + +```python +class Solution: + def deckRevealedIncreasing(self, deck: List[int]) -> List[int]: + deck.sort() + q = deque() + for i in range(len(deck) - 1, -1, -1): + if q: + q.append(q.popleft()) + q.append(deck[i]) + return list(q)[::-1] +``` + +```java +public class Solution { + public int[] deckRevealedIncreasing(int[] deck) { + Arrays.sort(deck); + Queue q = new LinkedList<>(); + + for (int i = deck.length - 1; i >= 0; i--) { + if (!q.isEmpty()) { + q.offer(q.poll()); + } + q.offer(deck[i]); + } + + int[] res = new int[deck.length]; + for (int i = deck.length - 1; i >= 0; i--) { + res[i] = q.poll(); + } + return res; + } +} +``` + +```cpp +class Solution { +public: + vector deckRevealedIncreasing(vector& deck) { + sort(deck.begin(), deck.end()); + queue q; + + for (int i = deck.size() - 1; i >= 0; i--) { + if (!q.empty()) { + q.push(q.front()); + q.pop(); + } + q.push(deck[i]); + } + + vector res(deck.size()); + for (int i = deck.size() - 1; i >= 0; i--) { + res[i] = q.front(); + q.pop(); + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} deck + * @return {number[]} + */ + deckRevealedIncreasing(deck) { + deck.sort((a, b) => a - b); + const q = new Queue(); + + for (let i = deck.length - 1; i >= 0; i--) { + if (!q.isEmpty()) { + q.push(q.pop()); + } + q.push(deck[i]); + } + + return q.toArray().reverse(); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Simulation Using Deque + +::tabs-start + +```python +class Solution: + def deckRevealedIncreasing(self, deck: List[int]) -> List[int]: + deck.sort() + dq = deque() + dq.append(deck.pop()) + for i in range(len(deck) - 1, -1, -1): + dq.appendleft(dq.pop()) + dq.appendleft(deck[i]) + return list(dq) +``` + +```java +public class Solution { + public int[] deckRevealedIncreasing(int[] deck) { + int n = deck.length; + Arrays.sort(deck); + Deque dq = new LinkedList<>(); + dq.addLast(deck[n - 1]); + + for (int i = n - 2; i >= 0; i--) { + dq.addFirst(dq.removeLast()); + dq.addFirst(deck[i]); + } + + return dq.stream().mapToInt(Integer::intValue).toArray(); + } +} +``` + +```cpp +class Solution { +public: + vector deckRevealedIncreasing(vector& deck) { + sort(deck.begin(), deck.end()); + deque dq; + dq.push_back(deck.back()); + + for (int i = deck.size() - 2; i >= 0; i--) { + dq.push_front(dq.back()); + dq.pop_back(); + dq.push_front(deck[i]); + } + + return vector(dq.begin(), dq.end()); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} deck + * @return {number[]} + */ + deckRevealedIncreasing(deck) { + deck.sort((a, b) => a - b); + const dq = new Deque([deck.pop()]); + + for (let i = deck.length - 1; i >= 0; i--) { + let val = dq.popBack(); + dq.pushFront(val); + dq.pushFront(deck[i]); + } + + let res = []; + while (!dq.isEmpty()) { + res.push(dq.popFront()); + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ + +--- + +## 4. Simulation Using Two Pointers + +::tabs-start + +```python +class Solution: + def deckRevealedIncreasing(self, deck: List[int]) -> List[int]: + n = len(deck) + res = [0] * n + skip = False + deckIndex, i = 0, 0 + + deck.sort() + + while deckIndex < n: + if res[i] == 0: + if not skip: + res[i] = deck[deckIndex] + deckIndex += 1 + skip = not skip + i = (i + 1) % n + + return res +``` + +```java +public class Solution { + public int[] deckRevealedIncreasing(int[] deck) { + int n = deck.length; + int[] res = new int[n]; + Arrays.fill(res, 0); + boolean skip = false; + int deckIndex = 0, i = 0; + + Arrays.sort(deck); + + while (deckIndex < n) { + if (res[i] == 0) { + if (!skip) { + res[i] = deck[deckIndex++]; + } + skip = !skip; + } + i = (i + 1) % n; + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector deckRevealedIncreasing(vector& deck) { + int n = deck.size(); + vector res(n, 0); + bool skip = false; + int deckIndex = 0, i = 0; + + sort(deck.begin(), deck.end()); + + while (deckIndex < n) { + if (res[i] == 0) { + if (!skip) { + res[i] = deck[deckIndex++]; + } + skip = !skip; + } + i = (i + 1) % n; + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} deck + * @return {number[]} + */ + deckRevealedIncreasing(deck) { + let n = deck.length; + let res = new Array(n).fill(0); + let skip = false; + let deckIndex = 0, i = 0; + + deck.sort((a, b) => a - b); + + while (deckIndex < n) { + if (res[i] === 0) { + if (!skip) { + res[i] = deck[deckIndex++]; + } + skip = !skip; + } + i = (i + 1) % n; + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: + * $O(1)$ or $O(n)$ space depending on the sorting algorithm. + * $O(n)$ space for the output array. \ No newline at end of file diff --git a/articles/rotate-list.md b/articles/rotate-list.md new file mode 100644 index 000000000..675821f82 --- /dev/null +++ b/articles/rotate-list.md @@ -0,0 +1,486 @@ +## 1. Convert To Array + +::tabs-start + +```python +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, val=0, next=None): +# self.val = val +# self.next = next +class Solution: + def rotateRight(self, head: Optional[ListNode], k: int) -> Optional[ListNode]: + if not head: + return None + + arr, cur = [], head + while cur: + arr.append(cur.val) + cur = cur.next + + n = len(arr) + k %= n + cur = head + for i in range(n - k, n): + cur.val = arr[i] + cur = cur.next + + for i in range(n - k): + cur.val = arr[i] + cur = cur.next + + return head +``` + +```java +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode() {} + * ListNode(int val) { this.val = val; } + * ListNode(int val, ListNode next) { this.val = val; this.next = next; } + * } + */ +public class Solution { + public ListNode rotateRight(ListNode head, int k) { + if (head == null) return null; + + ArrayList arr = new ArrayList<>(); + ListNode cur = head; + while (cur != null) { + arr.add(cur.val); + cur = cur.next; + } + + int n = arr.size(); + k %= n; + cur = head; + for (int i = n - k; i < n; i++) { + cur.val = arr.get(i); + cur = cur.next; + } + for (int i = 0; i < n - k; i++) { + cur.val = arr.get(i); + cur = cur.next; + } + return head; + } +} +``` + +```cpp +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode() : val(0), next(nullptr) {} + * ListNode(int x) : val(x), next(nullptr) {} + * ListNode(int x, ListNode *next) : val(x), next(next) {} + * }; + */ +class Solution { +public: + ListNode* rotateRight(ListNode* head, int k) { + if (!head) return nullptr; + + vector arr; + ListNode* cur = head; + while (cur) { + arr.push_back(cur->val); + cur = cur->next; + } + + int n = arr.size(); + k %= n; + cur = head; + for (int i = n - k; i < n; i++) { + cur->val = arr[i]; + cur = cur->next; + } + for (int i = 0; i < n - k; i++) { + cur->val = arr[i]; + cur = cur->next; + } + return head; + } +}; +``` + +```javascript +/** + * Definition for singly-linked list. + * class ListNode { + * constructor(val = 0, next = null) { + * this.val = val; + * this.next = next; + * } + * } + */ +class Solution { + /** + * @param {ListNode} head + * @param {number} k + * @return {ListNode} + */ + rotateRight(head, k) { + if (!head) return null; + + let arr = []; + let cur = head; + while (cur) { + arr.push(cur.val); + cur = cur.next; + } + + let n = arr.length; + k %= n; + cur = head; + for (let i = n - k; i < n; i++) { + cur.val = arr[i]; + cur = cur.next; + } + for (let i = 0; i < n - k; i++) { + cur.val = arr[i]; + cur = cur.next; + } + return head; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Iteration + +::tabs-start + +```python +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, val=0, next=None): +# self.val = val +# self.next = next +class Solution: + def rotateRight(self, head: ListNode, k: int) -> ListNode: + if not head: + return head + + length, tail = 1, head + while tail.next: + tail = tail.next + length += 1 + + k = k % length + if k == 0: + return head + + cur = head + for i in range(length - k - 1): + cur = cur.next + newHead = cur.next + cur.next = None + tail.next = head + + return newHead +``` + +```java +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode() {} + * ListNode(int val) { this.val = val; } + * ListNode(int val, ListNode next) { this.val = val; this.next = next; } + * } + */ +public class Solution { + public ListNode rotateRight(ListNode head, int k) { + if (head == null) { + return head; + } + + int length = 1; + ListNode tail = head; + while (tail.next != null) { + tail = tail.next; + length++; + } + + k = k % length; + if (k == 0) { + return head; + } + + ListNode cur = head; + for (int i = 0; i < length - k - 1; i++) { + cur = cur.next; + } + ListNode newHead = cur.next; + cur.next = null; + tail.next = head; + + return newHead; + } +} +``` + +```cpp +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode() : val(0), next(nullptr) {} + * ListNode(int x) : val(x), next(nullptr) {} + * ListNode(int x, ListNode *next) : val(x), next(next) {} + * }; + */ +class Solution { +public: + ListNode* rotateRight(ListNode* head, int k) { + if (!head) { + return head; + } + + int length = 1; + ListNode* tail = head; + while (tail->next) { + tail = tail->next; + length++; + } + + k = k % length; + if (k == 0) { + return head; + } + + ListNode* cur = head; + for (int i = 0; i < length - k - 1; i++) { + cur = cur->next; + } + ListNode* newHead = cur->next; + cur->next = nullptr; + tail->next = head; + + return newHead; + } +}; +``` + +```javascript +/** + * Definition for singly-linked list. + * class ListNode { + * constructor(val = 0, next = null) { + * this.val = val; + * this.next = next; + * } + * } + */ +class Solution { + /** + * @param {ListNode} head + * @param {number} k + * @return {ListNode} + */ + rotateRight(head, k) { + if (!head) { + return head; + } + + let length = 1, tail = head; + while (tail.next) { + tail = tail.next; + length++; + } + + k = k % length; + if (k === 0) { + return head; + } + + let cur = head; + for (let i = 0; i < length - k - 1; i++) { + cur = cur.next; + } + let newHead = cur.next; + cur.next = null; + tail.next = head; + + return newHead; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. + +--- + +## 3. Iteration (Using One Pointer) + +::tabs-start + +```python +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, val=0, next=None): +# self.val = val +# self.next = next +class Solution: + def rotateRight(self, head: ListNode, k: int) -> ListNode: + if not head: + return head + + cur, n = head, 1 + while cur.next: + n += 1 + cur = cur.next + + cur.next = head + k %= n + for i in range(n - k): + cur = cur.next + + head = cur.next + cur.next = None + return head +``` + +```java +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode() {} + * ListNode(int val) { this.val = val; } + * ListNode(int val, ListNode next) { this.val = val; this.next = next; } + * } + */ +public class Solution { + public ListNode rotateRight(ListNode head, int k) { + if (head == null) { + return head; + } + + ListNode cur = head; + int n = 1; + while (cur.next != null) { + n++; + cur = cur.next; + } + + cur.next = head; + k %= n; + for (int i = 0; i < n - k; i++) { + cur = cur.next; + } + + head = cur.next; + cur.next = null; + return head; + } +} +``` + +```cpp +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode() : val(0), next(nullptr) {} + * ListNode(int x) : val(x), next(nullptr) {} + * ListNode(int x, ListNode *next) : val(x), next(next) {} + * }; + */ +class Solution { +public: + ListNode* rotateRight(ListNode* head, int k) { + if (!head) { + return head; + } + + ListNode* cur = head; + int n = 1; + while (cur->next) { + n++; + cur = cur->next; + } + + cur->next = head; + k %= n; + for (int i = 0; i < n - k; i++) { + cur = cur->next; + } + + head = cur->next; + cur->next = nullptr; + return head; + } +}; +``` + +```javascript +/** + * Definition for singly-linked list. + * class ListNode { + * constructor(val = 0, next = null) { + * this.val = val; + * this.next = next; + * } + * } + */ +class Solution { + /** + * @param {ListNode} head + * @param {number} k + * @return {ListNode} + */ + rotateRight(head, k) { + if (!head) { + return head; + } + + let cur = head, n = 1; + while (cur.next) { + n++; + cur = cur.next; + } + + cur.next = head; + k %= n; + for (let i = 0; i < n - k; i++) { + cur = cur.next; + } + + head = cur.next; + cur.next = null; + return head; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. \ No newline at end of file diff --git a/articles/score-after-flipping-matrix.md b/articles/score-after-flipping-matrix.md new file mode 100644 index 000000000..83c222266 --- /dev/null +++ b/articles/score-after-flipping-matrix.md @@ -0,0 +1,253 @@ +## 1. Greedy (Overwriting the Input) + +::tabs-start + +```python +class Solution: + def matrixScore(self, grid: List[List[int]]) -> int: + ROWS, COLS = len(grid), len(grid[0]) + + for r in range(ROWS): + if grid[r][0] == 0: + for c in range(COLS): + grid[r][c] ^= 1 + + for c in range(COLS): + one_cnt = sum(grid[r][c] for r in range(ROWS)) + if one_cnt < ROWS - one_cnt: + for r in range(ROWS): + grid[r][c] ^= 1 + + res = 0 + for r in range(ROWS): + for c in range(COLS): + res += grid[r][c] << (COLS - c - 1) + + return res +``` + +```java +public class Solution { + public int matrixScore(int[][] grid) { + int ROWS = grid.length, COLS = grid[0].length; + + for (int r = 0; r < ROWS; r++) { + if (grid[r][0] == 0) { + for (int c = 0; c < COLS; c++) { + grid[r][c] ^= 1; + } + } + } + + for (int c = 0; c < COLS; c++) { + int oneCnt = 0; + for (int r = 0; r < ROWS; r++) { + oneCnt += grid[r][c]; + } + if (oneCnt < ROWS - oneCnt) { + for (int r = 0; r < ROWS; r++) { + grid[r][c] ^= 1; + } + } + } + + int res = 0; + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + res += grid[r][c] << (COLS - c - 1); + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int matrixScore(vector>& grid) { + int ROWS = grid.size(), COLS = grid[0].size(); + + for (int r = 0; r < ROWS; r++) { + if (grid[r][0] == 0) { + for (int c = 0; c < COLS; c++) { + grid[r][c] ^= 1; + } + } + } + + for (int c = 0; c < COLS; c++) { + int oneCnt = 0; + for (int r = 0; r < ROWS; r++) { + oneCnt += grid[r][c]; + } + if (oneCnt < ROWS - oneCnt) { + for (int r = 0; r < ROWS; r++) { + grid[r][c] ^= 1; + } + } + } + + int res = 0; + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + res += grid[r][c] << (COLS - c - 1); + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} grid + * @return {number} + */ + matrixScore(grid) { + let ROWS = grid.length, COLS = grid[0].length; + + for (let r = 0; r < ROWS; r++) { + if (grid[r][0] === 0) { + for (let c = 0; c < COLS; c++) { + grid[r][c] ^= 1; + } + } + } + + for (let c = 0; c < COLS; c++) { + let oneCnt = 0; + for (let r = 0; r < ROWS; r++) { + oneCnt += grid[r][c]; + } + if (oneCnt < ROWS - oneCnt) { + for (let r = 0; r < ROWS; r++) { + grid[r][c] ^= 1; + } + } + } + + let res = 0; + for (let r = 0; r < ROWS; r++) { + for (let c = 0; c < COLS; c++) { + res += grid[r][c] << (COLS - c - 1); + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n)$ +* Space complexity: $O(1)$ extra space. + +> Where $m$ is the number of rows and $n$ is the number of columns. + +--- + +## 2. Greedy (Optimal) + +::tabs-start + +```python +class Solution: + def matrixScore(self, grid: List[List[int]]) -> int: + ROWS, COLS = len(grid), len(grid[0]) + res = ROWS * (1 << (COLS - 1)) + + for c in range(1, COLS): + cnt = 0 + for r in range(ROWS): + if grid[r][c] != grid[r][0]: + cnt += 1 + + cnt = max(cnt, ROWS - cnt) + res += cnt * (1 << (COLS - c - 1)) + + return res +``` + +```java +public class Solution { + public int matrixScore(int[][] grid) { + int ROWS = grid.length, COLS = grid[0].length; + int res = ROWS * (1 << (COLS - 1)); + + for (int c = 1; c < COLS; c++) { + int cnt = 0; + for (int r = 0; r < ROWS; r++) { + if (grid[r][c] != grid[r][0]) { + cnt++; + } + } + cnt = Math.max(cnt, ROWS - cnt); + res += cnt * (1 << (COLS - c - 1)); + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int matrixScore(vector>& grid) { + int ROWS = grid.size(), COLS = grid[0].size(); + int res = ROWS * (1 << (COLS - 1)); + + for (int c = 1; c < COLS; c++) { + int cnt = 0; + for (int r = 0; r < ROWS; r++) { + if (grid[r][c] != grid[r][0]) { + cnt++; + } + } + cnt = max(cnt, ROWS - cnt); + res += cnt * (1 << (COLS - c - 1)); + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} grid + * @return {number} + */ + matrixScore(grid) { + const ROWS = grid.length, COLS = grid[0].length; + let res = ROWS * (1 << (COLS - 1)); + + for (let c = 1; c < COLS; c++) { + let cnt = 0; + for (let r = 0; r < ROWS; r++) { + if (grid[r][c] !== grid[r][0]) { + cnt++; + } + } + cnt = Math.max(cnt, ROWS - cnt); + res += cnt * (1 << (COLS - c - 1)); + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n)$ +* Space complexity: $O(1)$ extra space. + +> Where $m$ is the number of rows and $n$ is the number of columns. \ No newline at end of file diff --git a/articles/splitting-a-string-into-descending-consecutive-values.md b/articles/splitting-a-string-into-descending-consecutive-values.md new file mode 100644 index 000000000..b736eda11 --- /dev/null +++ b/articles/splitting-a-string-into-descending-consecutive-values.md @@ -0,0 +1,570 @@ +## 1. Backtracking + +::tabs-start + +```python +class Solution: + def splitString(self, s: str) -> bool: + n = len(s) + + def isValid(splits): + for i in range(1, len(splits)): + if splits[i] != splits[i - 1] - 1: + return False + return len(splits) > 1 + + def dfs(i, splits): + if i == n: + return isValid(splits) + num = 0 + for j in range(i, n): + num = num * 10 + int(s[j]) + splits.append(num) + if dfs(j + 1, splits): + return True + splits.pop() + return False + + return dfs(0, []) +``` + +```java +public class Solution { + public boolean splitString(String s) { + return dfs(s, 0, new ArrayList<>()); + } + + private boolean isValid(List splits) { + for (int i = 1; i < splits.size(); i++) { + if (splits.get(i) != splits.get(i - 1) - 1) { + return false; + } + } + return splits.size() > 1; + } + + private boolean dfs(String s, int i, List splits) { + if (i == s.length()) { + return isValid(splits); + } + long num = 0; + for (int j = i; j < s.length(); j++) { + num = num * 10 + (s.charAt(j) - '0'); + splits.add(num); + if (dfs(s, j + 1, splits)) { + return true; + } + splits.remove(splits.size() - 1); + } + return false; + } +} +``` + +```cpp +class Solution { +public: + bool splitString(string s) { + vector splits; + return dfs(s, 0, splits); + } + +private: + bool isValid(vector& splits) { + for (int i = 1; i < splits.size(); i++) { + if (splits[i] != splits[i - 1] - 1) { + return false; + } + } + return splits.size() > 1; + } + + bool dfs(string& s, int i, vector& splits) { + if (i == s.size()) { + return isValid(splits); + } + unsigned long long num = 0; + for (int j = i; j < s.size(); j++) { + num = num * 10 + (s[j] - '0'); + splits.push_back(num); + if (dfs(s, j + 1, splits)) { + return true; + } + splits.pop_back(); + } + return false; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {boolean} + */ + splitString(s) { + const n = s.length; + + const isValid = (splits) => { + for (let i = 1; i < splits.length; i++) { + if (splits[i] !== splits[i - 1] - 1) { + return false; + } + } + return splits.length > 1; + }; + + const dfs = (i, splits) => { + if (i === n) { + return isValid(splits); + } + let num = 0; + for (let j = i; j < n; j++) { + num = num * 10 + Number(s[j]); + splits.push(num); + if (dfs(j + 1, splits)) { + return true; + } + splits.pop(); + } + return false; + }; + + return dfs(0, []); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Recursion - I + +::tabs-start + +```python +class Solution: + def splitString(self, s: str) -> bool: + def dfs(index, prev): + if index == len(s): + return True + num = 0 + for j in range(index, len(s)): + num = num * 10 + int(s[j]) + if num + 1 == prev and dfs(j + 1, num): + return True + return False + + val = 0 + for i in range(len(s) - 1): + val = val * 10 + int(s[i]) + if dfs(i + 1, val): + return True + + return False +``` + +```java +public class Solution { + public boolean splitString(String s) { + int n = s.length(); + long val = 0; + for (int i = 0; i < n - 1; i++) { + val = val * 10 + (s.charAt(i) - '0'); + if (dfs(s, i + 1, val)) { + return true; + } + } + return false; + } + + private boolean dfs(String s, int index, long prev) { + if (index == s.length()) { + return true; + } + long num = 0; + for (int j = index; j < s.length(); j++) { + num = num * 10 + (s.charAt(j) - '0'); + if (num + 1 == prev && dfs(s, j + 1, num)) { + return true; + } + } + return false; + } +} +``` + +```cpp +class Solution { +public: + bool splitString(string s) { + int n = s.size(); + unsigned long long val = 0; + for (int i = 0; i < n - 1; i++) { + val = val * 10 + (s[i] - '0'); + if (dfs(s, i + 1, val)) { + return true; + } + } + return false; + } + +private: + bool dfs(string& s, int index, long long prev) { + if (index == s.size()) { + return true; + } + unsigned long long num = 0; + for (int j = index; j < s.size(); j++) { + num = num * 10 + (s[j] - '0'); + if (num + 1 == prev && dfs(s, j + 1, num)) { + return true; + } + } + return false; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {boolean} + */ + splitString(s) { + const n = s.length; + + const dfs = (index, prev) => { + if (index === n) { + return true; + } + let num = 0; + for (let j = index; j < n; j++) { + num = num * 10 + Number(s[j]); + if (num + 1 === prev && dfs(j + 1, num)) { + return true; + } + } + return false; + }; + + let val = 0; + for (let i = 0; i < n - 1; i++) { + val = val * 10 + Number(s[i]); + if (dfs(i + 1, val)) { + return true; + } + } + + return false; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n)$ for recursion stack. + +--- + +## 3. Recursion - II + +::tabs-start + +```python +class Solution: + def splitString(self, s: str) -> bool: + def dfs(index, prev): + if index == len(s): + return True + num = 0 + for j in range(index, len(s)): + num = num * 10 + int(s[j]) + if num + 1 == prev and dfs(j + 1, num): + return True + if num >= prev: + break + return False + + val = 0 + for i in range(len(s) - 1): + val = val * 10 + int(s[i]) + if dfs(i + 1, val): + return True + + return False +``` + +```java +public class Solution { + public boolean splitString(String s) { + int n = s.length(); + long val = 0; + for (int i = 0; i < n - 1; i++) { + val = val * 10 + (s.charAt(i) - '0'); + if (dfs(s, i + 1, val)) { + return true; + } + } + return false; + } + + private boolean dfs(String s, int index, long prev) { + if (index == s.length()) { + return true; + } + long num = 0; + for (int j = index; j < s.length(); j++) { + num = num * 10 + (s.charAt(j) - '0'); + if (num + 1 == prev && dfs(s, j + 1, num)) { + return true; + } + if (num >= prev) { + break; + } + } + return false; + } +} +``` + +```cpp +class Solution { +public: + bool splitString(string s) { + int n = s.size(); + unsigned long long val = 0; + for (int i = 0; i < n - 1; i++) { + val = val * 10 + (s[i] - '0'); + if (dfs(s, i + 1, val)) { + return true; + } + } + return false; + } + +private: + bool dfs(string& s, int index, long long prev) { + if (index == s.size()) { + return true; + } + unsigned long long num = 0; + for (int j = index; j < s.size(); j++) { + num = num * 10 + (s[j] - '0'); + if (num + 1 == prev && dfs(s, j + 1, num)) { + return true; + } + if (num >= prev) { + break; + } + } + return false; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {boolean} + */ + splitString(s) { + const n = s.length; + + const dfs = (index, prev) => { + if (index === n) { + return true; + } + let num = 0; + for (let j = index; j < n; j++) { + num = num * 10 + Number(s[j]); + if (num + 1 === prev && dfs(j + 1, num)) { + return true; + } + if (num >= prev) { + break; + } + } + return false; + }; + + let val = 0; + for (let i = 0; i < n - 1; i++) { + val = val * 10 + Number(s[i]); + if (dfs(i + 1, val)) { + return true; + } + } + + return false; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n)$ for recursion stack. + +--- + +## 4. Stack + +::tabs-start + +```python +class Solution: + def splitString(self, s: str) -> bool: + n = len(s) + stack = [] + val = 0 + + for i in range(n - 1): + val = val * 10 + int(s[i]) + stack.append((i + 1, val)) + + while stack: + index, prev = stack.pop() + num = 0 + for j in range(index, n): + num = num * 10 + int(s[j]) + if num + 1 == prev: + if j + 1 == n: + return True + stack.append((j + 1, num)) + elif num >= prev: + break + + return False +``` + +```java +public class Solution { + public boolean splitString(String s) { + int n = s.length(); + Stack stack = new Stack<>(); + long val = 0; + + for (int i = 0; i < n - 1; i++) { + val = val * 10 + (s.charAt(i) - '0'); + stack.push(new long[]{i + 1, val}); + + while (!stack.isEmpty()) { + long[] top = stack.pop(); + int index = (int) top[0]; + long prev = top[1]; + long num = 0; + + for (int j = index; j < n; j++) { + num = num * 10 + (s.charAt(j) - '0'); + if (num + 1 == prev) { + if (j + 1 == n) { + return true; + } + stack.push(new long[]{j + 1, num}); + } else if (num >= prev) { + break; + } + } + } + } + + return false; + } +} +``` + +```cpp +class Solution { +public: + bool splitString(string s) { + int n = s.size(); + stack> stack; + unsigned long long val = 0; + + for (int i = 0; i < n - 1; i++) { + val = val * 10 + (s[i] - '0'); + stack.push({i + 1, val}); + + while (!stack.empty()) { + auto [index, prev] = stack.top(); + stack.pop(); + unsigned long long num = 0; + + for (int j = index; j < n; j++) { + num = num * 10 + (s[j] - '0'); + if (num + 1 == prev) { + if (j + 1 == n) { + return true; + } + stack.push({j + 1, num}); + } else if (num >= prev) { + break; + } + } + } + } + + return false; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {boolean} + */ + splitString(s) { + const n = s.length; + let stack = []; + let val = 0; + + for (let i = 0; i < n - 1; i++) { + val = val * 10 + Number(s[i]); + stack.push([i + 1, val]); + + while (stack.length) { + let [index, prev] = stack.pop(); + let num = 0; + + for (let j = index; j < n; j++) { + num = num * 10 + Number(s[j]); + if (num + 1 === prev) { + if (j + 1 === n) { + return true; + } + stack.push([j + 1, num]); + } else if (num >= prev) { + break; + } + } + } + } + + return false; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/the-number-of-beautiful-subsets.md b/articles/the-number-of-beautiful-subsets.md new file mode 100644 index 000000000..1ed55b435 --- /dev/null +++ b/articles/the-number-of-beautiful-subsets.md @@ -0,0 +1,739 @@ +## 1. Backtracking + +::tabs-start + +```python +class Solution: + def beautifulSubsets(self, nums: List[int], k: int) -> int: + def helper(i, count): + if i == len(nums): + return 1 + + res = helper(i + 1, count) # Skip nums[i] + if not count[nums[i] + k] and not count[nums[i] - k]: + count[nums[i]] += 1 + res += helper(i + 1, count) + count[nums[i]] -= 1 + + return res + + return helper(0, defaultdict(int)) - 1 +``` + +```java +public class Solution { + public int beautifulSubsets(int[] nums, int k) { + return helper(0, new HashMap<>(), nums, k) - 1; + } + + private int helper(int i, Map count, int[] nums, int k) { + if (i == nums.length) { + return 1; + } + + int res = helper(i + 1, count, nums, k); // Skip nums[i] + + if (!count.containsKey(nums[i] + k) && !count.containsKey(nums[i] - k)) { + count.put(nums[i], count.getOrDefault(nums[i], 0) + 1); + res += helper(i + 1, count, nums, k); + count.put(nums[i], count.get(nums[i]) - 1); + if (count.get(nums[i]) == 0) { + count.remove(nums[i]); + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int beautifulSubsets(vector& nums, int k) { + unordered_map count; + return helper(0, count, nums, k) - 1; + } + +private: + int helper(int i, unordered_map& count, vector& nums, int k) { + if (i == nums.size()) { + return 1; + } + + int res = helper(i + 1, count, nums, k); // Skip nums[i] + if (!count[nums[i] + k] && !count[nums[i] - k]) { + count[nums[i]]++; + res += helper(i + 1, count, nums, k); + count[nums[i]]--; + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {number} + */ + beautifulSubsets(nums, k) { + const helper = (i, count) => { + if (i === nums.length) { + return 1; + } + + let res = helper(i + 1, count); // Skip nums[i] + + if (!count.has(nums[i] + k) && !count.has(nums[i] - k)) { + count.set(nums[i], (count.get(nums[i]) || 0) + 1); + res += helper(i + 1, count); + count.set(nums[i], count.get(nums[i]) - 1); + if (count.get(nums[i]) === 0) { + count.delete(nums[i]); + } + } + + return res; + }; + + return helper(0, new Map()) - 1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(2 ^ n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def beautifulSubsets(self, nums: List[int], k: int) -> int: + cnt = Counter(nums) + groups = [] # List of dicts + cache = {} + + def helper(n, g): + if n not in g: + return 1 + if n in cache: + return cache[n] + + skip = helper(n + k, g) + include = (2**g[n] - 1) * helper(n + 2 * k, g) + cache[n] = skip + include + return skip + include + + visit = set() + for n in cnt.keys(): + if n in visit: + continue + g = {} + while n - k in cnt: + n -= k + while n in cnt: + g[n] = cnt[n] + visit.add(n) + n += k + groups.append(g) + + res = 1 + for g in groups: + n = min(g.keys()) + res *= helper(n, g) + + return res - 1 +``` + +```java +public class Solution { + private Map cache; + private Map cnt; + private Set visit; + + public int beautifulSubsets(int[] nums, int k) { + List> groups = new ArrayList<>(); + this.cache = new HashMap<>(); + this.cnt = new HashMap<>(); + this.visit = new HashSet<>(); + + for (int num : nums) { + cnt.put(num, cnt.getOrDefault(num, 0) + 1); + } + + for (int n : cnt.keySet()) { + if (visit.contains(n)) { + continue; + } + Map g = new HashMap<>(); + while (cnt.containsKey(n - k)) { + n -= k; + } + while (cnt.containsKey(n)) { + g.put(n, cnt.get(n)); + visit.add(n); + n += k; + } + groups.add(g); + } + + int res = 1; + for (Map g : groups) { + int n = Collections.min(g.keySet()); + res *= helper(n, g, k); + } + + return res - 1; + } + + private int helper(int n, Map g, int k) { + if (!g.containsKey(n)) { + return 1; + } + if (cache.containsKey(n)) { + return cache.get(n); + } + + int skip = helper(n + k, g, k); + int include = (int) ((Math.pow(2, g.get(n)) - 1) * helper(n + 2 * k, g, k)); + int result = skip + include; + cache.put(n, result); + return result; + } +} +``` + +```cpp +class Solution { +public: + int beautifulSubsets(vector& nums, int k) { + vector> groups; + cache.clear(); + cnt.clear(); + visit.clear(); + + for (int& num : nums) { + cnt[num]++; + } + + for (auto it = cnt.begin(); it != cnt.end(); ++it) { + int n = it->first; + if (visit.count(n)) { + continue; + } + unordered_map g; + while (cnt.count(n - k)) { + n -= k; + } + while (cnt.count(n)) { + g[n] = cnt[n]; + visit.insert(n); + n += k; + } + groups.push_back(g); + } + + int res = 1; + for (auto& g : groups) { + int n = min_element(g.begin(), g.end())->first; + res *= helper(n, g, k); + } + return res - 1; + } + +private: + unordered_map cache; + unordered_map cnt; + unordered_set visit; + + int helper(int n, unordered_map& g, int k) { + if (!g.count(n)) { + return 1; + } + if (cache.count(n)) { + return cache[n]; + } + + int skip = helper(n + k, g, k); + int include = (pow(2, g[n]) - 1) * helper(n + 2 * k, g, k); + return cache[n] = skip + include; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {number} + */ + beautifulSubsets(nums, k) { + let cnt = new Map(); + for (const num of nums) { + cnt.set(num, (cnt.get(num) || 0) + 1); + } + + let groups = []; + let cache = new Map(); + let visit = new Set(); + + for (let n of cnt.keys()) { + if (visit.has(n)) { + continue; + } + let g = new Map(); + while (cnt.has(n - k)) { + n -= k; + } + while (cnt.has(n)) { + g.set(n, cnt.get(n)); + visit.add(n); + n += k; + } + groups.push(g); + } + + const helper = (n, g) => { + if (!g.has(n)) { + return 1; + } + if (cache.has(n)) { + return cache.get(n); + } + + let skip = helper(n + k, g); + let include = (2 ** g.get(n) - 1) * helper(n + 2 * k, g); + let result = skip + include; + cache.set(n, result); + return result; + }; + + let res = 1; + for (const g of groups) { + let n = Math.min(...g.keys()); + res *= helper(n, g); + } + return res - 1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def beautifulSubsets(self, nums: List[int], k: int) -> int: + cnt = Counter(nums) + groups = [] # List of dicts + + visit = set() + for n in cnt.keys(): + if n in visit: + continue + g = {} + while n - k in cnt: + n -= k + while n in cnt: + g[n] = cnt[n] + visit.add(n) + n += k + groups.append(g) + + res = 1 + for g in groups: + dp = {} + prev = None + + for num in sorted(g): + count = g[num] + if prev is None or prev + k != num: + dp[num] = (dp.get(prev, 1) * (1 + (2 ** count - 1))) + else: + dp[num] = dp[prev] + (2 ** count - 1) * dp.get(prev - k, 1) + prev = num + + res *= dp[prev] + + return res - 1 +``` + +```java +class Solution { + public int beautifulSubsets(int[] nums, int k) { + Map cnt = new HashMap<>(); + for (int num : nums) { + cnt.put(num, cnt.getOrDefault(num, 0) + 1); + } + + List> groups = new ArrayList<>(); + Set visit = new HashSet<>(); + + for (int n : cnt.keySet()) { + if (visit.contains(n)) { + continue; + } + Map g = new HashMap<>(); + while (cnt.containsKey(n - k)) { + n -= k; + } + while (cnt.containsKey(n)) { + g.put(n, cnt.get(n)); + visit.add(n); + n += k; + } + groups.add(g); + } + + int res = 1; + for (Map g : groups) { + Map dp = new HashMap<>(); + Integer prev = null; + + List arr = new ArrayList<>(g.keySet()); + Collections.sort(arr); + for (int num : arr) { + int count = g.get(num); + if (prev == null || prev + k != num) { + dp.put(num, dp.getOrDefault(prev, 1) * (1 + (int) Math.pow(2, count) - 1)); + } else { + dp.put(num, dp.get(prev) + + ((int) Math.pow(2, count) - 1) * dp.getOrDefault(prev - k, 1)); + } + prev = num; + } + + res *= dp.get(prev); + } + + return res - 1; + } +} +``` + +```cpp +class Solution { +public: + int beautifulSubsets(vector& nums, int k) { + unordered_map cnt; + for (int num : nums) { + cnt[num]++; + } + + vector> groups; + unordered_set visit; + + for (auto it = cnt.begin(); it != cnt.end(); ++it) { + int n = it->first; + if (visit.count(n)) { + continue; + } + unordered_map g; + while (cnt.count(n - k)) { + n -= k; + } + while (cnt.count(n)) { + g[n] = cnt[n]; + visit.insert(n); + n += k; + } + groups.push_back(g); + } + + int res = 1; + for (auto& g : groups) { + unordered_map dp; + int prev = -1; + + vector keys; + for (auto& [num, _] : g) { + keys.push_back(num); + } + sort(keys.begin(), keys.end()); + + for (int num : keys) { + int count = g[num]; + if (prev == -1 || prev + k != num) { + dp[num] = dp.count(prev) ? dp[prev] * (1 + (1 << count) - 1) : + (1 + (1 << count) - 1); + } else { + dp[num] = dp[prev] + ((1 << count) - 1) * + (dp.count(prev - k) ? dp[prev - k] : 1); + } + prev = num; + } + + res *= dp[prev]; + } + + return res - 1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {number} + */ + beautifulSubsets(nums, k) { + let cnt = new Map(); + for (const num of nums) { + cnt.set(num, (cnt.get(num) || 0) + 1); + } + + let groups = []; + let visit = new Set(); + + for (const n of cnt.keys()) { + if (visit.has(n)) { + continue; + } + let g = new Map(); + let num = n; + while (cnt.has(num - k)) { + num -= k; + } + while (cnt.has(num)) { + g.set(num, cnt.get(num)); + visit.add(num); + num += k; + } + groups.push(g); + } + + let res = 1; + for (const g of groups) { + let dp = new Map(); + let prev = null; + + for (const num of g.keys()) { + let count = g.get(num); + if (prev === null || prev + k !== num) { + dp.set(num, (dp.get(prev) || 1) * (1 + (2 ** count - 1))); + } else { + dp.set(num, dp.get(prev) + (2 ** count - 1) * (dp.get(prev - k) || 1)); + } + prev = num; + } + + res *= dp.get(prev); + } + + return res - 1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ + +--- + +## 4. Dynamic Programming (Space Optimized) + +::tabs-start + +```python +class Solution: + def beautifulSubsets(self, nums: List[int], k: int) -> int: + cnt = Counter(nums) + groups = defaultdict(dict) + + # Group numbers based on their remainder with k + for num in nums: + groups[num % k][num] = cnt[num] + + res = 1 + for g in groups.values(): + prev = 0 + dp, ndp = 0, 1 + + for num in sorted(g.keys()): + count = g[num] + have = (1 << count) - 1 + tmp = ndp + ndp += dp + + if prev == 0 or prev + k != num: + dp = have * (tmp + dp) + else: + dp = tmp * have + + prev = num + + res *= (dp + ndp) + + return res - 1 +``` + +```java +public class Solution { + public int beautifulSubsets(int[] nums, int k) { + Map> groups = new HashMap<>(); + Map cnt = new HashMap<>(); + for (int num : nums) { + cnt.put(num, cnt.getOrDefault(num, 0) + 1); + } + + // Group numbers based on remainder with k + for (int num : nums) { + groups.putIfAbsent(num % k, new HashMap<>()); + groups.get(num % k).put(num, cnt.get(num)); + } + + int res = 1; + for (Map g : groups.values()) { + int prev = 0, dp = 0, ndp = 1; + List sortedKeys = new ArrayList<>(g.keySet()); + Collections.sort(sortedKeys); + + for (int num : sortedKeys) { + int count = g.get(num); + int have = (1 << count) - 1; + int tmp = ndp; + ndp += dp; + + if (prev == 0 || prev + k != num) { + dp = have * (tmp + dp); + } else { + dp = tmp * have; + } + + prev = num; + } + + res *= (dp + ndp); + } + + return res - 1; + } +} +``` + +```cpp +class Solution { +public: + int beautifulSubsets(vector& nums, int k) { + unordered_map> groups; + unordered_map cnt; + for (int& num : nums) { + cnt[num]++; + } + + // Group numbers based on remainder with k + for (int num : nums) { + groups[num % k][num] = cnt[num]; + } + + int res = 1; + for (auto& [rem, g] : groups) { + int prev = 0, dp = 0, ndp = 1; + + for (auto& [num, count] : g) { + int have = (1 << count) - 1; + int tmp = ndp; + ndp += dp; + + if (prev == 0 || prev + k != num) { + dp = have * (tmp + dp); + } else { + dp = tmp * have; + } + + prev = num; + } + + res *= (dp + ndp); + } + + return res - 1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {number} + */ + beautifulSubsets(nums, k) { + let groups = new Map(); + let cnt = new Map(); + for (const num of nums) { + cnt.set(num, (cnt.get(num) || 0) + 1); + } + + // Group numbers based on remainder with k + for (const num of nums) { + if (!groups.has(num % k)) { + groups.set(num % k, new Map()); + } + groups.get(num % k).set(num, cnt.get(num)); + } + + let res = 1; + for (const g of groups.values()) { + let prev = 0, dp = 0, ndp = 1; + let sortedKeys = Array.from(g.keys()).sort((a, b) => a - b); + + for (const num of sortedKeys) { + let count = g.get(num); + let have = (1 << count) - 1; + let tmp = ndp; + ndp += dp; + + if (prev === 0 || prev + k !== num) { + dp = have * (tmp + dp); + } else { + dp = tmp * have; + } + + prev = num; + } + + res *= (dp + ndp); + } + + return res - 1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/two-city-scheduling.md b/articles/two-city-scheduling.md new file mode 100644 index 000000000..61f80767c --- /dev/null +++ b/articles/two-city-scheduling.md @@ -0,0 +1,683 @@ +## 1. Recursion + +::tabs-start + +```python +class Solution: + def twoCitySchedCost(self, costs: List[List[int]]) -> int: + n = len(costs) // 2 + + def dfs(i, aCount, bCount): + if i == len(costs): + return 0 + + res = float("inf") + if aCount > 0: + res = costs[i][0] + dfs(i + 1, aCount - 1, bCount) + + if bCount > 0: + res = min(res, costs[i][1] + dfs(i + 1, aCount, bCount - 1)) + return res + + return dfs(0, n, n) +``` + +```java +public class Solution { + public int twoCitySchedCost(int[][] costs) { + int n = costs.length / 2; + return dfs(costs, 0, n, n); + } + + private int dfs(int[][] costs, int i, int aCount, int bCount) { + if (i == costs.length) { + return 0; + } + + int res = Integer.MAX_VALUE; + if (aCount > 0) { + res = costs[i][0] + dfs(costs, i + 1, aCount - 1, bCount); + } + + if (bCount > 0) { + res = Math.min(res, costs[i][1] + dfs(costs, i + 1, aCount, bCount - 1)); + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int twoCitySchedCost(vector>& costs) { + int n = costs.size() / 2; + return dfs(costs, 0, n, n); + } + +private: + int dfs(vector>& costs, int i, int aCount, int bCount) { + if (i == costs.size()) { + return 0; + } + + int res = INT_MAX; + if (aCount > 0) { + res = costs[i][0] + dfs(costs, i + 1, aCount - 1, bCount); + } + + if (bCount > 0) { + res = min(res, costs[i][1] + dfs(costs, i + 1, aCount, bCount - 1)); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} costs + * @return {number} + */ + twoCitySchedCost(costs) { + let n = costs.length / 2; + + const dfs = (i, aCount, bCount) => { + if (i === costs.length) { + return 0; + } + + let res = Infinity; + if (aCount > 0) { + res = costs[i][0] + dfs(i + 1, aCount - 1, bCount); + } + + if (bCount > 0) { + res = Math.min(res, costs[i][1] + dfs(i + 1, aCount, bCount - 1)); + } + + return res; + }; + + return dfs(0, n, n); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(2 ^ N)$ +* Space complexity: $O(N)$ for recursion stack. + +> Where $N$ is the size of the array $costs$. + +--- + +## 2. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def twoCitySchedCost(self, costs: List[List[int]]) -> int: + n = len(costs) // 2 + dp = [[-1] * (n + 1) for _ in range(n + 1)] + + def dfs(i, aCount, bCount): + if i == len(costs): + return 0 + if dp[aCount][bCount] != -1: + return dp[aCount][bCount] + + res = float("inf") + if aCount > 0: + res = costs[i][0] + dfs(i + 1, aCount - 1, bCount) + if bCount > 0: + res = min(res, costs[i][1] + dfs(i + 1, aCount, bCount - 1)) + + dp[aCount][bCount] = res + return res + + return dfs(0, n, n) +``` + +```java +public class Solution { + private int[][] dp; + + public int twoCitySchedCost(int[][] costs) { + int n = costs.length / 2; + dp = new int[n + 1][n + 1]; + for (int[] row : dp) { + Arrays.fill(row, -1); + } + return dfs(costs, 0, n, n); + } + + private int dfs(int[][] costs, int i, int aCount, int bCount) { + if (i == costs.length) { + return 0; + } + if (dp[aCount][bCount] != -1) { + return dp[aCount][bCount]; + } + + int res = Integer.MAX_VALUE; + if (aCount > 0) { + res = costs[i][0] + dfs(costs, i + 1, aCount - 1, bCount); + } + if (bCount > 0) { + res = Math.min(res, costs[i][1] + dfs(costs, i + 1, aCount, bCount - 1)); + } + + dp[aCount][bCount] = res; + return res; + } +} +``` + +```cpp +class Solution { +public: + vector> dp; + + int twoCitySchedCost(vector>& costs) { + int n = costs.size() / 2; + dp = vector>(n + 1, vector(n + 1, -1)); + return dfs(costs, 0, n, n); + } + +private: + int dfs(vector>& costs, int i, int aCount, int bCount) { + if (i == costs.size()) { + return 0; + } + if (dp[aCount][bCount] != -1) { + return dp[aCount][bCount]; + } + + int res = INT_MAX; + if (aCount > 0) { + res = costs[i][0] + dfs(costs, i + 1, aCount - 1, bCount); + } + if (bCount > 0) { + res = min(res, costs[i][1] + dfs(costs, i + 1, aCount, bCount - 1)); + } + + dp[aCount][bCount] = res; + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} costs + * @return {number} + */ + twoCitySchedCost(costs) { + let n = costs.length / 2; + let dp = Array.from({ length: n + 1 }, () => new Array(n + 1).fill(-1)); + + const dfs = (i, aCount, bCount) => { + if (i === costs.length) { + return 0; + } + if (dp[aCount][bCount] !== -1) { + return dp[aCount][bCount]; + } + + let res = Infinity; + if (aCount > 0) { + res = costs[i][0] + dfs(i + 1, aCount - 1, bCount); + } + if (bCount > 0) { + res = Math.min(res, costs[i][1] + dfs(i + 1, aCount, bCount - 1)); + } + + dp[aCount][bCount] = res; + return res; + }; + + return dfs(0, n, n); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n ^ 2)$ + +> Where $n$ is the half of the size of the array $costs$. + +--- + +## 3. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def twoCitySchedCost(self, costs: List[List[int]]) -> int: + n = len(costs) // 2 + dp = [[0] * (n + 1) for _ in range(n + 1)] + + for aCount in range(n + 1): + for bCount in range(n + 1): + i = aCount + bCount + if i == 0: + continue + + dp[aCount][bCount] = float("inf") + if aCount > 0: + dp[aCount][bCount] = min(dp[aCount][bCount], dp[aCount - 1][bCount] + costs[i - 1][0]) + if bCount > 0: + dp[aCount][bCount] = min(dp[aCount][bCount], dp[aCount][bCount - 1] + costs[i - 1][1]) + + return dp[n][n] +``` + +```java +public class Solution { + public int twoCitySchedCost(int[][] costs) { + int n = costs.length / 2; + int[][] dp = new int[n + 1][n + 1]; + + for (int aCount = 0; aCount <= n; aCount++) { + for (int bCount = 0; bCount <= n; bCount++) { + int i = aCount + bCount; + if (i == 0) continue; + + dp[aCount][bCount] = Integer.MAX_VALUE; + if (aCount > 0) { + dp[aCount][bCount] = Math.min(dp[aCount][bCount], dp[aCount - 1][bCount] + costs[i - 1][0]); + } + if (bCount > 0) { + dp[aCount][bCount] = Math.min(dp[aCount][bCount], dp[aCount][bCount - 1] + costs[i - 1][1]); + } + } + } + + return dp[n][n]; + } +} +``` + +```cpp +class Solution { +public: + int twoCitySchedCost(vector>& costs) { + int n = costs.size() / 2; + vector> dp(n + 1, vector(n + 1)); + + for (int aCount = 0; aCount <= n; aCount++) { + for (int bCount = 0; bCount <= n; bCount++) { + int i = aCount + bCount; + if (i == 0) continue; + + dp[aCount][bCount] = INT_MAX; + if (aCount > 0) { + dp[aCount][bCount] = min(dp[aCount][bCount], dp[aCount - 1][bCount] + costs[i - 1][0]); + } + if (bCount > 0) { + dp[aCount][bCount] = min(dp[aCount][bCount], dp[aCount][bCount - 1] + costs[i - 1][1]); + } + } + } + + return dp[n][n]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} costs + * @return {number} + */ + twoCitySchedCost(costs) { + let n = costs.length / 2; + let dp = Array.from({ length: n + 1 }, () => new Array(n + 1).fill(0)); + + for (let aCount = 0; aCount <= n; aCount++) { + for (let bCount = 0; bCount <= n; bCount++) { + let i = aCount + bCount; + if (i === 0) continue; + + dp[aCount][bCount] = Infinity; + if (aCount > 0) { + dp[aCount][bCount] = Math.min(dp[aCount][bCount], dp[aCount - 1][bCount] + costs[i - 1][0]); + } + if (bCount > 0) { + dp[aCount][bCount] = Math.min(dp[aCount][bCount], dp[aCount][bCount - 1] + costs[i - 1][1]); + } + } + } + + return dp[n][n]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n ^ 2)$ + +> Where $n$ is the half of the size of the array $costs$. + +--- + +## 4. Dynamic Programming (Space Optimized) + +::tabs-start + +```python +class Solution: + def twoCitySchedCost(self, costs: List[List[int]]) -> int: + n = len(costs) // 2 + dp = [0] * (n + 1) + + for aCount in range(n + 1): + for bCount in range(n + 1): + i = aCount + bCount + if i == 0: + continue + + tmp = dp[bCount] + dp[bCount] = float("inf") + if aCount > 0: + dp[bCount] = min(dp[bCount], tmp + costs[i - 1][0]) + if bCount > 0: + dp[bCount] = min(dp[bCount], dp[bCount - 1] + costs[i - 1][1]) + + return dp[n] +``` + +```java +public class Solution { + public int twoCitySchedCost(int[][] costs) { + int n = costs.length / 2; + int[] dp = new int[n + 1]; + + for (int aCount = 0; aCount <= n; aCount++) { + for (int bCount = 0; bCount <= n; bCount++) { + int i = aCount + bCount; + if (i == 0) continue; + + int tmp = dp[bCount]; + dp[bCount] = Integer.MAX_VALUE; + if (aCount > 0) { + dp[bCount] = Math.min(dp[bCount], tmp + costs[i - 1][0]); + } + if (bCount > 0) { + dp[bCount] = Math.min(dp[bCount], dp[bCount - 1] + costs[i - 1][1]); + } + } + } + + return dp[n]; + } +} +``` + +```cpp +class Solution { +public: + int twoCitySchedCost(vector>& costs) { + int n = costs.size() / 2; + vector dp(n + 1, 0); + + for (int aCount = 0; aCount <= n; aCount++) { + for (int bCount = 0; bCount <= n; bCount++) { + int i = aCount + bCount; + if (i == 0) continue; + + int tmp = dp[bCount]; + dp[bCount] = INT_MAX; + if (aCount > 0) { + dp[bCount] = min(dp[bCount], tmp + costs[i - 1][0]); + } + if (bCount > 0) { + dp[bCount] = min(dp[bCount], dp[bCount - 1] + costs[i - 1][1]); + } + } + } + + return dp[n]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} costs + * @return {number} + */ + twoCitySchedCost(costs) { + let n = costs.length / 2; + let dp = new Array(n + 1).fill(0); + + for (let aCount = 0; aCount <= n; aCount++) { + for (let bCount = 0; bCount <= n; bCount++) { + let i = aCount + bCount; + if (i === 0) continue; + + let tmp = dp[bCount]; + dp[bCount] = Infinity; + if (aCount > 0) { + dp[bCount] = Math.min(dp[bCount], tmp + costs[i - 1][0]); + } + if (bCount > 0) { + dp[bCount] = Math.min(dp[bCount], dp[bCount - 1] + costs[i - 1][1]); + } + } + } + + return dp[n]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n)$ + +> Where $n$ is the half of the size of the array $costs$. + +--- + +## 5. Greedy + +::tabs-start + +```python +class Solution: + def twoCitySchedCost(self, costs: List[List[int]]) -> int: + diffs = [] + for c1, c2 in costs: + diffs.append([c2 - c1, c1, c2]) + + diffs.sort() + res = 0 + for i in range(len(diffs)): + if i < len(diffs) // 2: + res += diffs[i][2] + else: + res += diffs[i][1] + + return res +``` + +```java +public class Solution { + public int twoCitySchedCost(int[][] costs) { + List diffs = new ArrayList<>(); + for (int[] cost : costs) { + diffs.add(new int[]{cost[1] - cost[0], cost[0], cost[1]}); + } + + diffs.sort(Comparator.comparingInt(a -> a[0])); + + int res = 0; + for (int i = 0; i < diffs.size(); i++) { + if (i < diffs.size() / 2) { + res += diffs.get(i)[2]; + } else { + res += diffs.get(i)[1]; + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int twoCitySchedCost(vector>& costs) { + vector> diffs; + for (auto& cost : costs) { + diffs.push_back({cost[1] - cost[0], cost[0], cost[1]}); + } + + sort(diffs.begin(), diffs.end()); + + int res = 0; + for (int i = 0; i < diffs.size(); i++) { + if (i < diffs.size() / 2) { + res += diffs[i][2]; + } else { + res += diffs[i][1]; + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} costs + * @return {number} + */ + twoCitySchedCost(costs) { + let diffs = []; + for (let cost of costs) { + diffs.push([cost[1] - cost[0], cost[0], cost[1]]); + } + + diffs.sort((a, b) => a[0] - b[0]); + + let res = 0; + for (let i = 0; i < diffs.length; i++) { + if (i < diffs.length / 2) { + res += diffs[i][2]; + } else { + res += diffs[i][1]; + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ + +--- + +## 6. Greedy (Optimal) + +::tabs-start + +```python +class Solution: + def twoCitySchedCost(self, costs: List[List[int]]) -> int: + costs.sort(key=lambda x: x[1] - x[0]) + n, res = len(costs) // 2, 0 + + for i in range(n): + res += costs[i][1] + costs[i + n][0] + return res +``` + +```java +public class Solution { + public int twoCitySchedCost(int[][] costs) { + Arrays.sort(costs, (a, b) -> Integer.compare(a[1] - a[0], b[1] - b[0])); + int n = costs.length / 2, res = 0; + + for (int i = 0; i < n; i++) { + res += costs[i][1] + costs[i + n][0]; + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int twoCitySchedCost(vector>& costs) { + sort(costs.begin(), costs.end(), [](const auto& a, const auto& b) { + return (a[1] - a[0]) < (b[1] - b[0]); + }); + + int n = costs.size() / 2, res = 0; + for (int i = 0; i < n; i++) { + res += costs[i][1] + costs[i + n][0]; + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} costs + * @return {number} + */ + twoCitySchedCost(costs) { + costs.sort((a, b) => (a[1] - a[0]) - (b[1] - b[0])); + let n = costs.length / 2, res = 0; + + for (let i = 0; i < n; i++) { + res += costs[i][1] + costs[i + n][0]; + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(1)$ or $O(n)$ depending on the sorting algorithm. \ No newline at end of file diff --git a/articles/widest-vertical-area-between-two-points-containing-no-points.md b/articles/widest-vertical-area-between-two-points-containing-no-points.md new file mode 100644 index 000000000..b41a574f7 --- /dev/null +++ b/articles/widest-vertical-area-between-two-points-containing-no-points.md @@ -0,0 +1,212 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def maxWidthOfVerticalArea(self, points: List[List[int]]) -> int: + n = len(points) + res = 0 + + for i in range(1, n): + x1 = points[i][0] + for j in range(i): + x2 = points[j][0] + hasPoints = False + for k in range(n): + if k == i or k == j: + continue + + x3 = points[k][0] + if x3 > min(x1, x2) and x3 < max(x1, x2): + hasPoints = True + break + + if not hasPoints: + res = max(res, abs(x1 - x2)) + + return res +``` + +```java +public class Solution { + public int maxWidthOfVerticalArea(int[][] points) { + int n = points.length, res = 0; + + for (int i = 1; i < n; i++) { + int x1 = points[i][0]; + for (int j = 0; j < i; j++) { + int x2 = points[j][0]; + boolean hasPoints = false; + + for (int k = 0; k < n; k++) { + if (k == i || k == j) continue; + + int x3 = points[k][0]; + if (x3 > Math.min(x1, x2) && x3 < Math.max(x1, x2)) { + hasPoints = true; + break; + } + } + + if (!hasPoints) { + res = Math.max(res, Math.abs(x1 - x2)); + } + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int maxWidthOfVerticalArea(vector>& points) { + int n = points.size(), res = 0; + + for (int i = 1; i < n; i++) { + int x1 = points[i][0]; + for (int j = 0; j < i; j++) { + int x2 = points[j][0]; + bool hasPoints = false; + + for (int k = 0; k < n; k++) { + if (k == i || k == j) continue; + + int x3 = points[k][0]; + if (x3 > min(x1, x2) && x3 < max(x1, x2)) { + hasPoints = true; + break; + } + } + + if (!hasPoints) { + res = max(res, abs(x1 - x2)); + } + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} points + * @return {number} + */ + maxWidthOfVerticalArea(points) { + let n = points.length, res = 0; + + for (let i = 1; i < n; i++) { + let x1 = points[i][0]; + for (let j = 0; j < i; j++) { + let x2 = points[j][0]; + let hasPoints = false; + + for (let k = 0; k < n; k++) { + if (k === i || k === j) continue; + + let x3 = points[k][0]; + if (x3 > Math.min(x1, x2) && x3 < Math.max(x1, x2)) { + hasPoints = true; + break; + } + } + + if (!hasPoints) { + res = Math.max(res, Math.abs(x1 - x2)); + } + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 3)$ +* Space complexity: $O(1)$ + +--- + +## 2. Sorting + +::tabs-start + +```python +class Solution: + def maxWidthOfVerticalArea(self, points: List[List[int]]) -> int: + points.sort() + res = 0 + for i in range(len(points) - 1): + res = max(res, points[i + 1][0] - points[i][0]) + return res +``` + +```java +public class Solution { + public int maxWidthOfVerticalArea(int[][] points) { + Arrays.sort(points, Comparator.comparingInt(a -> a[0])); + int res = 0; + + for (int i = 0; i < points.length - 1; i++) { + res = Math.max(res, points[i + 1][0] - points[i][0]); + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int maxWidthOfVerticalArea(vector>& points) { + sort(points.begin(), points.end(), [](const auto& a, const auto& b) { + return a[0] < b[0]; + }); + + int res = 0; + for (int i = 0; i < points.size() - 1; i++) { + res = max(res, points[i + 1][0] - points[i][0]); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} points + * @return {number} + */ + maxWidthOfVerticalArea(points) { + points.sort((a, b) => a[0] - b[0]); + let res = 0; + + for (let i = 0; i < points.length - 1; i++) { + res = Math.max(res, points[i + 1][0] - points[i][0]); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(1)$ or $O(n)$ depending on the sorting algorithm. \ No newline at end of file From 9e68fa1311a58a5e047535d98b97ed5a1ad1a8ac Mon Sep 17 00:00:00 2001 From: Sri Hari <94112314+Srihari2222@users.noreply.github.com> Date: Fri, 28 Feb 2025 09:04:50 +0530 Subject: [PATCH 35/45] Batch-5/Neetcode-ALL/Added-articles (#3858) --- articles/design-a-food-rating-system.md | 457 +++++++++++++++ articles/design-parking-system.md | 167 ++++++ articles/design-underground-system.md | 357 ++++++++++++ articles/naming-a-company.md | 495 ++++++++++++++++ ...umber-of-submatrices-that-sum-to-target.md | 549 ++++++++++++++++++ articles/text-justification.md | 181 ++++++ 6 files changed, 2206 insertions(+) create mode 100644 articles/design-a-food-rating-system.md create mode 100644 articles/design-parking-system.md create mode 100644 articles/design-underground-system.md create mode 100644 articles/naming-a-company.md create mode 100644 articles/number-of-submatrices-that-sum-to-target.md create mode 100644 articles/text-justification.md diff --git a/articles/design-a-food-rating-system.md b/articles/design-a-food-rating-system.md new file mode 100644 index 000000000..7be2642c1 --- /dev/null +++ b/articles/design-a-food-rating-system.md @@ -0,0 +1,457 @@ +## 1. Brute Force + +::tabs-start + +```python +class FoodRatings: + + def __init__(self, foods: List[str], cuisines: List[str], ratings: List[int]): + self.foodToRating = {} # food -> rating + self.cuisineToFood = defaultdict(list) # cuisine -> [food] + for i in range(len(foods)): + self.foodToRating[foods[i]] = ratings[i] + self.cuisineToFood[cuisines[i]].append(foods[i]) + + def changeRating(self, food: str, newRating: int) -> None: + self.foodToRating[food] = newRating + + def highestRated(self, cuisine: str) -> str: + maxR, res = 0, "" + for food in self.cuisineToFood[cuisine]: + r = self.foodToRating[food] + if r > maxR or (r == maxR and food < res): + res = food + maxR = r + return res +``` + +```java +public class FoodRatings { + private Map foodToRating; + private Map> cuisineToFood; + + public FoodRatings(String[] foods, String[] cuisines, int[] ratings) { + foodToRating = new HashMap<>(); + cuisineToFood = new HashMap<>(); + for (int i = 0; i < foods.length; i++) { + foodToRating.put(foods[i], ratings[i]); + cuisineToFood.computeIfAbsent(cuisines[i], k -> new ArrayList<>()).add(foods[i]); + } + } + + public void changeRating(String food, int newRating) { + foodToRating.put(food, newRating); + } + + public String highestRated(String cuisine) { + int maxR = 0; + String res = ""; + for (String food : cuisineToFood.get(cuisine)) { + int r = foodToRating.get(food); + if (r > maxR || (r == maxR && food.compareTo(res) < 0)) { + res = food; + maxR = r; + } + } + return res; + } +} +``` + +```cpp +class FoodRatings { +private: + unordered_map foodToRating; + unordered_map> cuisineToFood; + +public: + FoodRatings(vector& foods, vector& cuisines, vector& ratings) { + for (size_t i = 0; i < foods.size(); i++) { + foodToRating[foods[i]] = ratings[i]; + cuisineToFood[cuisines[i]].push_back(foods[i]); + } + } + + void changeRating(string food, int newRating) { + foodToRating[food] = newRating; + } + + string highestRated(string cuisine) { + int maxR = 0; + string res = ""; + for (const string& food : cuisineToFood[cuisine]) { + int r = foodToRating[food]; + if (r > maxR || (r == maxR && food < res)) { + res = food; + maxR = r; + } + } + return res; + } +}; +``` + +```javascript +class FoodRatings { + /** + * @param {string[]} foods + * @param {string[]} cuisines + * @param {number[]} ratings + */ + constructor(foods, cuisines, ratings) { + this.foodToRating = new Map(); + this.cuisineToFood = new Map(); + + for (let i = 0; i < foods.length; i++) { + this.foodToRating.set(foods[i], ratings[i]); + if (!this.cuisineToFood.has(cuisines[i])) { + this.cuisineToFood.set(cuisines[i], []); + } + this.cuisineToFood.get(cuisines[i]).push(foods[i]); + } + } + + /** + * @param {string} food + * @param {number} newRating + * @return {void} + */ + changeRating(food, newRating) { + this.foodToRating.set(food, newRating); + } + + /** + * @param {string} cuisine + * @return {string} + */ + highestRated(cuisine) { + let maxR = 0, res = ""; + for (let food of this.cuisineToFood.get(cuisine)) { + let r = this.foodToRating.get(food); + if (r > maxR || (r === maxR && food < res)) { + res = food; + maxR = r; + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: + * $O(n)$ time for initialization. + * $O(1)$ time for each $changeRating()$ function call. + * $O(n)$ time for each $highestRated()$ function call. +* Space complexity: $O(n)$ + +--- + +## 2. Heap + +::tabs-start + +```python +class FoodRatings: + + def __init__(self, foods: List[str], cuisines: List[str], ratings: List[int]): + self.foodToRating = {} # food -> rating + self.foodToCuisine = {} # food -> cuisine + self.cuisineToHeap = defaultdict(list) # cuisine -> max_heap + + for i in range(len(foods)): + self.foodToRating[foods[i]] = ratings[i] + self.foodToCuisine[foods[i]] = cuisines[i] + heappush(self.cuisineToHeap[cuisines[i]], (-ratings[i], foods[i])) + + def changeRating(self, food: str, newRating: int) -> None: + cuisine = self.foodToCuisine[food] + self.foodToRating[food] = newRating + heappush(self.cuisineToHeap[cuisine], (-newRating, food)) + + def highestRated(self, cuisine: str) -> str: + heap = self.cuisineToHeap[cuisine] + while heap: + rating, food = heap[0] + if -rating == self.foodToRating[food]: + return food + heappop(heap) +``` + +```java +public class FoodRatings { + private Map foodToRating; + private Map foodToCuisine; + private Map> cuisineToHeap; + + private static class Food { + int rating; + String name; + + Food(int rating, String name) { + this.rating = rating; + this.name = name; + } + } + + public FoodRatings(String[] foods, String[] cuisines, int[] ratings) { + foodToRating = new HashMap<>(); + foodToCuisine = new HashMap<>(); + cuisineToHeap = new HashMap<>(); + + for (int i = 0; i < foods.length; i++) { + foodToRating.put(foods[i], ratings[i]); + foodToCuisine.put(foods[i], cuisines[i]); + cuisineToHeap + .computeIfAbsent(cuisines[i], k -> new PriorityQueue<>( + (a, b) -> a.rating == b.rating ? a.name.compareTo(b.name) : b.rating - a.rating)) + .offer(new Food(ratings[i], foods[i])); + } + } + + public void changeRating(String food, int newRating) { + String cuisine = foodToCuisine.get(food); + foodToRating.put(food, newRating); + cuisineToHeap.get(cuisine).offer(new Food(newRating, food)); + } + + public String highestRated(String cuisine) { + PriorityQueue heap = cuisineToHeap.get(cuisine); + while (!heap.isEmpty()) { + Food top = heap.peek(); + if (foodToRating.get(top.name) == top.rating) { + return top.name; + } + heap.poll(); + } + return ""; + } +} +``` + +```cpp +class FoodRatings { + unordered_map foodToRating; + unordered_map foodToCuisine; + struct cmp { + bool operator()(const pair& a, const pair& b) { + if (a.first == b.first) return a.second > b.second; + return a.first < b.first; + } + }; + unordered_map, + vector>, cmp>> cuisineToHeap; + +public: + FoodRatings(vector& foods, vector& cuisines, vector& ratings) { + for (int i = 0; i < foods.size(); i++) { + foodToRating[foods[i]] = ratings[i]; + foodToCuisine[foods[i]] = cuisines[i]; + cuisineToHeap[cuisines[i]].push({ratings[i], foods[i]}); + } + } + + void changeRating(string food, int newRating) { + string cuisine = foodToCuisine[food]; + foodToRating[food] = newRating; + cuisineToHeap[cuisine].push({newRating, food}); + } + + string highestRated(string cuisine) { + auto &heap = cuisineToHeap[cuisine]; + while (!heap.empty()) { + auto [rating, food] = heap.top(); + if (foodToRating[food] == rating) return food; + heap.pop(); + } + return ""; + } +}; +``` + +```javascript +class FoodRatings { + /** + * @param {string[]} foods + * @param {string[]} cuisines + * @param {number[]} ratings + */ + constructor(foods, cuisines, ratings) { + this.foodToRating = new Map(); + this.foodToCuisine = new Map(); + this.cuisineToHeap = new Map(); + + for (let i = 0; i < foods.length; i++) { + this.foodToRating.set(foods[i], ratings[i]); + this.foodToCuisine.set(foods[i], cuisines[i]); + if (!this.cuisineToHeap.has(cuisines[i])) { + this.cuisineToHeap.set(cuisines[i], new PriorityQueue( + (a, b) => b.rating - a.rating || a.name.localeCompare(b.name) + )); + } + this.cuisineToHeap.get(cuisines[i]).enqueue( + { rating: ratings[i], name: foods[i] } + ); + } + } + + /** + * @param {string} food + * @param {number} newRating + * @return {void} + */ + changeRating(food, newRating) { + let cuisine = this.foodToCuisine.get(food); + this.foodToRating.set(food, newRating); + this.cuisineToHeap.get(cuisine).enqueue( + { rating: newRating, name: food } + ); + } + + /** + * @param {string} cuisine + * @return {string} + */ + highestRated(cuisine) { + let heap = this.cuisineToHeap.get(cuisine); + while (!heap.isEmpty()) { + let top = heap.front(); + if (this.foodToRating.get(top.name) === top.rating) { + return top.name; + } + heap.dequeue(); + } + return ""; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: + * $O(n \log n)$ time for initialization. + * $O(\log n)$ time for each $changeRating()$ function call. + * $O(\log n)$ time for each $highestRated()$ function call. +* Space complexity: $O(n)$ + +--- + +## 3. Sorted Set + +::tabs-start + +```python +class FoodRatings: + + def __init__(self, foods: List[str], cuisines: List[str], ratings: List[int]): + self.foodToRating = {} # food -> rating + self.foodToCuisine = {} # food -> cuisine + self.cuisineToSortedSet = defaultdict(SortedSet) # cuisine -> SortedSet[(rating, food)] + + for i in range(len(foods)): + self.foodToRating[foods[i]] = ratings[i] + self.foodToCuisine[foods[i]] = cuisines[i] + self.cuisineToSortedSet[cuisines[i]].add((-ratings[i], foods[i])) + + def changeRating(self, food: str, newRating: int) -> None: + cuisine = self.foodToCuisine[food] + oldRating = self.foodToRating[food] + + self.cuisineToSortedSet[cuisine].remove((-oldRating, food)) + self.foodToRating[food] = newRating + self.cuisineToSortedSet[cuisine].add((-newRating, food)) + + def highestRated(self, cuisine: str) -> str: + return self.cuisineToSortedSet[cuisine][0][1] +``` + +```java +public class FoodRatings { + private Map foodToRating; + private Map foodToCuisine; + private Map> cuisineToSortedSet; + + private static class FoodPair { + int rating; + String food; + + FoodPair(int rating, String food) { + this.rating = rating; + this.food = food; + } + } + + public FoodRatings(String[] foods, String[] cuisines, int[] ratings) { + foodToRating = new HashMap<>(); + foodToCuisine = new HashMap<>(); + cuisineToSortedSet = new HashMap<>(); + + for (int i = 0; i < foods.length; i++) { + foodToRating.put(foods[i], ratings[i]); + foodToCuisine.put(foods[i], cuisines[i]); + cuisineToSortedSet.computeIfAbsent(cuisines[i], k -> new TreeSet<>((a, b) -> { + if (a.rating != b.rating) return b.rating - a.rating; + return a.food.compareTo(b.food); + })).add(new FoodPair(ratings[i], foods[i])); + } + } + + public void changeRating(String food, int newRating) { + String cuisine = foodToCuisine.get(food); + int oldRating = foodToRating.get(food); + TreeSet set = cuisineToSortedSet.get(cuisine); + set.remove(new FoodPair(oldRating, food)); + foodToRating.put(food, newRating); + set.add(new FoodPair(newRating, food)); + } + + public String highestRated(String cuisine) { + return cuisineToSortedSet.get(cuisine).first().food; + } +} +``` + +```cpp +class FoodRatings { + unordered_map foodToRating; + unordered_map foodToCuisine; + unordered_map>> cuisineToSet; + +public: + FoodRatings(vector& foods, vector& cuisines, vector& ratings) { + for (int i = 0; i < foods.size(); i++) { + foodToRating[foods[i]] = ratings[i]; + foodToCuisine[foods[i]] = cuisines[i]; + cuisineToSet[cuisines[i]].insert({-ratings[i], foods[i]}); + } + } + + void changeRating(string food, int newRating) { + string cuisine = foodToCuisine[food]; + auto& s = cuisineToSet[cuisine]; + + s.erase({-foodToRating[food], food}); + foodToRating[food] = newRating; + s.insert({-newRating, food}); + } + + string highestRated(string cuisine) { + return begin(cuisineToSet[cuisine])->second; + } +}; +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: + * $O(n \log n)$ time for initialization. + * $O(\log n)$ time for each $changeRating()$ function call. + * $O(1)$ in Python and $O(\log n)$ in other languages for each $highestRated()$ function call. +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/design-parking-system.md b/articles/design-parking-system.md new file mode 100644 index 000000000..4abdb9aa3 --- /dev/null +++ b/articles/design-parking-system.md @@ -0,0 +1,167 @@ +## 1. Array - I + +::tabs-start + +```python +class ParkingSystem: + + def __init__(self, big: int, medium: int, small: int): + self.spaces = [big, medium, small] + + def addCar(self, carType: int) -> bool: + if self.spaces[carType - 1] > 0: + self.spaces[carType - 1] -= 1 + return True + return False +``` + +```java +public class ParkingSystem { + private int[] spaces; + + public ParkingSystem(int big, int medium, int small) { + spaces = new int[]{big, medium, small}; + } + + public boolean addCar(int carType) { + if (spaces[carType - 1] > 0) { + spaces[carType - 1]--; + return true; + } + return false; + } +} +``` + +```cpp +class ParkingSystem { + int spaces[3]; + +public: + ParkingSystem(int big, int medium, int small) { + spaces[0] = big; + spaces[1] = medium; + spaces[2] = small; + } + + bool addCar(int carType) { + if (spaces[carType - 1] > 0) { + spaces[carType - 1]--; + return true; + } + return false; + } +}; +``` + +```javascript +class ParkingSystem { + /** + * @constructor + * @param {number} big + * @param {number} medium + * @param {number} small + */ + constructor(big, medium, small) { + this.spaces = [big, medium, small]; + } + + /** + * @param {number} carType + * @return {boolean} + */ + addCar(carType) { + if (this.spaces[carType - 1] > 0) { + this.spaces[carType - 1]--; + return true; + } + return false; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: + * $O(1)$ time for initialization. + * $O(1)$ time for each $addCar()$ function call. +* Space complexity: $O(1)$ + +--- + +## 2. Array - II + +::tabs-start + +```python +class ParkingSystem: + + def __init__(self, big: int, medium: int, small: int): + self.spaces = [big, medium, small] + + def addCar(self, carType: int) -> bool: + self.spaces[carType - 1] -= 1 + return self.spaces[carType - 1] >= 0 +``` + +```java +public class ParkingSystem { + int[] spaces; + + public ParkingSystem(int big, int medium, int small) { + spaces = new int[]{big, medium, small}; + } + + public boolean addCar(int carType) { + return spaces[carType - 1]-- > 0; + } +} +``` + +```cpp +class ParkingSystem { + int spaces[3]; + +public: + ParkingSystem(int big, int medium, int small) { + spaces[0] = big, spaces[1] = medium, spaces[2] = small; + } + + bool addCar(int carType) { + return spaces[carType - 1]-- > 0; + } +}; +``` + +```javascript +class ParkingSystem { + /** + * @constructor + * @param {number} big + * @param {number} medium + * @param {number} small + */ + constructor(big, medium, small) { + this.spaces = [big, medium, small]; + } + + /** + * @param {number} carType + * @return {boolean} + */ + addCar(carType) { + return this.spaces[carType - 1]-- > 0; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: + * $O(1)$ time for initialization. + * $O(1)$ time for each $addCar()$ function call. +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/design-underground-system.md b/articles/design-underground-system.md new file mode 100644 index 000000000..d03c0c9e8 --- /dev/null +++ b/articles/design-underground-system.md @@ -0,0 +1,357 @@ +## 1. Two HashMaps + +::tabs-start + +```python +class UndergroundSystem: + + def __init__(self): + self.checkInMap = {} # id -> (startStation, time) + self.routeMap = {} # (start, end) -> [totalTime, count] + + def checkIn(self, id: int, startStation: str, t: int) -> None: + self.checkInMap[id] = (startStation, t) + + def checkOut(self, id: int, endStation: str, t: int) -> None: + startStation, time = self.checkInMap[id] + route = (startStation, endStation) + if route not in self.routeMap: + self.routeMap[route] = [0, 0] + self.routeMap[route][0] += t - time + self.routeMap[route][1] += 1 + + def getAverageTime(self, startStation: str, endStation: str) -> float: + totalTime, count = self.routeMap[(startStation, endStation)] + return totalTime / count +``` + +```java +public class UndergroundSystem { + private Map> checkInMap; + private Map routeMap; + + public UndergroundSystem() { + checkInMap = new HashMap<>(); + routeMap = new HashMap<>(); + } + + public void checkIn(int id, String startStation, int t) { + checkInMap.put(id, new Pair<>(startStation, t)); + } + + public void checkOut(int id, String endStation, int t) { + Pair entry = checkInMap.get(id); + String route = entry.getKey() + "," + endStation; + routeMap.putIfAbsent(route, new int[]{0, 0}); + routeMap.get(route)[0] += t - entry.getValue(); + routeMap.get(route)[1] += 1; + } + + public double getAverageTime(String startStation, String endStation) { + int[] data = routeMap.get(startStation + "," + endStation); + return (double) data[0] / data[1]; + } +} +``` + +```cpp +class UndergroundSystem { + unordered_map> checkInMap; + unordered_map> routeMap; + +public: + UndergroundSystem() {} + + void checkIn(int id, string startStation, int t) { + checkInMap[id] = {startStation, t}; + } + + void checkOut(int id, string endStation, int t) { + auto [startStation, time] = checkInMap[id]; + string route = startStation + "," + endStation; + if (!routeMap.count(route)) + routeMap[route] = {0, 0}; + routeMap[route].first += t - time; + routeMap[route].second += 1; + } + + double getAverageTime(string startStation, string endStation) { + string route = startStation + "," + endStation; + auto [totalTime, count] = routeMap[route]; + return (double) totalTime / count; + } +}; +``` + +```javascript +class UndergroundSystem { + /** + * @constructor + */ + constructor() { + this.checkInMap = new Map(); + this.routeMap = new Map(); + } + + /** + * @param {number} id + * @param {string} startStation + * @param {number} t + * @return {void} + */ + checkIn(id, startStation, t) { + this.checkInMap.set(id, [startStation, t]); + } + + /** + * @param {number} id + * @param {string} endStation + * @param {number} t + * @return {void} + */ + checkOut(id, endStation, t) { + const [startStation, time] = this.checkInMap.get(id); + const route = `${startStation},${endStation}`; + if (!this.routeMap.has(route)) this.routeMap.set(route, [0, 0]); + this.routeMap.get(route)[0] += t - time; + this.routeMap.get(route)[1] += 1; + } + + /** + * @param {string} startStation + * @param {string} endStation + * @return {number} + */ + getAverageTime(startStation, endStation) { + const [totalTime, count] = this.routeMap.get(`${startStation},${endStation}`); + return totalTime / count; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: + * $O(1)$ time for initialization. + * $O(1)$ time for each $checkIn()$ function call. + * $O(m)$ time for each $checkOut()$ and $getAverageTime()$ function calls. +* Space complexity: $O(n + N ^ 2)$ + +> Where $n$ is the number of passengers, $N$ is the total number of stations, and $m$ is the average length of station name. + +--- + +## 2. Two HashMaps + Hashing + +::tabs-start + +```python +class UndergroundSystem: + MOD1, MOD2 = 768258391, 685683731 + BASE1, BASE2 = 37, 31 + + def __init__(self): + self.checkInMap = {} # id -> (startStation, time) + self.routeMap = {} # hash(route) -> (totalTime, count) + + def getHash(self, s1: str, s2: str) -> int: + h1, h2, p1, p2 = 0, 0, 1, 1 + for c in s1 + ',' + s2: + h1 = (h1 + (ord(c) - 96) * p1) % self.MOD1 + h2 = (h2 + (ord(c) - 96) * p2) % self.MOD2 + p1 = (p1 * self.BASE1) % self.MOD1 + p2 = (p2 * self.BASE2) % self.MOD2 + return (h1 << 32) | h2 + + def checkIn(self, id: int, startStation: str, t: int) -> None: + self.checkInMap[id] = (startStation, t) + + def checkOut(self, id: int, endStation: str, t: int) -> None: + startStation, time = self.checkInMap[id] + routeHash = self.getHash(startStation, endStation) + if routeHash not in self.routeMap: + self.routeMap[routeHash] = [0, 0] + self.routeMap[routeHash][0] += t - time + self.routeMap[routeHash][1] += 1 + + def getAverageTime(self, startStation: str, endStation: str) -> float: + routeHash = self.getHash(startStation, endStation) + totalTime, count = self.routeMap[routeHash] + return totalTime / count +``` + +```java +public class UndergroundSystem { + private static final int MOD1 = 768258391, MOD2 = 685683731; + private static final int BASE1 = 37, BASE2 = 31; + private Map> checkInMap; + private Map routeMap; + + public UndergroundSystem() { + checkInMap = new HashMap<>(); + routeMap = new HashMap<>(); + } + + private long getHash(String s1, String s2) { + long h1 = 0, h2 = 0, p1 = 1, p2 = 1; + String combined = s1 + "," + s2; + + for (char c : combined.toCharArray()) { + h1 = (h1 + (c - 96) * p1) % MOD1; + h2 = (h2 + (c - 96) * p2) % MOD2; + p1 = (p1 * BASE1) % MOD1; + p2 = (p2 * BASE2) % MOD2; + } + return (h1 << 32) | h2; + } + + public void checkIn(int id, String startStation, int t) { + checkInMap.put(id, new Pair<>(startStation, t)); + } + + public void checkOut(int id, String endStation, int t) { + Pair checkInData = checkInMap.get(id); + long routeHash = getHash(checkInData.getKey(), endStation); + routeMap.putIfAbsent(routeHash, new int[]{0, 0}); + int[] data = routeMap.get(routeHash); + data[0] += (t - checkInData.getValue()); + data[1]++; + } + + public double getAverageTime(String startStation, String endStation) { + long routeHash = getHash(startStation, endStation); + int[] data = routeMap.get(routeHash); + return (double) data[0] / data[1]; + } +} +``` + +```cpp +class UndergroundSystem { +private: + static constexpr int MOD1 = 768258391, MOD2 = 685683731; + static constexpr int BASE1 = 37, BASE2 = 31; + unordered_map> checkInMap; + unordered_map> routeMap; + + unsigned long long getHash(const string& s1, const string& s2) { + long long h1 = 0, h2 = 0, p1 = 1, p2 = 1; + string combined = s1 + "," + s2; + + for (char c : combined) { + h1 = (h1 + (c - 96) * p1) % MOD1; + h2 = (h2 + (c - 96) * p2) % MOD2; + p1 = (p1 * BASE1) % MOD1; + p2 = (p2 * BASE2) % MOD2; + } + return (h1 << 32) | h2; + } + +public: + UndergroundSystem() {} + + void checkIn(int id, string startStation, int t) { + checkInMap[id] = {startStation, t}; + } + + void checkOut(int id, string endStation, int t) { + auto [startStation, time] = checkInMap[id]; + unsigned long long routeHash = getHash(startStation, endStation); + routeMap[routeHash].first += (t - time); + routeMap[routeHash].second++; + } + + double getAverageTime(string startStation, string endStation) { + unsigned long long routeHash = getHash(startStation, endStation); + auto [totalTime, count] = routeMap[routeHash]; + return (double) totalTime / count; + } +}; +``` + +```javascript +class UndergroundSystem { + /** + * @constructor + */ + constructor() { + this.MOD1 = 768258391; + this.MOD2 = 685683731; + this.BASE1 = 37; + this.BASE2 = 31; + this.checkInMap = new Map(); + this.routeMap = new Map(); + } + + /** + * @param {string} s1 + * @param {string} s2 + * @return {number} + */ + getHash(s1, s2) { + let h1 = 0, h2 = 0, p1 = 1, p2 = 1; + let combined = s1 + "," + s2; + + for (let i = 0; i < combined.length; i++) { + let c = combined.charCodeAt(i) - 96; + h1 = (h1 + c * p1) % this.MOD1; + h2 = (h2 + c * p2) % this.MOD2; + p1 = (p1 * this.BASE1) % this.MOD1; + p2 = (p2 * this.BASE2) % this.MOD2; + } + return BigInt(h1) << BigInt(32) | BigInt(h2); + } + + /** + * @param {number} id + * @param {string} startStation + * @param {number} t + * @return {void} + */ + checkIn(id, startStation, t) { + this.checkInMap.set(id, [startStation, t]); + } + + /** + * @param {number} id + * @param {string} endStation + * @param {number} t + * @return {void} + */ + checkOut(id, endStation, t) { + let [startStation, time] = this.checkInMap.get(id); + let routeHash = this.getHash(startStation, endStation); + if (!this.routeMap.has(routeHash)) { + this.routeMap.set(routeHash, [0, 0]); + } + let data = this.routeMap.get(routeHash); + data[0] += t - time; + data[1]++; + } + + /** + * @param {string} startStation + * @param {string} endStation + * @return {number} + */ + getAverageTime(startStation, endStation) { + let routeHash = this.getHash(startStation, endStation); + let [totalTime, count] = this.routeMap.get(routeHash); + return totalTime / count; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: + * $O(1)$ time for initialization. + * $O(1)$ time for each $checkIn()$ function call. + * $O(m)$ time for each $checkOut()$ and $getAverageTime()$ function calls. +* Space complexity: $O(n + N ^ 2)$ + +> Where $n$ is the number of passengers, $N$ is the total number of stations, and $m$ is the average length of station name. \ No newline at end of file diff --git a/articles/naming-a-company.md b/articles/naming-a-company.md new file mode 100644 index 000000000..dbd85775d --- /dev/null +++ b/articles/naming-a-company.md @@ -0,0 +1,495 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def distinctNames(self, ideas: List[str]) -> int: + n = len(ideas) + res = set() + ideasSet = set(ideas) + + for i in range(n): + for j in range(i + 1, n): + A, B = ideas[j][0] + ideas[i][1:], ideas[i][0] + ideas[j][1:] + if A not in ideasSet and B not in ideasSet: + res.add(A + ' ' + B) + res.add(B + ' ' + A) + + return len(res) +``` + +```java +public class Solution { + public long distinctNames(String[] ideas) { + int n = ideas.length; + Set res = new HashSet<>(); + Set ideasSet = new HashSet<>(Arrays.asList(ideas)); + + for (int i = 0; i < n; i++) { + for (int j = i + 1; j < n; j++) { + String A = ideas[j].charAt(0) + ideas[i].substring(1); + String B = ideas[i].charAt(0) + ideas[j].substring(1); + + if (!ideasSet.contains(A) && !ideasSet.contains(B)) { + res.add(A + " " + B); + res.add(B + " " + A); + } + } + } + + return res.size(); + } +} +``` + +```cpp +class Solution { +public: + long long distinctNames(vector& ideas) { + int n = ideas.size(); + unordered_set res; + unordered_set ideasSet(ideas.begin(), ideas.end()); + + for (int i = 0; i < n; i++) { + for (int j = i + 1; j < n; j++) { + string A = ideas[j][0] + ideas[i].substr(1); + string B = ideas[i][0] + ideas[j].substr(1); + + if (!ideasSet.count(A) && !ideasSet.count(B)) { + res.insert(A + " " + B); + res.insert(B + " " + A); + } + } + } + + return res.size(); + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} ideas + * @return {number} + */ + distinctNames(ideas) { + let n = ideas.length; + let res = new Set(); + let ideasSet = new Set(ideas); + + for (let i = 0; i < n; i++) { + for (let j = i + 1; j < n; j++) { + let A = ideas[j][0] + ideas[i].slice(1); + let B = ideas[i][0] + ideas[j].slice(1); + + if (!ideasSet.has(A) && !ideasSet.has(B)) { + res.add(A + " " + B); + res.add(B + " " + A); + } + } + } + + return res.size; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n ^ 2)$ +* Space complexity: $O(m * n ^ 2)$ + +> Where $n$ is the size of the array $ideas$ and $m$ is the average length of the strings. + +--- + +## 2. Group By First Letter (Hash Map) + +::tabs-start + +```python +class Solution: + def distinctNames(self, ideas: List[str]) -> int: + wordMap = collections.defaultdict(set) + for w in ideas: + wordMap[w[0]].add(w[1:]) + + res = 0 + for char1 in wordMap: + for char2 in wordMap: + if char1 == char2: + continue + + intersect = sum(1 for w in wordMap[char1] if w in wordMap[char2]) + distinct1 = len(wordMap[char1]) - intersect + distinct2 = len(wordMap[char2]) - intersect + res += distinct1 * distinct2 + + return res +``` + +```java +public class Solution { + public long distinctNames(String[] ideas) { + Map> wordMap = new HashMap<>(); + for (String word : ideas) { + wordMap.computeIfAbsent( + word.charAt(0), k -> new HashSet<>()).add(word.substring(1) + ); + } + + long res = 0; + for (char char1 : wordMap.keySet()) { + for (char char2 : wordMap.keySet()) { + if (char1 == char2) continue; + + int intersect = 0; + for (String w : wordMap.get(char1)) { + if (wordMap.get(char2).contains(w)) { + intersect++; + } + } + + int distinct1 = wordMap.get(char1).size() - intersect; + int distinct2 = wordMap.get(char2).size() - intersect; + res += distinct1 * 1L * distinct2; + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + long long distinctNames(vector& ideas) { + unordered_map> wordMap; + for (const string& word : ideas) { + wordMap[word[0]].insert(word.substr(1)); + } + + long long res = 0; + for (auto& [char1, set1] : wordMap) { + for (auto& [char2, set2] : wordMap) { + if (char1 == char2) continue; + + int intersect = 0; + for (const string& w : set1) { + if (set2.count(w)) { + intersect++; + } + } + + int distinct1 = set1.size() - intersect; + int distinct2 = set2.size() - intersect; + res += distinct1 * 1LL * distinct2; + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} ideas + * @return {number} + */ + distinctNames(ideas) { + const wordMap = new Map(); + for (let word of ideas) { + let key = word[0]; + if (!wordMap.has(key)) { + wordMap.set(key, new Set()); + } + wordMap.get(key).add(word.slice(1)); + } + + let res = 0; + for (let [char1, set1] of wordMap) { + for (let [char2, set2] of wordMap) { + if (char1 === char2) continue; + + let intersect = 0; + for (let w of set1) { + if (set2.has(w)) { + intersect++; + } + } + + let distinct1 = set1.size - intersect; + let distinct2 = set2.size - intersect; + res += distinct1 * distinct2; + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n)$ +* Space complexity: $O(m * n)$ + +> Where $n$ is the size of the array $ideas$ and $m$ is the average length of the strings. + +--- + +## 3. Group By First Letter (Array) + +::tabs-start + +```python +class Solution: + def distinctNames(self, ideas: List[str]) -> int: + suffixes = [set() for _ in range(26)] + for w in ideas: + suffixes[ord(w[0]) - ord('a')].add(w[1:]) + + res = 0 + for i in range(26): + for j in range(i + 1, 26): + intersect = len(suffixes[i] & suffixes[j]) + res += 2 * (len(suffixes[i]) - intersect) * (len(suffixes[j]) - intersect) + + return res +``` + +```java +public class Solution { + public long distinctNames(String[] ideas) { + Set[] suffixes = new HashSet[26]; + for (int i = 0; i < 26; i++) { + suffixes[i] = new HashSet<>(); + } + for (String w : ideas) { + suffixes[w.charAt(0) - 'a'].add(w.substring(1)); + } + + long res = 0; + for (int i = 0; i < 26; i++) { + for (int j = i + 1; j < 26; j++) { + int intersect = 0; + for (String s : suffixes[i]) { + if (suffixes[j].contains(s)) { + intersect++; + } + } + res += 2L * (suffixes[i].size() - intersect) * (suffixes[j].size() - intersect); + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + long long distinctNames(vector& ideas) { + unordered_set suffixes[26]; + for (const string& w : ideas) { + suffixes[w[0] - 'a'].insert(w.substr(1)); + } + + long long res = 0; + for (int i = 0; i < 26; i++) { + for (int j = i + 1; j < 26; j++) { + int intersect = 0; + for (const string& s : suffixes[i]) { + if (suffixes[j].count(s)) { + intersect++; + } + } + res += 2LL * (suffixes[i].size() - intersect) * (suffixes[j].size() - intersect); + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} ideas + * @return {number} + */ + distinctNames(ideas) { + const suffixes = Array.from({ length: 26 }, () => new Set()); + for (let w of ideas) { + suffixes[w.charCodeAt(0) - 97].add(w.slice(1)); + } + + let res = 0; + for (let i = 0; i < 26; i++) { + for (let j = i + 1; j < 26; j++) { + let intersect = 0; + for (let s of suffixes[i]) { + if (suffixes[j].has(s)) { + intersect++; + } + } + res += 2 * (suffixes[i].size - intersect) * (suffixes[j].size - intersect); + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n)$ +* Space complexity: $O(m * n)$ + +> Where $n$ is the size of the array $ideas$ and $m$ is the average length of the strings. + +--- + +## 4. Counting + +::tabs-start + +```python +class Solution: + def distinctNames(self, ideas: List[str]) -> int: + mp = defaultdict(lambda: [False] * 26) + count = [[0] * 26 for _ in range(26)] + res = 0 + + for s in ideas: + first_char = ord(s[0]) - ord('a') + suffix = s[1:] + mp[suffix][first_char] = True + + for suffix, arr in mp.items(): + for i in range(26): + if arr[i]: + for j in range(26): + if not arr[j]: + count[i][j] += 1 + res += count[j][i] + + return 2 * res +``` + +```java +public class Solution { + public long distinctNames(String[] ideas) { + Map mp = new HashMap<>(); + int[][] count = new int[26][26]; + long res = 0; + + for (String s : ideas) { + int firstChar = s.charAt(0) - 'a'; + String suffix = s.substring(1); + mp.putIfAbsent(suffix, new boolean[26]); + mp.get(suffix)[firstChar] = true; + } + + for (boolean[] arr : mp.values()) { + for (int i = 0; i < 26; i++) { + if (arr[i]) { + for (int j = 0; j < 26; j++) { + if (!arr[j]) { + count[i][j]++; + res += count[j][i]; + } + } + } + } + } + return 2 * res; + } +} +``` + +```cpp +class Solution { +public: + long long distinctNames(vector& ideas) { + unordered_map> mp; + long long count[26][26] = {}; + long long res = 0; + + for (const string& s : ideas) { + int firstChar = s[0] - 'a'; + string suffix = s.substr(1); + mp[suffix][firstChar] = true; + } + + for (auto& [suffix, arr] : mp) { + for (int i = 0; i < 26; i++) { + if (arr[i]) { + for (int j = 0; j < 26; j++) { + if (!arr[j]) { + count[i][j]++; + res += count[j][i]; + } + } + } + } + } + return 2 * res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} ideas + * @return {number} + */ + distinctNames(ideas) { + const mp = new Map(); + const count = Array.from({ length: 26 }, () => Array(26).fill(0)); + let res = 0; + + for (const s of ideas) { + const firstChar = s.charCodeAt(0) - 97; + const suffix = s.slice(1); + if (!mp.has(suffix)) mp.set(suffix, new Array(26).fill(false)); + mp.get(suffix)[firstChar] = true; + } + + for (const arr of mp.values()) { + for (let i = 0; i < 26; i++) { + if (arr[i]) { + for (let j = 0; j < 26; j++) { + if (!arr[j]) { + count[i][j]++; + res += count[j][i]; + } + } + } + } + } + return 2 * res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n)$ +* Space complexity: $O(m * n)$ + +> Where $n$ is the size of the array $ideas$ and $m$ is the average length of the strings. \ No newline at end of file diff --git a/articles/number-of-submatrices-that-sum-to-target.md b/articles/number-of-submatrices-that-sum-to-target.md new file mode 100644 index 000000000..0fe84feca --- /dev/null +++ b/articles/number-of-submatrices-that-sum-to-target.md @@ -0,0 +1,549 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def numSubmatrixSumTarget(self, matrix: List[List[int]], target: int) -> int: + ROWS, COLS = len(matrix), len(matrix[0]) + res = 0 + + for r1 in range(ROWS): + for r2 in range(r1, ROWS): + for c1 in range(COLS): + for c2 in range(c1, COLS): + subSum = 0 + for r in range(r1, r2 + 1): + for c in range(c1, c2 + 1): + subSum += matrix[r][c] + + if subSum == target: + res += 1 + + return res +``` + +```java +public class Solution { + public int numSubmatrixSumTarget(int[][] matrix, int target) { + int ROWS = matrix.length, COLS = matrix[0].length; + int res = 0; + + for (int r1 = 0; r1 < ROWS; r1++) { + for (int r2 = r1; r2 < ROWS; r2++) { + for (int c1 = 0; c1 < COLS; c1++) { + for (int c2 = c1; c2 < COLS; c2++) { + int subSum = 0; + for (int r = r1; r <= r2; r++) { + for (int c = c1; c <= c2; c++) { + subSum += matrix[r][c]; + } + } + if (subSum == target) { + res++; + } + } + } + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int numSubmatrixSumTarget(vector>& matrix, int target) { + int ROWS = matrix.size(), COLS = matrix[0].size(); + int res = 0; + + for (int r1 = 0; r1 < ROWS; r1++) { + for (int r2 = r1; r2 < ROWS; r2++) { + for (int c1 = 0; c1 < COLS; c1++) { + for (int c2 = c1; c2 < COLS; c2++) { + int subSum = 0; + for (int r = r1; r <= r2; r++) { + for (int c = c1; c <= c2; c++) { + subSum += matrix[r][c]; + } + } + if (subSum == target) { + res++; + } + } + } + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} matrix + * @param {number} target + * @return {number} + */ + numSubmatrixSumTarget(matrix, target) { + const ROWS = matrix.length, COLS = matrix[0].length; + let res = 0; + + for (let r1 = 0; r1 < ROWS; r1++) { + for (let r2 = r1; r2 < ROWS; r2++) { + for (let c1 = 0; c1 < COLS; c1++) { + for (let c2 = c1; c2 < COLS; c2++) { + let subSum = 0; + for (let r = r1; r <= r2; r++) { + for (let c = c1; c <= c2; c++) { + subSum += matrix[r][c]; + } + } + if (subSum === target) { + res++; + } + } + } + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m ^ 3 * n ^ 3)$ +* Space complexity: $O(1)$ extra space. + +> Where $m$ is the number of rows and $n$ is the number of columns of the given matrix. + +--- + +## 2. Two Dimensional Prefix Sum + +::tabs-start + +```python +class Solution: + def numSubmatrixSumTarget(self, matrix: List[List[int]], target: int) -> int: + ROWS, COLS = len(matrix), len(matrix[0]) + sub_sum = [[0] * COLS for _ in range(ROWS)] + + for r in range(ROWS): + for c in range(COLS): + top = sub_sum[r - 1][c] if r > 0 else 0 + left = sub_sum[r][c - 1] if c > 0 else 0 + top_left = sub_sum[r - 1][c - 1] if min(r, c) > 0 else 0 + sub_sum[r][c] = matrix[r][c] + top + left - top_left + + res = 0 + for r1 in range(ROWS): + for r2 in range(r1, ROWS): + for c1 in range(COLS): + for c2 in range(c1, COLS): + top = sub_sum[r1 - 1][c2] if r1 > 0 else 0 + left = sub_sum[r2][c1 - 1] if c1 > 0 else 0 + top_left = sub_sum[r1 - 1][c1 - 1] if min(r1, c1) > 0 else 0 + cur_sum = sub_sum[r2][c2] - top - left + top_left + if cur_sum == target: + res += 1 + return res +``` + +```java +public class Solution { + public int numSubmatrixSumTarget(int[][] matrix, int target) { + int ROWS = matrix.length, COLS = matrix[0].length; + int[][] subSum = new int[ROWS][COLS]; + + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + int top = (r > 0) ? subSum[r - 1][c] : 0; + int left = (c > 0) ? subSum[r][c - 1] : 0; + int topLeft = (Math.min(r, c) > 0) ? subSum[r - 1][c - 1] : 0; + subSum[r][c] = matrix[r][c] + top + left - topLeft; + } + } + + int res = 0; + for (int r1 = 0; r1 < ROWS; r1++) { + for (int r2 = r1; r2 < ROWS; r2++) { + for (int c1 = 0; c1 < COLS; c1++) { + for (int c2 = c1; c2 < COLS; c2++) { + int top = (r1 > 0) ? subSum[r1 - 1][c2] : 0; + int left = (c1 > 0) ? subSum[r2][c1 - 1] : 0; + int topLeft = (Math.min(r1, c1) > 0) ? subSum[r1 - 1][c1 - 1] : 0; + int curSum = subSum[r2][c2] - top - left + topLeft; + if (curSum == target) { + res++; + } + } + } + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int numSubmatrixSumTarget(vector>& matrix, int target) { + int ROWS = matrix.size(), COLS = matrix[0].size(); + vector> subSum(ROWS, vector(COLS, 0)); + + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + int top = (r > 0) ? subSum[r - 1][c] : 0; + int left = (c > 0) ? subSum[r][c - 1] : 0; + int topLeft = (min(r, c) > 0) ? subSum[r - 1][c - 1] : 0; + subSum[r][c] = matrix[r][c] + top + left - topLeft; + } + } + + int res = 0; + for (int r1 = 0; r1 < ROWS; r1++) { + for (int r2 = r1; r2 < ROWS; r2++) { + for (int c1 = 0; c1 < COLS; c1++) { + for (int c2 = c1; c2 < COLS; c2++) { + int top = (r1 > 0) ? subSum[r1 - 1][c2] : 0; + int left = (c1 > 0) ? subSum[r2][c1 - 1] : 0; + int topLeft = (min(r1, c1) > 0) ? subSum[r1 - 1][c1 - 1] : 0; + int curSum = subSum[r2][c2] - top - left + topLeft; + if (curSum == target) { + res++; + } + } + } + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} matrix + * @param {number} target + * @return {number} + */ + numSubmatrixSumTarget(matrix, target) { + const ROWS = matrix.length, COLS = matrix[0].length; + const subSum = Array.from({ length: ROWS }, () => Array(COLS).fill(0)); + + for (let r = 0; r < ROWS; r++) { + for (let c = 0; c < COLS; c++) { + let top = r > 0 ? subSum[r - 1][c] : 0; + let left = c > 0 ? subSum[r][c - 1] : 0; + let topLeft = Math.min(r, c) > 0 ? subSum[r - 1][c - 1] : 0; + subSum[r][c] = matrix[r][c] + top + left - topLeft; + } + } + + let res = 0; + for (let r1 = 0; r1 < ROWS; r1++) { + for (let r2 = r1; r2 < ROWS; r2++) { + for (let c1 = 0; c1 < COLS; c1++) { + for (let c2 = c1; c2 < COLS; c2++) { + let top = r1 > 0 ? subSum[r1 - 1][c2] : 0; + let left = c1 > 0 ? subSum[r2][c1 - 1] : 0; + let topLeft = Math.min(r1, c1) > 0 ? subSum[r1 - 1][c1 - 1] : 0; + let curSum = subSum[r2][c2] - top - left + topLeft; + if (curSum === target) { + res++; + } + } + } + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m ^ 2 * n ^ 2)$ +* Space complexity: $O(m * n)$ + +> Where $m$ is the number of rows and $n$ is the number of columns of the given matrix. + +--- + +## 3. Horizontal 1D Prefix Sum + +::tabs-start + +```python +class Solution: + def numSubmatrixSumTarget(self, matrix: List[List[int]], target: int) -> int: + ROWS, COLS = len(matrix), len(matrix[0]) + sub_sum = [[0] * COLS for _ in range(ROWS)] + + for r in range(ROWS): + for c in range(COLS): + top = sub_sum[r - 1][c] if r > 0 else 0 + left = sub_sum[r][c - 1] if c > 0 else 0 + top_left = sub_sum[r - 1][c - 1] if min(r, c) > 0 else 0 + sub_sum[r][c] = matrix[r][c] + top + left - top_left + + res = 0 + for r1 in range(ROWS): + for r2 in range(r1, ROWS): + count = defaultdict(int) + count[0] = 1 + for c in range(COLS): + cur_sum = sub_sum[r2][c] - (sub_sum[r1 - 1][c] if r1 > 0 else 0) + res += count[cur_sum - target] + count[cur_sum] += 1 + + return res +``` + +```java +public class Solution { + public int numSubmatrixSumTarget(int[][] matrix, int target) { + int ROWS = matrix.length, COLS = matrix[0].length; + int[][] subSum = new int[ROWS][COLS]; + + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + int top = (r > 0) ? subSum[r - 1][c] : 0; + int left = (c > 0) ? subSum[r][c - 1] : 0; + int topLeft = (Math.min(r, c) > 0) ? subSum[r - 1][c - 1] : 0; + subSum[r][c] = matrix[r][c] + top + left - topLeft; + } + } + + int res = 0; + for (int r1 = 0; r1 < ROWS; r1++) { + for (int r2 = r1; r2 < ROWS; r2++) { + Map count = new HashMap<>(); + count.put(0, 1); + for (int c = 0; c < COLS; c++) { + int curSum = subSum[r2][c] - (r1 > 0 ? subSum[r1 - 1][c] : 0); + res += count.getOrDefault(curSum - target, 0); + count.put(curSum, count.getOrDefault(curSum, 0) + 1); + } + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int numSubmatrixSumTarget(vector>& matrix, int target) { + int ROWS = matrix.size(), COLS = matrix[0].size(); + vector> subSum(ROWS, vector(COLS, 0)); + + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + int top = (r > 0) ? subSum[r - 1][c] : 0; + int left = (c > 0) ? subSum[r][c - 1] : 0; + int topLeft = (min(r, c) > 0) ? subSum[r - 1][c - 1] : 0; + subSum[r][c] = matrix[r][c] + top + left - topLeft; + } + } + + int res = 0; + for (int r1 = 0; r1 < ROWS; r1++) { + for (int r2 = r1; r2 < ROWS; r2++) { + unordered_map count; + count[0] = 1; + for (int c = 0; c < COLS; c++) { + int curSum = subSum[r2][c] - (r1 > 0 ? subSum[r1 - 1][c] : 0); + res += count[curSum - target]; + count[curSum]++; + } + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} matrix + * @param {number} target + * @return {number} + */ + numSubmatrixSumTarget(matrix, target) { + let ROWS = matrix.length, COLS = matrix[0].length; + let subSum = Array.from({ length: ROWS }, () => Array(COLS).fill(0)); + + for (let r = 0; r < ROWS; r++) { + for (let c = 0; c < COLS; c++) { + let top = r > 0 ? subSum[r - 1][c] : 0; + let left = c > 0 ? subSum[r][c - 1] : 0; + let topLeft = Math.min(r, c) > 0 ? subSum[r - 1][c - 1] : 0; + subSum[r][c] = matrix[r][c] + top + left - topLeft; + } + } + + let res = 0; + for (let r1 = 0; r1 < ROWS; r1++) { + for (let r2 = r1; r2 < ROWS; r2++) { + let count = new Map(); + count.set(0, 1); + for (let c = 0; c < COLS; c++) { + let curSum = subSum[r2][c] - (r1 > 0 ? subSum[r1 - 1][c] : 0); + res += count.get(curSum - target) || 0; + count.set(curSum, (count.get(curSum) || 0) + 1); + } + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m ^ 2 * n)$ +* Space complexity: $O(m * n)$ + +> Where $m$ is the number of rows and $n$ is the number of columns of the given matrix. + +--- + +## 4. Vertical 1D Prefix Sum + +::tabs-start + +```python +class Solution: + def numSubmatrixSumTarget(self, matrix: List[List[int]], target: int) -> int: + ROWS, COLS = len(matrix), len(matrix[0]) + res = 0 + + for c1 in range(COLS): + row_prefix = [0] * ROWS + for c2 in range(c1, COLS): + for r in range(ROWS): + row_prefix[r] += matrix[r][c2] + + count = defaultdict(int) + count[0] = 1 + cur_sum = 0 + for r in range(ROWS): + cur_sum += row_prefix[r] + res += count[cur_sum - target] + count[cur_sum] += 1 + + return res +``` + +```java +public class Solution { + public int numSubmatrixSumTarget(int[][] matrix, int target) { + int ROWS = matrix.length, COLS = matrix[0].length, res = 0; + + for (int c1 = 0; c1 < COLS; c1++) { + int[] rowPrefix = new int[ROWS]; + for (int c2 = c1; c2 < COLS; c2++) { + for (int r = 0; r < ROWS; r++) { + rowPrefix[r] += matrix[r][c2]; + } + + Map count = new HashMap<>(); + count.put(0, 1); + int curSum = 0; + + for (int r = 0; r < ROWS; r++) { + curSum += rowPrefix[r]; + res += count.getOrDefault(curSum - target, 0); + count.put(curSum, count.getOrDefault(curSum, 0) + 1); + } + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int numSubmatrixSumTarget(vector>& matrix, int target) { + int ROWS = matrix.size(), COLS = matrix[0].size(), res = 0; + + for (int c1 = 0; c1 < COLS; c1++) { + vector rowPrefix(ROWS, 0); + for (int c2 = c1; c2 < COLS; c2++) { + for (int r = 0; r < ROWS; r++) { + rowPrefix[r] += matrix[r][c2]; + } + + unordered_map count; + count[0] = 1; + int curSum = 0; + for (int r = 0; r < ROWS; r++) { + curSum += rowPrefix[r]; + res += count[curSum - target]; + count[curSum]++; + } + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} matrix + * @param {number} target + * @return {number} + */ + numSubmatrixSumTarget(matrix, target) { + let ROWS = matrix.length, COLS = matrix[0].length, res = 0; + + for (let c1 = 0; c1 < COLS; c1++) { + let rowPrefix = new Array(ROWS).fill(0); + for (let c2 = c1; c2 < COLS; c2++) { + for (let r = 0; r < ROWS; r++) { + rowPrefix[r] += matrix[r][c2]; + } + + let count = new Map(); + count.set(0, 1); + let curSum = 0; + + for (let r = 0; r < ROWS; r++) { + curSum += rowPrefix[r]; + res += count.get(curSum - target) || 0; + count.set(curSum, (count.get(curSum) || 0) + 1); + } + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n ^ 2)$ +* Space complexity: $O(m)$ + +> Where $m$ is the number of rows and $n$ is the number of columns of the given matrix. \ No newline at end of file diff --git a/articles/text-justification.md b/articles/text-justification.md new file mode 100644 index 000000000..fb48dcabe --- /dev/null +++ b/articles/text-justification.md @@ -0,0 +1,181 @@ +## 1. Iteration + +::tabs-start + +```python +class Solution: + def fullJustify(self, words: List[str], maxWidth: int) -> List[str]: + res = [] + line, length = [], 0 + i = 0 + + while i < len(words): + if length + len(words[i]) + len(line) <= maxWidth: + line.append(words[i]) + length += len(words[i]) + i += 1 + else: + # Line complete + extra_space = maxWidth - length + remainder = extra_space % max(1, (len(line) - 1)) + space = extra_space // max(1, (len(line) - 1)) + for j in range(max(1, len(line) - 1)): + line[j] += " " * space + if remainder: + line[j] += " " + remainder -= 1 + res.append("".join(line)) + line, length = [], 0 + + # Handling last line + last_line = " ".join(line) + trail_space = maxWidth - len(last_line) + res.append(last_line + " " * trail_space) + return res +``` + +```java +public class Solution { + public List fullJustify(String[] words, int maxWidth) { + List res = new ArrayList<>(); + List line = new ArrayList<>(); + int length = 0, i = 0; + + while (i < words.length) { + // If the current word can fit in the line + if (length + words[i].length() + line.size() <= maxWidth) { + line.add(words[i]); + length += words[i].length(); + i++; + } else { + // Line complete + int extra_space = maxWidth - length; + int remainder = extra_space % Math.max(1, (line.size() - 1)); + int space = extra_space / Math.max(1, (line.size() - 1)); + + for (int j = 0; j < Math.max(1, line.size() - 1); j++) { + line.set(j, line.get(j) + " ".repeat(space)); + if (remainder > 0) { + line.set(j, line.get(j) + " "); + remainder--; + } + } + + res.add(String.join("", line)); + line.clear(); + length = 0; + } + } + + // Handling last line + String last_line = String.join(" ", line); + int trail_space = maxWidth - last_line.length(); + res.add(last_line + " ".repeat(trail_space)); + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector fullJustify(vector& words, int maxWidth) { + vector res; + vector line; + int length = 0, i = 0; + + while (i < words.size()) { + if (length + words[i].size() + line.size() <= maxWidth) { + line.push_back(words[i]); + length += words[i].size(); + i++; + } else { + // Line complete + int extra_space = maxWidth - length; + int remainder = extra_space % max(1, (int)(line.size() - 1)); + int space = extra_space / max(1, (int)(line.size() - 1)); + + for (int j = 0; j < max(1, (int)line.size() - 1); j++) { + line[j] += string(space, ' '); + if (remainder > 0) { + line[j] += " "; + remainder--; + } + } + + string justified_line = accumulate(line.begin(), line.end(), string()); + res.push_back(justified_line); + line.clear(); + length = 0; + } + } + + // Handling last line + string last_line = accumulate(line.begin(), line.end(), string(), + [](string a, string b) { + return a.empty() ? b : a + " " + b; + }); + int trail_space = maxWidth - last_line.size(); + last_line += string(trail_space, ' '); + res.push_back(last_line); + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} words + * @param {number} maxWidth + * @return {string[]} + */ + fullJustify(words, maxWidth) { + let res = []; + let line = [], length = 0, i = 0; + + while (i < words.length) { + if (length + words[i].length + line.length <= maxWidth) { + line.push(words[i]); + length += words[i].length; + i++; + } else { + // Line complete + let extra_space = maxWidth - length; + let remainder = extra_space % Math.max(1, line.length - 1); + let space = Math.floor(extra_space / Math.max(1, line.length - 1)); + + for (let j = 0; j < Math.max(1, line.length - 1); j++) { + line[j] += " ".repeat(space); + if (remainder > 0) { + line[j] += " "; + remainder--; + } + } + + res.push(line.join("")); + line = []; + length = 0; + } + } + + // Handling last line + let last_line = line.join(" "); + let trail_space = maxWidth - last_line.length; + res.push(last_line + " ".repeat(trail_space)); + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * m)$ +* Space complexity: $O(n * m)$ + +> Where $n$ is the number of words and $m$ is the average length of the words. \ No newline at end of file From 3a66f0820a308bf11c8d9ed606e5865d2796d6ed Mon Sep 17 00:00:00 2001 From: Sri Hari <94112314+Srihari2222@users.noreply.github.com> Date: Fri, 28 Feb 2025 09:30:41 +0530 Subject: [PATCH 36/45] Sri Hari: Batch-5/Neetcode-ALL/Added-articles (#3854) * Batch-5/Neetcode-ALL/Added-articles * Batch-5/Neetcode-ALL/Added-articles * Batch-5/Neetcode-ALL/Added-articles --- articles/constrained-subsequence-sum.md | 701 ++++++++++++++ ...nd-the-kth-largest-integer-in-the-array.md | 543 +++++++++++ ...-valid-obstacle-course-at-each-position.md | 387 ++++++++ ...ber-of-unique-integers-after-k-removals.md | 343 +++++++ articles/maximum-performance-of-a-team.md | 238 +++++ articles/maximum-profit-in-job-scheduling.md | 655 ++++++++++++++ articles/maximum-subsequence-score.md | 412 +++++++++ articles/minimize-deviation-in-array.md | 468 ++++++++++ articles/minimum-cost-to-hire-k-workers.md | 144 +++ articles/number-of-flowers-in-full-bloom.md | 675 ++++++++++++++ articles/paint-house.md | 445 +++++++++ articles/process-tasks-using-servers.md | 501 ++++++++++ articles/seat-reservation-manager.md | 407 +++++++++ articles/student-attendance-record-ii.md | 854 ++++++++++++++++++ 14 files changed, 6773 insertions(+) create mode 100644 articles/constrained-subsequence-sum.md create mode 100644 articles/find-the-kth-largest-integer-in-the-array.md create mode 100644 articles/find-the-longest-valid-obstacle-course-at-each-position.md create mode 100644 articles/least-number-of-unique-integers-after-k-removals.md create mode 100644 articles/maximum-performance-of-a-team.md create mode 100644 articles/maximum-profit-in-job-scheduling.md create mode 100644 articles/maximum-subsequence-score.md create mode 100644 articles/minimize-deviation-in-array.md create mode 100644 articles/minimum-cost-to-hire-k-workers.md create mode 100644 articles/number-of-flowers-in-full-bloom.md create mode 100644 articles/paint-house.md create mode 100644 articles/process-tasks-using-servers.md create mode 100644 articles/seat-reservation-manager.md create mode 100644 articles/student-attendance-record-ii.md diff --git a/articles/constrained-subsequence-sum.md b/articles/constrained-subsequence-sum.md new file mode 100644 index 000000000..1c04624df --- /dev/null +++ b/articles/constrained-subsequence-sum.md @@ -0,0 +1,701 @@ +## 1. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def constrainedSubsetSum(self, nums: List[int], k: int) -> int: + memo = [None] * len(nums) + + def dfs(i): + if memo[i] != None: + return memo[i] + + res = nums[i] + for j in range(i + 1, len(nums)): + if j - i > k: + break + res = max(res, nums[i] + dfs(j)) + + memo[i] = res + return res + + ans = float('-inf') + for i in range(len(nums)): + ans = max(ans, dfs(i)) + return ans +``` + +```java +public class Solution { + private int[] nums; + private Integer[] memo; + private int k; + + public int constrainedSubsetSum(int[] nums, int k) { + this.nums = nums; + this.memo = new Integer[nums.length]; + this.k = k; + + int ans = Integer.MIN_VALUE; + for (int i = 0; i < nums.length; i++) { + ans = Math.max(ans, dfs(i)); + } + return ans; + } + + private int dfs(int i) { + if (memo[i] != null) { + return memo[i]; + } + + int res = nums[i]; + for (int j = i + 1; j < nums.length && j - i <= k; j++) { + res = Math.max(res, nums[i] + dfs(j)); + } + + memo[i] = res; + return res; + } +} +``` + +```cpp +class Solution { +public: + int constrainedSubsetSum(vector& nums, int k) { + vector memo(nums.size(), INT_MIN); + int ans = INT_MIN; + for (int i = 0; i < nums.size(); i++) { + ans = max(ans, dfs(nums, memo, k, i)); + } + return ans; + } + +private: + int dfs(vector& nums, vector& memo, int k, int i) { + if (memo[i] != INT_MIN) { + return memo[i]; + } + + int res = nums[i]; + for (int j = i + 1; j < nums.size() && j - i <= k; j++) { + res = max(res, nums[i] + dfs(nums, memo, k, j)); + } + + memo[i] = res; + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {number} + */ + constrainedSubsetSum(nums, k) { + const memo = new Array(nums.length).fill(null); + + const dfs = (i) => { + if (memo[i] !== null) { + return memo[i]; + } + + let res = nums[i]; + for (let j = i + 1; j < nums.length && j - i <= k; j++) { + res = Math.max(res, nums[i] + dfs(j)); + } + + memo[i] = res; + return res; + }; + + let ans = -Infinity; + for (let i = 0; i < nums.length; i++) { + ans = Math.max(ans, dfs(i)); + } + return ans; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * k)$ +* Space complexity: $O(n)$ + +--- + +## 2. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def constrainedSubsetSum(self, nums: List[int], k: int) -> int: + dp = [num for num in nums] + + for i in range(1, len(nums)): + for j in range(max(0, i - k), i): + dp[i] = max(dp[i], nums[i] + dp[j]) + + return max(dp) +``` + +```java +public class Solution { + public int constrainedSubsetSum(int[] nums, int k) { + int n = nums.length; + int[] dp = new int[n]; + System.arraycopy(nums, 0, dp, 0, n); + + for (int i = 1; i < n; i++) { + for (int j = Math.max(0, i - k); j < i; j++) { + dp[i] = Math.max(dp[i], nums[i] + dp[j]); + } + } + + int res = Integer.MIN_VALUE; + for (int val : dp) { + res = Math.max(res, val); + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int constrainedSubsetSum(vector& nums, int k) { + int n = nums.size(); + vector dp(nums.begin(), nums.end()); + + for (int i = 1; i < n; i++) { + for (int j = max(0, i - k); j < i; j++) { + dp[i] = max(dp[i], nums[i] + dp[j]); + } + } + + return *max_element(dp.begin(), dp.end()); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {number} + */ + constrainedSubsetSum(nums, k) { + const n = nums.length; + const dp = [...nums]; + + for (let i = 1; i < n; i++) { + for (let j = Math.max(0, i - k); j < i; j++) { + dp[i] = Math.max(dp[i], nums[i] + dp[j]); + } + } + + return Math.max(...dp); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * k)$ +* Space complexity: $O(n)$ + +--- + +## 3. Dynamic Programming + Segment Tree + +::tabs-start + +```python +class SegmentTree: + def __init__(self, N): + self.n = N + while (self.n & (self.n - 1)) != 0: + self.n += 1 + self.tree = [float('-inf')] * (2 * self.n) + + def update(self, i, val): + i += self.n + self.tree[i] = val + while i > 1: + i >>= 1 + self.tree[i] = max(self.tree[i << 1], self.tree[i << 1 | 1]) + + def query(self, l, r): + res = float('-inf') + l += self.n + r += self.n + 1 + while l < r: + if l & 1: + res = max(res, self.tree[l]) + l += 1 + if r & 1: + r -= 1 + res = max(res, self.tree[r]) + l >>= 1 + r >>= 1 + return max(0, res) + +class Solution: + def constrainedSubsetSum(self, nums: List[int], k: int) -> int: + n = len(nums) + maxSegTree = SegmentTree(n) + maxSegTree.update(0, nums[0]) + res = nums[0] + + for i in range(1, n): + cur = nums[i] + maxSegTree.query(max(0, i - k), i - 1) + maxSegTree.update(i, cur) + res = max(res, cur) + + return res +``` + +```java +class SegmentTree { + int n; + int[] tree; + + public SegmentTree(int N) { + this.n = N; + while ((this.n & (this.n - 1)) != 0) { + this.n++; + } + tree = new int[2 * this.n]; + for (int i = 0; i < 2 * this.n; i++) { + tree[i] = Integer.MIN_VALUE; + } + } + + public void update(int i, int val) { + i += n; + tree[i] = val; + while (i > 1) { + i >>= 1; + tree[i] = Math.max(tree[i << 1], tree[(i << 1) | 1]); + } + } + + public int query(int l, int r) { + int res = Integer.MIN_VALUE; + l += n; + r += n + 1; + while (l < r) { + if ((l & 1) == 1) { + res = Math.max(res, tree[l]); + l++; + } + if ((r & 1) == 1) { + r--; + res = Math.max(res, tree[r]); + } + l >>= 1; + r >>= 1; + } + return Math.max(0, res); + } +} + +public class Solution { + public int constrainedSubsetSum(int[] nums, int k) { + int n = nums.length; + SegmentTree maxSegTree = new SegmentTree(n); + maxSegTree.update(0, nums[0]); + int res = nums[0]; + + for (int i = 1; i < n; i++) { + int cur = nums[i] + maxSegTree.query(Math.max(0, i - k), i - 1); + maxSegTree.update(i, cur); + res = Math.max(res, cur); + } + + return res; + } +} +``` + +```cpp +class SegmentTree { +public: + int n; + vector tree; + + SegmentTree(int N) { + n = N; + while ((n & (n - 1)) != 0) { + n++; + } + tree.assign(2 * n, INT_MIN); + } + + void update(int i, int val) { + i += n; + tree[i] = val; + while (i > 1) { + i >>= 1; + tree[i] = max(tree[i << 1], tree[i << 1 | 1]); + } + } + + int query(int l, int r) { + int res = INT_MIN; + l += n; + r += n + 1; + while (l < r) { + if (l & 1) { + res = max(res, tree[l]); + l++; + } + if (r & 1) { + r--; + res = max(res, tree[r]); + } + l >>= 1; + r >>= 1; + } + return max(0, res); + } +}; + +class Solution { +public: + int constrainedSubsetSum(vector& nums, int k) { + int n = nums.size(); + SegmentTree maxSegTree(n); + maxSegTree.update(0, nums[0]); + int res = nums[0]; + + for (int i = 1; i < n; i++) { + int cur = nums[i] + maxSegTree.query(max(0, i - k), i - 1); + maxSegTree.update(i, cur); + res = max(res, cur); + } + + return res; + } +}; +``` + +```javascript +class SegmentTree { + /** + * @constructor + * @param {number} N + */ + constructor(N) { + this.n = N; + while ((this.n & (this.n - 1)) !== 0) { + this.n++; + } + this.tree = new Array(2 * this.n).fill(-Infinity); + } + + /** + * @param {number} i + * @param {number} val + * @return {void} + */ + update(i, val) { + i += this.n; + this.tree[i] = val; + while (i > 1) { + i >>= 1; + this.tree[i] = Math.max(this.tree[i << 1], this.tree[i << 1 | 1]); + } + } + + /** + * @param {number} l + * @param {number} r + * @return {number} + */ + query(l, r) { + let res = -Infinity; + l += this.n; + r += this.n + 1; + while (l < r) { + if (l & 1) { + res = Math.max(res, this.tree[l]); + l++; + } + if (r & 1) { + r--; + res = Math.max(res, this.tree[r]); + } + l >>= 1; + r >>= 1; + } + return Math.max(0, res); + } +} + +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {number} + */ + constrainedSubsetSum(nums, k) { + const n = nums.length; + const maxSegTree = new SegmentTree(n); + maxSegTree.update(0, nums[0]); + let res = nums[0]; + + for (let i = 1; i < n; i++) { + let cur = nums[i] + maxSegTree.query(Math.max(0, i - k), i - 1); + maxSegTree.update(i, cur); + res = Math.max(res, cur); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ + +--- + +## 4. Max-Heap + +::tabs-start + +```python +class Solution: + def constrainedSubsetSum(self, nums: List[int], k: int) -> int: + res = nums[0] + max_heap = [(-nums[0], 0)] # max_sum, index + + for i in range(1, len(nums)): + while i - max_heap[0][1] > k: + heapq.heappop(max_heap) + + cur_max = max(nums[i], nums[i] - max_heap[0][0]) + res = max(res, cur_max) + heapq.heappush(max_heap, (-cur_max, i)) + + return res +``` + +```java +public class Solution { + public int constrainedSubsetSum(int[] nums, int k) { + int res = nums[0]; + PriorityQueue maxHeap = new PriorityQueue<>( + (a, b) -> b[0] - a[0] // max_sum, index + ); + maxHeap.offer(new int[]{nums[0], 0}); + + for (int i = 1; i < nums.length; i++) { + while (i - maxHeap.peek()[1] > k) { + maxHeap.poll(); + } + + int curMax = Math.max(nums[i], nums[i] + maxHeap.peek()[0]); + res = Math.max(res, curMax); + maxHeap.offer(new int[]{curMax, i}); + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int constrainedSubsetSum(vector& nums, int k) { + int res = nums[0]; + priority_queue> maxHeap; // max_sum, index + maxHeap.emplace(nums[0], 0); + + for (int i = 1; i < nums.size(); i++) { + while (i - maxHeap.top().second > k) { + maxHeap.pop(); + } + + int curMax = max(nums[i], nums[i] + maxHeap.top().first); + res = max(res, curMax); + maxHeap.emplace(curMax, i); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {number} + */ + constrainedSubsetSum(nums, k) { + let res = nums[0]; + const maxHeap = new PriorityQueue( + (a, b) => b[0] - a[0] // max_sum, index + ); + maxHeap.enqueue([nums[0], 0]); + + for (let i = 1; i < nums.length; i++) { + while (i - maxHeap.front()[1] > k) { + maxHeap.dequeue(); + } + + let curMax = Math.max(nums[i], nums[i] + maxHeap.front()[0]); + res = Math.max(res, curMax); + maxHeap.enqueue([curMax, i]); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ + +--- + +## 5. Monotonic Deque + +::tabs-start + +```python +class Solution: + def constrainedSubsetSum(self, nums: List[int], k: int) -> int: + n = len(nums) + dq = deque([(0, nums[0])]) + res = nums[0] + + for i in range(1, n): + if dq and dq[0][0] < i - k: + dq.popleft() + + cur = max(0, dq[0][1]) + nums[i] + while dq and cur > dq[-1][1]: + dq.pop() + + dq.append((i, cur)) + res = max(res, cur) + + return res +``` + +```java +public class Solution { + public int constrainedSubsetSum(int[] nums, int k) { + int n = nums.length; + Deque dq = new ArrayDeque<>(); + dq.offer(new int[]{0, nums[0]}); + int res = nums[0]; + + for (int i = 1; i < n; i++) { + if (!dq.isEmpty() && dq.peekFirst()[0] < i - k) { + dq.pollFirst(); + } + + int cur = Math.max(0, dq.peekFirst()[1]) + nums[i]; + while (!dq.isEmpty() && cur > dq.peekLast()[1]) { + dq.pollLast(); + } + + dq.offer(new int[]{i, cur}); + res = Math.max(res, cur); + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int constrainedSubsetSum(vector& nums, int k) { + int n = nums.size(); + deque> dq{{0, nums[0]}}; + int res = nums[0]; + + for (int i = 1; i < n; i++) { + if (!dq.empty() && dq.front().first < i - k) { + dq.pop_front(); + } + + int cur = max(0, dq.front().second) + nums[i]; + while (!dq.empty() && cur > dq.back().second) { + dq.pop_back(); + } + + dq.emplace_back(i, cur); + res = max(res, cur); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {number} + */ + constrainedSubsetSum(nums, k) { + const n = nums.length; + const dq = new Deque([[0, nums[0]]]); + let res = nums[0]; + + for (let i = 1; i < n; i++) { + if (!dq.isEmpty() && dq.front()[0] < i - k) { + dq.popFront(); + } + + let cur = Math.max(0, dq.front()[1]) + nums[i]; + while (!dq.isEmpty() && cur > dq.back()[1]) { + dq.popBack(); + } + + dq.pushBack([i, cur]); + res = Math.max(res, cur); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(k)$ \ No newline at end of file diff --git a/articles/find-the-kth-largest-integer-in-the-array.md b/articles/find-the-kth-largest-integer-in-the-array.md new file mode 100644 index 000000000..ba70f2ed8 --- /dev/null +++ b/articles/find-the-kth-largest-integer-in-the-array.md @@ -0,0 +1,543 @@ +## 1. Sorting + +::tabs-start + +```python +class Solution: + def kthLargestNumber(self, nums: List[str], k: int) -> str: + return sorted(nums, key=lambda x: (len(x), x), reverse=True)[k - 1] +``` + +```java +public class Solution { + public String kthLargestNumber(String[] nums, int k) { + Arrays.sort(nums, + (a, b) -> a.length() == b.length() ? b.compareTo(a) : b.length() - a.length() + ); + return nums[k - 1]; + } +} +``` + +```cpp +class Solution { +public: + string kthLargestNumber(vector& nums, int k) { + sort(nums.begin(), nums.end(), [](const string& a, const string& b) { + return a.size() == b.size() ? a > b : a.size() > b.size(); + }); + return nums[k - 1]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} nums + * @param {number} k + * @return {string} + */ + kthLargestNumber(nums, k) { + nums.sort( + (a, b) => a.length === b.length ? b.localeCompare(a) : b.length - a.length + ); + return nums[k - 1]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n \log n)$ +* Space complexity: $O(1)$ or $O(n)$ depending on the sorting algorithm. + +> Where $n$ is the number of strings and $m$ is the average length of a string. + +--- + +## 2. Max-Heap + +::tabs-start + +```python +class Num: + def __init__(self, s: str): + self.s = s + + def __lt__(self, other: "Num") -> bool: + if len(self.s) != len(other.s): + return len(self.s) > len(other.s) + return self.s > other.s + +class Solution: + def kthLargestNumber(self, nums: List[str], k: int) -> str: + maxHeap = [Num(s) for s in nums] + heapq.heapify(maxHeap) + + for _ in range(k - 1): + heapq.heappop(maxHeap) + + return heapq.heappop(maxHeap).s +``` + +```java +public class Solution { + public String kthLargestNumber(String[] nums, int k) { + PriorityQueue maxHeap = new PriorityQueue<>((a, b) -> + a.length() == b.length() ? b.compareTo(a) : Integer.compare(b.length(), a.length()) + ); + + for (String num : nums) { + maxHeap.offer(num); + } + + while (--k > 0) { + maxHeap.poll(); + } + + return maxHeap.poll(); + } +} +``` + +```cpp +class Solution { +public: + string kthLargestNumber(vector& nums, int k) { + auto cmp = [](const string& a, const string& b) { + return a.size() == b.size() ? a < b : a.size() < b.size(); + }; + + priority_queue, decltype(cmp)> maxHeap(cmp); + + for (const string& num : nums) { + maxHeap.push(num); + } + + while (--k > 0) { + maxHeap.pop(); + } + + return maxHeap.top(); + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} nums + * @param {number} k + * @return {string} + */ + kthLargestNumber(nums, k) { + const maxHeap = new PriorityQueue( + (a, b) => a.length === b.length ? b.localeCompare(a) : b.length - a.length + ); + + for (const num of nums) { + maxHeap.enqueue(num); + } + + while (--k > 0) { + maxHeap.dequeue(); + } + + return maxHeap.dequeue(); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * (n + k) * \log n)$ +* Space complexity: $O(n)$ + +> Where $n$ is the number of strings and $m$ is the average length of a string. + +--- + +## 3. Min-Heap + +::tabs-start + +```python +class Num: + def __init__(self, s: str): + self.s = s + + def __lt__(self, other: "Num") -> bool: + if len(self.s) != len(other.s): + return len(self.s) < len(other.s) + return self.s < other.s + +class Solution: + def kthLargestNumber(self, nums: List[str], k: int) -> str: + minHeap = [] + for num in nums: + heapq.heappush(minHeap, Num(num)) + if len(minHeap) > k: + heapq.heappop(minHeap) + return minHeap[0].s +``` + +```java +public class Solution { + public String kthLargestNumber(String[] nums, int k) { + PriorityQueue minHeap = new PriorityQueue<>((a, b) -> + a.length() == b.length() ? a.compareTo(b) : Integer.compare(a.length(), b.length()) + ); + + for (String num : nums) { + minHeap.offer(num); + if (minHeap.size() > k) { + minHeap.poll(); + } + } + + return minHeap.peek(); + } +} +``` + +```cpp +class Solution { +public: + string kthLargestNumber(vector& nums, int k) { + auto cmp = [](const string& a, const string& b) { + return a.size() == b.size() ? a > b : a.size() > b.size(); + }; + + priority_queue, decltype(cmp)> minHeap(cmp); + + for (const string& num : nums) { + minHeap.push(num); + if (minHeap.size() > k) { + minHeap.pop(); + } + } + + return minHeap.top(); + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} nums + * @param {number} k + * @return {string} + */ + kthLargestNumber(nums, k) { + const minHeap = new PriorityQueue( + (a, b) => a.length === b.length ? a.localeCompare(b) : a.length - b.length + ); + + for (const num of nums) { + minHeap.enqueue(num); + if (minHeap.size() > k) { + minHeap.dequeue(); + } + } + + return minHeap.front(); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n * \log k)$ +* Space complexity: $O(k)$ + +> Where $n$ is the number of strings and $m$ is the average length of a string. + +--- + +## 4. Quick Select + +::tabs-start + +```python +class Solution: + def greater(self, x: str, y: str) -> bool: + if len(x) != len(y): + return len(x) > len(y) + return x > y + + def less(self, x: str, y: str) -> bool: + if len(x) != len(y): + return len(x) < len(y) + return x < y + + def partition(self, nums: List[str], left: int, right: int) -> int: + mid = (left + right) >> 1 + nums[mid], nums[left + 1] = nums[left + 1], nums[mid] + + if self.less(nums[left], nums[right]): + nums[left], nums[right] = nums[right], nums[left] + if self.less(nums[left + 1], nums[right]): + nums[left + 1], nums[right] = nums[right], nums[left + 1] + if self.less(nums[left], nums[left + 1]): + nums[left], nums[left + 1] = nums[left + 1], nums[left] + + pivot = nums[left + 1] + i = left + 1 + j = right + + while True: + while True: + i += 1 + if not self.greater(nums[i], pivot): + break + while True: + j -= 1 + if not self.less(nums[j], pivot): + break + if i > j: + break + nums[i], nums[j] = nums[j], nums[i] + + nums[left + 1], nums[j] = nums[j], nums[left + 1] + return j + + def quickSelect(self, nums: List[str], k: int) -> str: + left = 0 + right = len(nums) - 1 + + while True: + if right <= left + 1: + if right == left + 1 and self.greater(nums[right], nums[left]): + nums[left], nums[right] = nums[right], nums[left] + return nums[k] + + j = self.partition(nums, left, right) + if j >= k: + right = j - 1 + if j <= k: + left = j + 1 + + def kthLargestNumber(self, nums: List[str], k: int) -> str: + return self.quickSelect(nums, k - 1) +``` + +```java +public class Solution { + public String kthLargestNumber(String[] nums, int k) { + return quickSelect(nums, k - 1); + } + + private boolean greater(String x, String y) { + if (x.length() != y.length()) { + return x.length() > y.length(); + } + return x.compareTo(y) > 0; + } + + private boolean less(String x, String y) { + if (x.length() != y.length()) { + return x.length() < y.length(); + } + return x.compareTo(y) < 0; + } + + private int partition(String[] nums, int left, int right) { + int mid = (left + right) >> 1; + swap(nums, mid, left + 1); + + if (less(nums[left], nums[right])) { + swap(nums, left, right); + } + if (less(nums[left + 1], nums[right])) { + swap(nums, left + 1, right); + } + if (less(nums[left], nums[left + 1])) { + swap(nums, left, left + 1); + } + + String pivot = nums[left + 1]; + int i = left + 1, j = right; + + while (true) { + while (greater(nums[++i], pivot)); + while (less(nums[--j], pivot)); + if (i > j) break; + swap(nums, i, j); + } + + swap(nums, left + 1, j); + return j; + } + + private String quickSelect(String[] nums, int k) { + int left = 0, right = nums.length - 1; + + while (true) { + if (right <= left + 1) { + if (right == left + 1 && greater(nums[right], nums[left])) { + swap(nums, left, right); + } + return nums[k]; + } + + int j = partition(nums, left, right); + if (j >= k) { + right = j - 1; + } + if (j <= k) { + left = j + 1; + } + } + } + + private void swap(String[] nums, int i, int j) { + String temp = nums[i]; + nums[i] = nums[j]; + nums[j] = temp; + } +} +``` + +```cpp +class Solution { +public: + string kthLargestNumber(vector& nums, int k) { + return quickSelect(nums, k - 1); + } + +private: + bool greater(const string& x, const string& y) { + if (x.size() != y.size()) { + return x.size() > y.size(); + } + return x > y; + } + + bool less(const string& x, const string& y) { + if (x.size() != y.size()) { + return x.size() < y.size(); + } + return x < y; + } + + int partition(vector& nums, int left, int right) { + int mid = (left + right) >> 1; + swap(nums[mid], nums[left + 1]); + + if (less(nums[left], nums[right])) { + swap(nums[left], nums[right]); + } + if (less(nums[left + 1], nums[right])) { + swap(nums[left + 1], nums[right]); + } + if (less(nums[left], nums[left + 1])) { + swap(nums[left], nums[left + 1]); + } + + string pivot = nums[left + 1]; + int i = left + 1, j = right; + + while (true) { + while (greater(nums[++i], pivot)); + while (less(nums[--j], pivot)); + if (i > j) break; + swap(nums[i], nums[j]); + } + + swap(nums[left + 1], nums[j]); + return j; + } + + string quickSelect(vector& nums, int k) { + int left = 0, right = nums.size() - 1; + + while (true) { + if (right <= left + 1) { + if (right == left + 1 && greater(nums[right], nums[left])) { + swap(nums[left], nums[right]); + } + return nums[k]; + } + + int j = partition(nums, left, right); + if (j >= k) { + right = j - 1; + } + if (j <= k) { + left = j + 1; + } + } + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} nums + * @param {number} k + * @return {string} + */ + kthLargestNumber(nums, k) { + const greater = (x, y) => x.length !== y.length ? x.length > y.length : x > y; + const less = (x, y) => x.length !== y.length ? x.length < y.length : x < y; + const swap = (arr, i, j) => [arr[i], arr[j]] = [arr[j], arr[i]]; + + const partition = (nums, left, right) => { + const mid = Math.floor((left + right) / 2); + swap(nums, mid, left + 1); + + if (less(nums[left], nums[right])) swap(nums, left, right); + if (less(nums[left + 1], nums[right])) swap(nums, left + 1, right); + if (less(nums[left], nums[left + 1])) swap(nums, left, left + 1); + + const pivot = nums[left + 1]; + let i = left + 1, j = right; + + while (true) { + while (greater(nums[++i], pivot)); + while (less(nums[--j], pivot)); + if (i > j) break; + swap(nums, i, j); + } + + swap(nums, left + 1, j); + return j; + }; + + const quickSelect = (nums, k) => { + let left = 0, right = nums.length - 1; + + while (true) { + if (right <= left + 1) { + if (right === left + 1 && greater(nums[right], nums[left])) { + swap(nums, left, right); + } + return nums[k]; + } + + const j = partition(nums, left, right); + if (j >= k) right = j - 1; + if (j <= k) left = j + 1; + } + }; + + return quickSelect(nums, k - 1); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n)$ in average case, $O(m * n ^ 2)$ in worst case. +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/find-the-longest-valid-obstacle-course-at-each-position.md b/articles/find-the-longest-valid-obstacle-course-at-each-position.md new file mode 100644 index 000000000..ff2346555 --- /dev/null +++ b/articles/find-the-longest-valid-obstacle-course-at-each-position.md @@ -0,0 +1,387 @@ +## 1. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def longestObstacleCourseAtEachPosition(self, obstacles: List[int]) -> List[int]: + n = len(obstacles) + dp = [[-1] * (n + 1) for _ in range(n)] + + def dfs(i, prev): + if i < 0: + return 0 + if dp[i][prev] != -1: + return dp[i][prev] + + res = dfs(i - 1, prev) + if prev == n or obstacles[prev] >= obstacles[i]: + res = max(res, 1 + dfs(i - 1, i)) + dp[i][prev] = res + return res + + dfs(n - 1, n) + return [1] + [1 + dp[i - 1][i] for i in range(1, n)] +``` + +```java +public class Solution { + private int[][] dp; + + public int[] longestObstacleCourseAtEachPosition(int[] obstacles) { + int n = obstacles.length; + this.dp = new int[n][n + 1]; + for (int[] row : dp) { + Arrays.fill(row, -1); + } + + dfs(n - 1, n, obstacles); + + int[] res = new int[n]; + res[0] = 1; + for (int i = 1; i < n; i++) { + res[i] = 1 + dp[i - 1][i]; + } + return res; + } + + private int dfs(int i, int prev, int[] obstacles) { + if (i < 0) { + return 0; + } + if (dp[i][prev] != -1) { + return dp[i][prev]; + } + + int res = dfs(i - 1, prev, obstacles); + if (prev == obstacles.length || obstacles[prev] >= obstacles[i]) { + res = Math.max(res, 1 + dfs(i - 1, i, obstacles)); + } + return dp[i][prev] = res; + } +} +``` + +```cpp +class Solution { +public: + vector> dp; + + vector longestObstacleCourseAtEachPosition(vector& obstacles) { + int n = obstacles.size(); + this->dp = vector>(n, vector(n + 1, -1)); + + dfs(n - 1, n, obstacles); + + vector res(n, 1); + for (int i = 1; i < n; i++) { + res[i] = 1 + dp[i - 1][i]; + } + return res; + } + +private: + int dfs(int i, int prev, vector& obstacles) { + if (i < 0) { + return 0; + } + if (dp[i][prev] != -1) { + return dp[i][prev]; + } + + int res = dfs(i - 1, prev, obstacles); + if (prev == obstacles.size() || obstacles[prev] >= obstacles[i]) { + res = max(res, 1 + dfs(i - 1, i, obstacles)); + } + return dp[i][prev] = res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} obstacles + * @return {number[]} + */ + longestObstacleCourseAtEachPosition(obstacles) { + const n = obstacles.length; + const dp = Array.from({ length: n }, () => new Array(n + 1).fill(-1)); + + const dfs = (i, prev) => { + if (i < 0) { + return 0; + } + if (dp[i][prev] !== -1) { + return dp[i][prev]; + } + + let res = dfs(i - 1, prev); + if (prev === n || obstacles[prev] >= obstacles[i]) { + res = Math.max(res, 1 + dfs(i - 1, i)); + } + dp[i][prev] = res; + return res; + }; + + dfs(n - 1, n); + + const res = new Array(n).fill(1); + for (let i = 1; i < n; i++) { + res[i] = 1 + dp[i - 1][i]; + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n ^ 2)$ + +--- + +## 2. Dynamic Programming (Binary Search) - I + +::tabs-start + +```python +class Solution: + def longestObstacleCourseAtEachPosition(self, obstacles: List[int]) -> List[int]: + res = [] + dp = [10**8] * (len(obstacles) + 1) + + for num in obstacles: + index = bisect.bisect(dp, num) + res.append(index + 1) + dp[index] = num + + return res +``` + +```java +public class Solution { + public int[] longestObstacleCourseAtEachPosition(int[] obstacles) { + int n = obstacles.length; + int[] res = new int[n]; + int[] dp = new int[n + 1]; + Arrays.fill(dp, (int) 1e8); + + for (int i = 0; i < n; i++) { + int index = upperBound(dp, obstacles[i]); + res[i] = index + 1; + dp[index] = obstacles[i]; + } + + return res; + } + + private int upperBound(int[] dp, int target) { + int left = 0, right = dp.length; + while (left < right) { + int mid = left + (right - left) / 2; + if (dp[mid] > target) { + right = mid; + } else { + left = mid + 1; + } + } + return left; + } +} +``` + +```cpp +class Solution { +public: + vector longestObstacleCourseAtEachPosition(vector& obstacles) { + int n = obstacles.size(); + vector res(n); + vector dp(n + 1, 1e8); + + for (int i = 0; i < n; i++) { + int index = upper_bound(dp.begin(), dp.end(), obstacles[i]) - dp.begin(); + res[i] = index + 1; + dp[index] = obstacles[i]; + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} obstacles + * @return {number[]} + */ + longestObstacleCourseAtEachPosition(obstacles) { + let n = obstacles.length; + let res = new Array(n).fill(0); + let dp = new Array(n + 1).fill(1e8); + + const upperBound = (dp, target) => { + let left = 0, right = dp.length; + while (left < right) { + let mid = Math.floor((left + right) / 2); + if (dp[mid] > target) { + right = mid; + } else { + left = mid + 1; + } + } + return left; + }; + + for (let i = 0; i < n; i++) { + let index = upperBound(dp, obstacles[i]); + res[i] = index + 1; + dp[index] = obstacles[i]; + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Dynamic Programming (Binary Search) - II + +::tabs-start + +```python +class Solution: + def longestObstacleCourseAtEachPosition(self, obstacles: List[int]) -> List[int]: + res = [] + dp = [] + + for num in obstacles: + index = bisect.bisect_right(dp, num) + res.append(index + 1) + + if index == len(dp): + dp.append(num) + else: + dp[index] = num + + return res +``` + +```java +public class Solution { + public int[] longestObstacleCourseAtEachPosition(int[] obstacles) { + int n = obstacles.length; + int[] res = new int[n]; + List dp = new ArrayList<>(); + + for (int i = 0; i < n; i++) { + int index = upperBound(dp, obstacles[i]); + res[i] = index + 1; + + if (index == dp.size()) { + dp.add(obstacles[i]); + } else { + dp.set(index, obstacles[i]); + } + } + + return res; + } + + private int upperBound(List dp, int target) { + int left = 0, right = dp.size(); + while (left < right) { + int mid = left + (right - left) / 2; + if (dp.get(mid) > target) { + right = mid; + } else { + left = mid + 1; + } + } + return left; + } +} +``` + +```cpp +class Solution { +public: + vector longestObstacleCourseAtEachPosition(vector& obstacles) { + int n = obstacles.size(); + vector res(n); + vector dp; + + for (int i = 0; i < n; i++) { + int index = upper_bound(dp.begin(), dp.end(), obstacles[i]) - dp.begin(); + res[i] = index + 1; + + if (index == dp.size()) { + dp.push_back(obstacles[i]); + } else { + dp[index] = obstacles[i]; + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} obstacles + * @return {number[]} + */ + longestObstacleCourseAtEachPosition(obstacles) { + let n = obstacles.length; + let res = new Array(n).fill(0); + let dp = []; + + const upperBound = (dp, target) => { + let left = 0, right = dp.length; + while (left < right) { + let mid = Math.floor((left + right) / 2); + if (dp[mid] > target) { + right = mid; + } else { + left = mid + 1; + } + } + return left; + }; + + for (let i = 0; i < n; i++) { + let index = upperBound(dp, obstacles[i]); + res[i] = index + 1; + + if (index === dp.length) { + dp.push(obstacles[i]); + } else { + dp[index] = obstacles[i]; + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/least-number-of-unique-integers-after-k-removals.md b/articles/least-number-of-unique-integers-after-k-removals.md new file mode 100644 index 000000000..ddffbfb03 --- /dev/null +++ b/articles/least-number-of-unique-integers-after-k-removals.md @@ -0,0 +1,343 @@ +## 1. Sorting + +::tabs-start + +```python +class Solution: + def findLeastNumOfUniqueInts(self, arr: List[int], k: int) -> int: + freq = sorted(Counter(arr).values()) + n = len(freq) + for i in range(n): + if k >= freq[i]: + k -= freq[i] + else: + return n - i + return 0 +``` + +```java +public class Solution { + public int findLeastNumOfUniqueInts(int[] arr, int k) { + Map freqMap = new HashMap<>(); + for (int num : arr) { + freqMap.put(num, freqMap.getOrDefault(num, 0) + 1); + } + + List freq = new ArrayList<>(freqMap.values()); + Collections.sort(freq); + + int n = freq.size(); + for (int i = 0; i < n; i++) { + if (k >= freq.get(i)) { + k -= freq.get(i); + } else { + return n - i; + } + } + return 0; + } +} +``` + +```cpp +class Solution { +public: + int findLeastNumOfUniqueInts(vector& arr, int k) { + unordered_map freqMap; + for (int num : arr) { + freqMap[num]++; + } + + vector freq; + for (auto& [_, count] : freqMap) { + freq.push_back(count); + } + + sort(freq.begin(), freq.end()); + + int n = freq.size(); + for (int i = 0; i < n; i++) { + if (k >= freq[i]) { + k -= freq[i]; + } else { + return n - i; + } + } + return 0; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} arr + * @param {number} k + * @return {number} + */ + findLeastNumOfUniqueInts(arr, k) { + let freqMap = new Map(); + for (let num of arr) { + freqMap.set(num, (freqMap.get(num) || 0) + 1); + } + + let freq = Array.from(freqMap.values()).sort((a, b) => a - b); + + let n = freq.length; + for (let i = 0; i < n; i++) { + if (k >= freq[i]) { + k -= freq[i]; + } else { + return n - i; + } + } + return 0; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Min-Heap + +::tabs-start + +```python +class Solution: + def findLeastNumOfUniqueInts(self, arr: List[int], k: int) -> int: + freq = Counter(arr) + heap = list(freq.values()) + heapq.heapify(heap) + + res = len(heap) + while k > 0 and heap: + f = heapq.heappop(heap) + if k >= f: + k -= f + res -= 1 + return res +``` + +```java +public class Solution { + public int findLeastNumOfUniqueInts(int[] arr, int k) { + Map freqMap = new HashMap<>(); + for (int num : arr) { + freqMap.put(num, freqMap.getOrDefault(num, 0) + 1); + } + + PriorityQueue minHeap = new PriorityQueue<>(freqMap.values()); + + int res = minHeap.size(); + while (k > 0 && !minHeap.isEmpty()) { + int f = minHeap.poll(); + if (k >= f) { + k -= f; + res--; + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int findLeastNumOfUniqueInts(vector& arr, int k) { + unordered_map freqMap; + for (int num : arr) { + freqMap[num]++; + } + + priority_queue, greater> minHeap; + for (auto& [_, count] : freqMap) { + minHeap.push(count); + } + + int res = minHeap.size(); + while (k > 0 && !minHeap.empty()) { + int f = minHeap.top(); + minHeap.pop(); + if (k >= f) { + k -= f; + res--; + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} arr + * @param {number} k + * @return {number} + */ + findLeastNumOfUniqueInts(arr, k) { + let freqMap = new Map(); + for (let num of arr) { + freqMap.set(num, (freqMap.get(num) || 0) + 1); + } + + const minHeap = MinPriorityQueue.fromArray([...freqMap.values()]); + + let res = minHeap.size(); + while (k > 0 && !minHeap.isEmpty()) { + let f = minHeap.pop(); + if (k >= f) { + k -= f; + res--; + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Bucket Sort + +::tabs-start + +```python +class Solution: + def findLeastNumOfUniqueInts(self, arr: List[int], k: int) -> int: + freq = Counter(arr) + freq_list = [0] * (len(arr) + 1) + + for n, f in freq.items(): + freq_list[f] += 1 + + res = len(freq) + for f in range(1, len(freq_list)): + remove = freq_list[f] + if k >= f * remove: + k -= f * remove + res -= remove + else: + remove = k // f + res -= remove + break + return res +``` + +```java +public class Solution { + public int findLeastNumOfUniqueInts(int[] arr, int k) { + Map freqMap = new HashMap<>(); + for (int num : arr) { + freqMap.put(num, freqMap.getOrDefault(num, 0) + 1); + } + + int[] freqList = new int[arr.length + 1]; + for (int f : freqMap.values()) { + freqList[f]++; + } + + int res = freqMap.size(); + for (int f = 1; f < freqList.length; f++) { + int remove = freqList[f]; + if (k >= f * remove) { + k -= f * remove; + res -= remove; + } else { + remove = k / f; + res -= remove; + break; + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int findLeastNumOfUniqueInts(vector& arr, int k) { + unordered_map freqMap; + for (int num : arr) { + freqMap[num]++; + } + + vector freqList(arr.size() + 1, 0); + for (auto& [_, f] : freqMap) { + freqList[f]++; + } + + int res = freqMap.size(); + for (int f = 1; f < freqList.size(); f++) { + int remove = freqList[f]; + if (k >= f * remove) { + k -= f * remove; + res -= remove; + } else { + remove = k / f; + res -= remove; + break; + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} arr + * @param {number} k + * @return {number} + */ + findLeastNumOfUniqueInts(arr, k) { + let freqMap = new Map(); + for (let num of arr) { + freqMap.set(num, (freqMap.get(num) || 0) + 1); + } + + let freqList = new Array(arr.length + 1).fill(0); + for (let f of freqMap.values()) { + freqList[f]++; + } + + let res = freqMap.size; + for (let f = 1; f < freqList.length; f++) { + let remove = freqList[f]; + if (k >= f * remove) { + k -= f * remove; + res -= remove; + } else { + remove = Math.floor(k / f); + res -= remove; + break; + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/maximum-performance-of-a-team.md b/articles/maximum-performance-of-a-team.md new file mode 100644 index 000000000..3fc63e969 --- /dev/null +++ b/articles/maximum-performance-of-a-team.md @@ -0,0 +1,238 @@ +## 1. Brute Force (Recursion) + +::tabs-start + +```python +class Solution: + def maxPerformance(self, n: int, speed: List[int], efficiency: List[int], k: int) -> int: + MOD = 1000000007 + res = 0 + + def dfs(i, k, speedSum, minEff): + nonlocal res + res = max(res, speedSum * minEff) + if i == n or k == 0: + return + + dfs(i + 1, k, speedSum, minEff) + dfs(i + 1, k - 1, speedSum + speed[i], min(minEff, efficiency[i])) + + dfs(0, k, 0, float("inf")) + return res % MOD +``` + +```java +public class Solution { + private static final int MOD = 1000000007; + private int[] speed, efficiency; + private int n; + private long res; + + public int maxPerformance(int n, int[] speed, int[] efficiency, int k) { + this.n = n; + this.speed = speed; + this.efficiency = efficiency; + this.res = 0; + + dfs(0, k, Integer.MAX_VALUE, 0); + return (int) (res % MOD); + } + + private void dfs(int i, int k, int minEff, long speedSum) { + res = Math.max(res, speedSum * minEff); + if (i == n || k == 0) return; + + dfs(i + 1, k, minEff, speedSum); + dfs(i + 1, k - 1, Math.min(minEff, efficiency[i]), speedSum + speed[i]); + } +} +``` + +```cpp +class Solution { +private: + static constexpr int MOD = 1000000007; + vector speed, efficiency; + int n; + long long res; + +public: + int maxPerformance(int n, vector& speed, vector& efficiency, int k) { + this->n = n; + this->speed = speed; + this->efficiency = efficiency; + res = 0; + + dfs(0, k, INT_MAX, 0); + return int(res % MOD); + } + +private: + void dfs(int i, int k, int minEff, long long speedSum) { + res = max(res, speedSum * minEff); + if (i == n || k == 0) return; + + dfs(i + 1, k, minEff, speedSum); + dfs(i + 1, k - 1, min(minEff, efficiency[i]), speedSum + speed[i]); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number[]} speed + * @param {number[]} efficiency + * @param {number} k + * @return {number} + */ + maxPerformance(n, speed, efficiency, k) { + const MOD = 1000000007; + let res = 0; + + const dfs = (i, k, minEff, speedSum) => { + res = Math.max(res, minEff === Infinity ? 0 : speedSum * minEff); + if (i === n || k === 0) return; + + dfs(i + 1, k, minEff, speedSum); + dfs(i + 1, k - 1, Math.min(minEff, efficiency[i]), speedSum + speed[i]); + }; + + dfs(0, k, Infinity, 0); + return res % MOD; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(2 ^ n)$ +* Space complexity: $O(n)$ for recursion stack. + +--- + +## 2. Sorting + Min-Heap + +::tabs-start + +```python +class Solution: + def maxPerformance(self, n: int, speed: List[int], efficiency: List[int], k: int) -> int: + MOD = 10**9 + 7 + eng = sorted(zip(efficiency, speed), reverse=True) + + res = speedSum = 0 + minHeap = [] + + for eff, spd in eng: + if len(minHeap) == k: + speedSum -= heapq.heappop(minHeap) + speedSum += spd + heapq.heappush(minHeap, spd) + res = max(res, eff * speedSum) + + return res % MOD +``` + +```java +public class Solution { + public int maxPerformance(int n, int[] speed, int[] efficiency, int k) { + final int MOD = 1_000_000_007; + int[][] engineers = new int[n][2]; + + for (int i = 0; i < n; i++) { + engineers[i] = new int[]{efficiency[i], speed[i]}; + } + + Arrays.sort(engineers, (a, b) -> Integer.compare(b[0], a[0])); + + PriorityQueue minHeap = new PriorityQueue<>(); + long speedSum = 0, res = 0; + + for (int[] eng : engineers) { + if (minHeap.size() == k) { + speedSum -= minHeap.poll(); + } + speedSum += eng[1]; + minHeap.offer(eng[1]); + res = Math.max(res, speedSum * eng[0]); + } + + return (int) (res % MOD); + } +} +``` + +```cpp +class Solution { +public: + int maxPerformance(int n, vector& speed, vector& efficiency, int k) { + constexpr int MOD = 1'000'000'007; + vector> engineers; + + for (int i = 0; i < n; i++) { + engineers.emplace_back(efficiency[i], speed[i]); + } + + sort(engineers.rbegin(), engineers.rend()); + + priority_queue, greater> minHeap; + long long speedSum = 0, res = 0; + + for (const auto& [eff, spd] : engineers) { + if (minHeap.size() == k) { + speedSum -= minHeap.top(); + minHeap.pop(); + } + speedSum += spd; + minHeap.push(spd); + res = max(res, speedSum * eff); + } + + return res % MOD; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number[]} speed + * @param {number[]} efficiency + * @param {number} k + * @return {number} + */ + maxPerformance(n, speed, efficiency, k) { + const MOD = BigInt(1e9 + 7); + const engineers = efficiency.map((eff, i) => [eff, speed[i]]); + engineers.sort((a, b) => b[0] - a[0]); + + const minHeap = new MinPriorityQueue(); + let speedSum = BigInt(0), res = BigInt(0); + + for (const [eff, spd] of engineers) { + if (minHeap.size() === k) { + speedSum -= BigInt(minHeap.dequeue()); + } + speedSum += BigInt(spd); + minHeap.enqueue(spd); + res = res > speedSum * BigInt(eff) ? res : speedSum * BigInt(eff); + } + + return Number(res % MOD); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * (\log n + \log k))$ +* Space complexity: $O(n + k)$ + +> Where $n$ is the number of engineers and $k$ is the maximum number of engineers that can be selected. \ No newline at end of file diff --git a/articles/maximum-profit-in-job-scheduling.md b/articles/maximum-profit-in-job-scheduling.md new file mode 100644 index 000000000..181614b62 --- /dev/null +++ b/articles/maximum-profit-in-job-scheduling.md @@ -0,0 +1,655 @@ +## 1. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def jobScheduling(self, startTime: List[int], endTime: List[int], profit: List[int]) -> int: + intervals = sorted(zip(startTime, endTime, profit)) + cache = {} + + def dfs(i): + if i == len(intervals): + return 0 + if i in cache: + return cache[i] + + # don't include + res = dfs(i + 1) + + # include + j = i + 1 + while j < len(intervals): + if intervals[i][1] <= intervals[j][0]: + break + j += 1 + + cache[i] = res = max(res, intervals[i][2] + dfs(j)) + return res + + return dfs(0) +``` + +```java +public class Solution { + private int[][] intervals; + private int[] cache; + + public int jobScheduling(int[] startTime, int[] endTime, int[] profit) { + int n = startTime.length; + intervals = new int[n][3]; + cache = new int[n]; + Arrays.fill(cache, -1); + + for (int i = 0; i < n; i++) { + intervals[i] = new int[]{startTime[i], endTime[i], profit[i]}; + } + Arrays.sort(intervals, Comparator.comparingInt(a -> a[0])); + + return dfs(0); + } + + private int dfs(int i) { + if (i == intervals.length) { + return 0; + } + if (cache[i] != -1) { + return cache[i]; + } + + // Don't include + int res = dfs(i + 1); + + // Include + int j = i + 1; + while (j < intervals.length && intervals[i][1] > intervals[j][0]) { + j++; + } + + return cache[i] = Math.max(res, intervals[i][2] + dfs(j)); + } +} +``` + +```cpp +class Solution { +public: + vector> intervals; + vector cache; + + int jobScheduling(vector& startTime, vector& endTime, vector& profit) { + int n = startTime.size(); + intervals.resize(n, vector(3)); + cache.assign(n, -1); + + for (int i = 0; i < n; i++) { + intervals[i] = {startTime[i], endTime[i], profit[i]}; + } + sort(intervals.begin(), intervals.end()); + + return dfs(0); + } + +private: + int dfs(int i) { + if (i == intervals.size()) { + return 0; + } + if (cache[i] != -1) { + return cache[i]; + } + + // Don't include + int res = dfs(i + 1); + + // Include + int j = i + 1; + while (j < intervals.size() && intervals[i][1] > intervals[j][0]) { + j++; + } + + return cache[i] = max(res, intervals[i][2] + dfs(j)); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} startTime + * @param {number[]} endTime + * @param {number[]} profit + * @return {number} + */ + jobScheduling(startTime, endTime, profit) { + let n = startTime.length; + let intervals = new Array(n).fill(null).map((_, i) => + [startTime[i], endTime[i], profit[i]] + ); + intervals.sort((a, b) => a[0] - b[0]); + + let cache = new Array(n).fill(-1); + + const dfs = (i) => { + if (i === n) { + return 0; + } + if (cache[i] !== -1) { + return cache[i]; + } + + // Don't include + let res = dfs(i + 1); + + // Include + let j = i + 1; + while (j < n && intervals[i][1] > intervals[j][0]) { + j++; + } + + return (cache[i] = Math.max(res, intervals[i][2] + dfs(j))); + }; + + return dfs(0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n)$ + +--- + +## 2. Dynamic Programming (Top-Down) + Binary Search + +::tabs-start + +```python +class Solution: + def jobScheduling(self, startTime: List[int], endTime: List[int], profit: List[int]) -> int: + intervals = sorted(zip(startTime, endTime, profit)) + cache = {} + + def dfs(i): + if i == len(intervals): + return 0 + if i in cache: + return cache[i] + + # don't include + res = dfs(i + 1) + + # include + j = bisect.bisect(intervals, (intervals[i][1], -1, -1)) + cache[i] = res = max(res, intervals[i][2] + dfs(j)) + return res + + return dfs(0) +``` + +```java +public class Solution { + private int[][] intervals; + private int[] cache; + + public int jobScheduling(int[] startTime, int[] endTime, int[] profit) { + int n = startTime.length; + intervals = new int[n][3]; + cache = new int[n]; + Arrays.fill(cache, -1); + + for (int i = 0; i < n; i++) { + intervals[i] = new int[]{startTime[i], endTime[i], profit[i]}; + } + Arrays.sort(intervals, Comparator.comparingInt(a -> a[0])); + + return dfs(0); + } + + private int dfs(int i) { + if (i == intervals.length) { + return 0; + } + if (cache[i] != -1) { + return cache[i]; + } + + int res = dfs(i + 1); + + int left = i + 1, right = intervals.length, j = intervals.length; + while (left < right) { + int mid = left + (right - left) / 2; + if (intervals[mid][0] >= intervals[i][1]) { + j = mid; + right = mid; + } else { + left = mid + 1; + } + } + + return cache[i] = Math.max(res, intervals[i][2] + dfs(j)); + } +} +``` + +```cpp +class Solution { +public: + vector> intervals; + vector cache; + + int jobScheduling(vector& startTime, vector& endTime, vector& profit) { + int n = startTime.size(); + intervals.resize(n, vector(3)); + cache.assign(n, -1); + + for (int i = 0; i < n; i++) { + intervals[i] = {startTime[i], endTime[i], profit[i]}; + } + sort(intervals.begin(), intervals.end()); + + return dfs(0); + } + +private: + int dfs(int i) { + if (i == intervals.size()) { + return 0; + } + if (cache[i] != -1) { + return cache[i]; + } + + int res = dfs(i + 1); + + int left = i + 1, right = intervals.size(), j = intervals.size(); + while (left < right) { + int mid = left + (right - left) / 2; + if (intervals[mid][0] >= intervals[i][1]) { + j = mid; + right = mid; + } else { + left = mid + 1; + } + } + + return cache[i] = max(res, intervals[i][2] + dfs(j)); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} startTime + * @param {number[]} endTime + * @param {number[]} profit + * @return {number} + */ + jobScheduling(startTime, endTime, profit) { + let n = startTime.length; + let intervals = new Array(n).fill(null).map((_, i) => + [startTime[i], endTime[i], profit[i]] + ); + intervals.sort((a, b) => a[0] - b[0]); + + let cache = new Array(n).fill(-1); + + const dfs = (i) => { + if (i === n) { + return 0; + } + if (cache[i] !== -1) { + return cache[i]; + } + + let res = dfs(i + 1); + + let left = i + 1, right = n, j = n; + while (left < right) { + let mid = Math.floor((left + right) / 2); + if (intervals[mid][0] >= intervals[i][1]) { + j = mid; + right = mid; + } else { + left = mid + 1; + } + } + + return (cache[i] = Math.max(res, intervals[i][2] + dfs(j))); + }; + + return dfs(0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Dynamic Programming (Top-Down) + Binary Search (Optimal) + +::tabs-start + +```python +class Solution: + def jobScheduling(self, startTime: List[int], endTime: List[int], profit: List[int]) -> int: + n = len(startTime) + index = list(range(n)) + index.sort(key=lambda i: startTime[i]) + + cache = [-1] * n + + def dfs(i): + if i == n: + return 0 + if cache[i] != -1: + return cache[i] + + res = dfs(i + 1) + + left, right, j = i + 1, n, n + while left < right: + mid = (left + right) // 2 + if startTime[index[mid]] >= endTime[index[i]]: + j = mid + right = mid + else: + left = mid + 1 + + cache[i] = res = max(res, profit[index[i]] + dfs(j)) + return res + + return dfs(0) +``` + +```java +public class Solution { + private int[] startTime, endTime, profit, cache; + private Integer[] index; + private int n; + + public int jobScheduling(int[] startTime, int[] endTime, int[] profit) { + this.n = startTime.length; + this.startTime = startTime; + this.endTime = endTime; + this.profit = profit; + this.index = new Integer[n]; + this.cache = new int[n]; + Arrays.fill(cache, -1); + + for (int i = 0; i < n; i++) { + index[i] = i; + } + Arrays.sort(index, Comparator.comparingInt(i -> startTime[i])); + + return dfs(0); + } + + private int dfs(int i) { + if (i == n) { + return 0; + } + if (cache[i] != -1) { + return cache[i]; + } + + int res = dfs(i + 1); + + int left = i + 1, right = n, j = n; + while (left < right) { + int mid = left + (right - left) / 2; + if (startTime[index[mid]] >= endTime[index[i]]) { + j = mid; + right = mid; + } else { + left = mid + 1; + } + } + + return cache[i] = Math.max(res, profit[index[i]] + dfs(j)); + } +} +``` + +```cpp +class Solution { +public: + vector startTime, endTime, profit, index, cache; + int n; + + int jobScheduling(vector& startTime, vector& endTime, vector& profit) { + this->n = startTime.size(); + this->startTime = startTime; + this->endTime = endTime; + this->profit = profit; + this->index.resize(n); + this->cache.assign(n, -1); + + for (int i = 0; i < n; i++) { + index[i] = i; + } + sort(index.begin(), index.end(), [&](int i, int j) { + return startTime[i] < startTime[j]; + }); + + return dfs(0); + } + +private: + int dfs(int i) { + if (i == n) { + return 0; + } + if (cache[i] != -1) { + return cache[i]; + } + + int res = dfs(i + 1); + + int left = i + 1, right = n, j = n; + while (left < right) { + int mid = left + (right - left) / 2; + if (startTime[index[mid]] >= endTime[index[i]]) { + j = mid; + right = mid; + } else { + left = mid + 1; + } + } + + return cache[i] = max(res, profit[index[i]] + dfs(j)); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} startTime + * @param {number[]} endTime + * @param {number[]} profit + * @return {number} + */ + jobScheduling(startTime, endTime, profit) { + let n = startTime.length; + let index = Array.from({ length: n }, (_, i) => i); + index.sort((a, b) => startTime[a] - startTime[b]); + + let cache = new Array(n).fill(-1); + + const dfs = (i) => { + if (i === n) { + return 0; + } + if (cache[i] !== -1) { + return cache[i]; + } + + let res = dfs(i + 1); + + let left = i + 1, right = n, j = n; + while (left < right) { + let mid = Math.floor((left + right) / 2); + if (startTime[index[mid]] >= endTime[index[i]]) { + j = mid; + right = mid; + } else { + left = mid + 1; + } + } + + return (cache[i] = Math.max(res, profit[index[i]] + dfs(j))); + }; + + return dfs(0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ + +--- + +## 4. Dynamic Programming (Bottom-Up) + Binary Search + +::tabs-start + +```python +class Solution: + def jobScheduling(self, startTime: List[int], endTime: List[int], profit: List[int]) -> int: + n = len(startTime) + index = list(range(n)) + index.sort(key=lambda i: startTime[i]) + + dp = [0] * (n + 1) + + for i in range(n - 1, -1, -1): + left, right, j = i + 1, n, n + while left < right: + mid = (left + right) // 2 + if startTime[index[mid]] >= endTime[index[i]]: + j = mid + right = mid + else: + left = mid + 1 + + dp[i] = max(dp[i + 1], profit[index[i]] + dp[j]) + + return dp[0] +``` + +```java +public class Solution { + public int jobScheduling(int[] startTime, int[] endTime, int[] profit) { + int n = startTime.length; + Integer[] index = new Integer[n]; + for (int i = 0; i < n; i++) index[i] = i; + Arrays.sort(index, Comparator.comparingInt(i -> startTime[i])); + + int[] dp = new int[n + 1]; + + for (int i = n - 1; i >= 0; i--) { + int left = i + 1, right = n, j = n; + while (left < right) { + int mid = left + (right - left) / 2; + if (startTime[index[mid]] >= endTime[index[i]]) { + j = mid; + right = mid; + } else { + left = mid + 1; + } + } + dp[i] = Math.max(dp[i + 1], profit[index[i]] + dp[j]); + } + + return dp[0]; + } +} +``` + +```cpp +class Solution { +public: + int jobScheduling(vector& startTime, vector& endTime, vector& profit) { + int n = startTime.size(); + vector index(n), dp(n + 1, 0); + for (int i = 0; i < n; i++) index[i] = i; + sort(index.begin(), index.end(), [&](int i, int j) { + return startTime[i] < startTime[j]; + }); + + for (int i = n - 1; i >= 0; i--) { + int left = i + 1, right = n, j = n; + while (left < right) { + int mid = left + (right - left) / 2; + if (startTime[index[mid]] >= endTime[index[i]]) { + j = mid; + right = mid; + } else { + left = mid + 1; + } + } + dp[i] = max(dp[i + 1], profit[index[i]] + dp[j]); + } + + return dp[0]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} startTime + * @param {number[]} endTime + * @param {number[]} profit + * @return {number} + */ + jobScheduling(startTime, endTime, profit) { + let n = startTime.length; + let index = Array.from({ length: n }, (_, i) => i); + index.sort((a, b) => startTime[a] - startTime[b]); + + let dp = new Array(n + 1).fill(0); + + for (let i = n - 1; i >= 0; i--) { + let left = i + 1, right = n, j = n; + while (left < right) { + let mid = Math.floor((left + right) / 2); + if (startTime[index[mid]] >= endTime[index[i]]) { + j = mid; + right = mid; + } else { + left = mid + 1; + } + } + dp[i] = Math.max(dp[i + 1], profit[index[i]] + dp[j]); + } + + return dp[0]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/maximum-subsequence-score.md b/articles/maximum-subsequence-score.md new file mode 100644 index 000000000..97c461caa --- /dev/null +++ b/articles/maximum-subsequence-score.md @@ -0,0 +1,412 @@ +## 1. Brute Force (Recursion) + +::tabs-start + +```python +class Solution: + def maxScore(self, nums1: List[int], nums2: List[int], k: int) -> int: + n = len(nums1) + + def dfs(i, k, minVal, curSum): + if k == 0: + return curSum * minVal + if i == n or (n - i) < k: + return float("-inf") + if minVal == 0: + return 0 + + res = dfs(i + 1, k, minVal, curSum) + res = max(res, dfs(i + 1, k - 1, min(minVal, nums2[i]), curSum + nums1[i])) + return res + + return dfs(0, k, float("inf"), 0) +``` + +```java +public class Solution { + private int[] nums1, nums2; + private int n; + + public long maxScore(int[] nums1, int[] nums2, int k) { + this.nums1 = nums1; + this.nums2 = nums2; + this.n = nums1.length; + return dfs(0, k, Integer.MAX_VALUE, 0); + } + + private long dfs(int i, int k, int minVal, long curSum) { + if (k == 0) { + return curSum * minVal; + } + if (i == n || (n - i) < k) { + return Integer.MIN_VALUE; + } + if (minVal == 0) { + return 0; + } + + long res = dfs(i + 1, k, minVal, curSum); + res = Math.max( + res, + dfs(i + 1, k - 1, Math.min(minVal, nums2[i]), curSum + nums1[i]) + ); + return res; + } +} +``` + +```cpp +class Solution { +private: + vector nums1, nums2; + int n; + +public: + long long maxScore(vector& nums1, vector& nums2, int k) { + this->nums1 = nums1; + this->nums2 = nums2; + this->n = nums1.size(); + return dfs(0, k, INT_MAX, 0); + } + +private: + long long dfs(int i, int k, int minVal, long long curSum) { + if (k == 0) { + return curSum * minVal; + } + if (i == n || (n - i) < k) { + return INT_MIN; + } + if (minVal == 0) { + return 0; + } + + long long res = dfs(i + 1, k, minVal, curSum); + res = max(res, dfs(i + 1, k - 1, min(minVal, nums2[i]), curSum + nums1[i])); + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums1 + * @param {number[]} nums2 + * @param {number} k + * @return {number} + */ + maxScore(nums1, nums2, k) { + const n = nums1.length; + + const dfs = (i, k, minVal, curSum) => { + if (k === 0) { + return curSum * minVal; + } + if (i === n || (n - i) < k) { + return -Infinity; + } + if (minVal === 0) { + return 0; + } + + let res = dfs(i + 1, k, minVal, curSum); + res = Math.max( + res, + dfs(i + 1, k - 1, Math.min(minVal, nums2[i]), curSum + nums1[i]) + ); + return res; + }; + + return dfs(0, k, Infinity, 0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(2 ^ n)$ +* Space complexity: $O(n)$ for recursion stack. + +--- + +## 2. Min-Heap - I + +::tabs-start + +```python +class Solution: + def maxScore(self, nums1: List[int], nums2: List[int], k: int) -> int: + pairs = sorted(zip(nums1, nums2), key=lambda p: p[1], reverse=True) + + minHeap = [] + n1Sum = 0 + res = 0 + + for n1, n2 in pairs: + n1Sum += n1 + heapq.heappush(minHeap, n1) + + if len(minHeap) > k: + n1Sum -= heapq.heappop(minHeap) + + if len(minHeap) == k: + res = max(res, n1Sum * n2) + + return res +``` + +```java +public class Solution { + public long maxScore(int[] nums1, int[] nums2, int k) { + int n = nums1.length; + int[][] pairs = new int[n][2]; + + for (int i = 0; i < n; i++) { + pairs[i][0] = nums1[i]; + pairs[i][1] = nums2[i]; + } + + Arrays.sort(pairs, (a, b) -> Integer.compare(b[1], a[1])); + + PriorityQueue minHeap = new PriorityQueue<>(); + long n1Sum = 0, res = 0; + + for (int[] pair : pairs) { + n1Sum += pair[0]; + minHeap.offer(pair[0]); + + if (minHeap.size() > k) { + n1Sum -= minHeap.poll(); + } + + if (minHeap.size() == k) { + res = Math.max(res, n1Sum * pair[1]); + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + long long maxScore(vector& nums1, vector& nums2, int k) { + int n = nums1.size(); + vector> pairs(n); + + for (int i = 0; i < n; i++) { + pairs[i] = {nums1[i], nums2[i]}; + } + + sort(pairs.begin(), pairs.end(), [](const auto& a, const auto& b) { + return b.second < a.second; + }); + + priority_queue, greater> minHeap; + long long n1Sum = 0, res = 0; + + for (auto& pair : pairs) { + n1Sum += pair.first; + minHeap.push(pair.first); + + if (minHeap.size() > k) { + n1Sum -= minHeap.top(); + minHeap.pop(); + } + + if (minHeap.size() == k) { + res = max(res, n1Sum * (long long)pair.second); + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums1 + * @param {number[]} nums2 + * @param {number} k + * @return {number} + */ + maxScore(nums1, nums2, k) { + let pairs = nums1.map((n1, i) => [n1, nums2[i]]); + pairs.sort((a, b) => b[1] - a[1]); + + let minHeap = new MinPriorityQueue(); + let n1Sum = 0, res = 0; + + for (let [n1, n2] of pairs) { + n1Sum += n1; + minHeap.enqueue(n1); + + if (minHeap.size() > k) { + n1Sum -= minHeap.dequeue(); + } + + if (minHeap.size() === k) { + res = Math.max(res, n1Sum * n2); + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Min-Heap - II + +::tabs-start + +```python +class Solution: + def maxScore(self, nums1: List[int], nums2: List[int], k: int) -> int: + n = len(nums1) + arr = [(nums2[i] << 30) | nums1[i] for i in range(n)] + arr.sort(reverse=True) + + minHeap = [] + n1Sum = 0 + res = 0 + + for num in arr: + n1, n2 = num & ((1 << 30) - 1), num >> 30 + n1Sum += n1 + heapq.heappush(minHeap, n1) + + if len(minHeap) > k: + n1Sum -= heapq.heappop(minHeap) + + if len(minHeap) == k: + res = max(res, n1Sum * n2) + + return res +``` + +```java +public class Solution { + public long maxScore(int[] nums1, int[] nums2, int k) { + int n = nums1.length; + long[] arr = new long[n]; + for (int i = 0; i < n; i++) { + arr[i] = ((long) nums2[i] << 30) | nums1[i]; + } + + Arrays.sort(arr); + PriorityQueue minHeap = new PriorityQueue<>(); + long n1Sum = 0, res = 0; + + for (int i = n - 1; i >= 0; i--) { + int n1 = (int) (arr[i] & ((1L << 30) - 1)); + int n2 = (int) (arr[i] >> 30); + n1Sum += n1; + minHeap.offer(n1); + + if (minHeap.size() > k) { + n1Sum -= minHeap.poll(); + } + if (minHeap.size() == k) { + res = Math.max(res, n1Sum * (long) n2); + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + long long maxScore(vector& nums1, vector& nums2, int k) { + int n = nums1.size(); + vector arr(n); + for (int i = 0; i < n; i++) { + arr[i] = ((long long) nums2[i] << 30) | nums1[i]; + } + + sort(arr.rbegin(), arr.rend()); + priority_queue, greater> minHeap; + long long n1Sum = 0, res = 0; + + for (long long& num : arr) { + int n1 = num & ((1LL << 30) - 1); + int n2 = num >> 30; + n1Sum += n1; + minHeap.push(n1); + + if (minHeap.size() > k) { + n1Sum -= minHeap.top(); + minHeap.pop(); + } + if (minHeap.size() == k) { + res = max(res, n1Sum * (long long)n2); + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums1 + * @param {number[]} nums2 + * @param {number} k + * @return {number} + */ + maxScore(nums1, nums2, k) { + const n = nums1.length; + const arr = []; + for (let i = 0; i < n; i++) { + arr.push((BigInt(nums2[i]) << BigInt(30)) | BigInt(nums1[i])); + } + + arr.sort((a, b) => Number(b - a)); + const minHeap = new MinPriorityQueue(); + let n1Sum = 0n, res = 0n; + + for (let num of arr) { + let n1 = Number(num & ((1n << 30n) - 1n)); + let n2 = Number(num >> 30n); + n1Sum += BigInt(n1); + minHeap.enqueue(n1); + + if (minHeap.size() > k) { + n1Sum -= BigInt(minHeap.dequeue()); + } + if (minHeap.size() === k) { + res = BigInt(Math.max(Number(res), Number(n1Sum * BigInt(n2)))); + } + } + + return Number(res); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/minimize-deviation-in-array.md b/articles/minimize-deviation-in-array.md new file mode 100644 index 000000000..34f231a21 --- /dev/null +++ b/articles/minimize-deviation-in-array.md @@ -0,0 +1,468 @@ +## 1. Sorting + Sliding Window + +::tabs-start + +```python +class Solution: + def minimumDeviation(self, nums: List[int]) -> int: + n = len(nums) + arr = [] + for i, num in enumerate(nums): + if num & 1: + arr.append((num, i)) + arr.append((num * 2, i)) + else: + while num % 2 == 0: + arr.append((num, i)) + num //= 2 + arr.append((num, i)) + + arr.sort() + res = float("inf") + + seen = [0] * n + count = i = 0 + for j in range(len(arr)): + seen[arr[j][1]] += 1 + if seen[arr[j][1]] == 1: + count += 1 + while count == n: + res = min(res, arr[j][0] - arr[i][0]) + seen[arr[i][1]] -= 1 + if seen[arr[i][1]] == 0: + count -= 1 + i += 1 + + return res +``` + +```java +public class Solution { + public int minimumDeviation(int[] nums) { + int n = nums.length; + List arr = new ArrayList<>(); + + for (int i = 0; i < n; i++) { + int num = nums[i]; + if (num % 2 == 1) { + arr.add(new int[]{num, i}); + arr.add(new int[]{num * 2, i}); + } else { + while (num % 2 == 0) { + arr.add(new int[]{num, i}); + num /= 2; + } + arr.add(new int[]{num, i}); + } + } + + arr.sort(Comparator.comparingInt(a -> a[0])); + int res = Integer.MAX_VALUE; + + int[] seen = new int[n]; + int count = 0, i = 0; + + for (int j = 0; j < arr.size(); j++) { + seen[arr.get(j)[1]]++; + if (seen[arr.get(j)[1]] == 1) { + count++; + while (count == n) { + res = Math.min(res, arr.get(j)[0] - arr.get(i)[0]); + seen[arr.get(i)[1]]--; + if (seen[arr.get(i)[1]] == 0) { + count--; + } + i++; + } + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int minimumDeviation(vector& nums) { + int n = nums.size(); + vector> arr; + + for (int i = 0; i < n; i++) { + int num = nums[i]; + if (num % 2 == 1) { + arr.emplace_back(num, i); + arr.emplace_back(num * 2, i); + } else { + while (num % 2 == 0) { + arr.emplace_back(num, i); + num /= 2; + } + arr.emplace_back(num, i); + } + } + + sort(arr.begin(), arr.end()); + int res = INT_MAX; + + vector seen(n, 0); + int count = 0, i = 0; + + for (int j = 0; j < arr.size(); j++) { + seen[arr[j].second]++; + if (seen[arr[j].second] == 1) { + count++; + while (count == n) { + res = min(res, arr[j].first - arr[i].first); + seen[arr[i].second]--; + if (seen[arr[i].second] == 0) { + count--; + } + i++; + } + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + minimumDeviation(nums) { + let n = nums.length; + let arr = []; + + for (let i = 0; i < n; i++) { + let num = nums[i]; + if (num % 2 === 1) { + arr.push([num, i]); + arr.push([num * 2, i]); + } else { + while (num % 2 === 0) { + arr.push([num, i]); + num /= 2; + } + arr.push([num, i]); + } + } + + arr.sort((a, b) => a[0] - b[0]); + let res = Infinity; + + let seen = new Array(n).fill(0); + let count = 0, i = 0; + + for (let j = 0; j < arr.length; j++) { + seen[arr[j][1]]++; + if (seen[arr[j][1]] === 1) { + count++; + while (count === n) { + res = Math.min(res, arr[j][0] - arr[i][0]); + seen[arr[i][1]]--; + if (seen[arr[i][1]] === 0) { + count--; + } + i++; + } + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O((n \log m) * \log (n \log m))$ +* Space complexity: $O(n \log m)$ + +> Where $n$ is the size of the array $nums$ and $m$ is the maximum element in $nums$. + +--- + +## 2. Min-Heap + +::tabs-start + +```python +class Solution: + def minimumDeviation(self, nums: List[int]) -> int: + minHeap, heapMax = [], 0 + + for n in nums: + tmp = n + while n % 2 == 0: + n //= 2 + minHeap.append((n, max(tmp, 2 * n))) + heapMax = max(heapMax, n) + + res = float("inf") + heapq.heapify(minHeap) + + while len(minHeap) == len(nums): + n, nMax = heapq.heappop(minHeap) + res = min(res, heapMax - n) + + if n < nMax: + heapq.heappush(minHeap, (n * 2, nMax)) + heapMax = max(heapMax, n * 2) + + return res +``` + +```java +public class Solution { + public int minimumDeviation(int[] nums) { + PriorityQueue minHeap = new PriorityQueue<>((a, b) -> a[0] - b[0]); + int heapMax = 0; + + for (int num : nums) { + int tmp = num; + while (num % 2 == 0) { + num /= 2; + } + minHeap.offer(new int[]{num, Math.max(tmp, 2 * num)}); + heapMax = Math.max(heapMax, num); + } + + int res = Integer.MAX_VALUE; + + while (minHeap.size() == nums.length) { + int[] minElement = minHeap.poll(); + int n = minElement[0], nMax = minElement[1]; + res = Math.min(res, heapMax - n); + + if (n < nMax) { + minHeap.offer(new int[]{n * 2, nMax}); + heapMax = Math.max(heapMax, n * 2); + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int minimumDeviation(vector& nums) { + priority_queue, vector>, greater<>> minHeap; + int heapMax = 0; + + for (int num : nums) { + int tmp = num; + while (num % 2 == 0) { + num /= 2; + } + minHeap.push({num, max(tmp, 2 * num)}); + heapMax = max(heapMax, num); + } + + int res = INT_MAX; + + while (minHeap.size() == nums.size()) { + auto [n, nMax] = minHeap.top(); + minHeap.pop(); + res = min(res, heapMax - n); + + if (n < nMax) { + minHeap.push({n * 2, nMax}); + heapMax = max(heapMax, n * 2); + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + minimumDeviation(nums) { + const minHeap = new MinPriorityQueue(x => x[0]); + let heapMax = 0; + + for (let num of nums) { + let tmp = num; + while (num % 2 === 0) { + num /= 2; + } + minHeap.enqueue([num, Math.max(tmp, num * 2)]); + heapMax = Math.max(heapMax, num); + } + + let res = Infinity; + + while (minHeap.size() === nums.length) { + let [n, nMax] = minHeap.dequeue(); + res = Math.min(res, heapMax - n); + + if (n < nMax) { + minHeap.enqueue([n * 2, nMax]); + heapMax = Math.max(heapMax, n * 2); + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n *\log n * \log m)$ +* Space complexity: $O(n)$ + +> Where $n$ is the size of the array $nums$ and $m$ is the maximum element in $nums$. + +--- + +## 3. Max-Heap + +::tabs-start + +```python +class Solution: + def minimumDeviation(self, nums: List[int]) -> int: + maxHeap = [] + minVal = float("inf") + + for num in nums: + if num % 2 == 1: + num *= 2 + heapq.heappush(maxHeap, -num) + minVal = min(minVal, num) + + res = float("inf") + + while maxHeap: + maxVal = -heapq.heappop(maxHeap) + res = min(res, maxVal - minVal) + if maxVal % 2 == 1: + break + + nextVal = maxVal // 2 + heapq.heappush(maxHeap, -nextVal) + minVal = min(minVal, nextVal) + + return res +``` + +```java +public class Solution { + public int minimumDeviation(int[] nums) { + PriorityQueue maxHeap = new PriorityQueue<>((a, b) -> b - a); + int minVal = Integer.MAX_VALUE; + + for (int num : nums) { + if (num % 2 == 1) num *= 2; + maxHeap.offer(num); + minVal = Math.min(minVal, num); + } + + int res = Integer.MAX_VALUE; + + while (!maxHeap.isEmpty()) { + int maxVal = maxHeap.poll(); + res = Math.min(res, maxVal - minVal); + + if (maxVal % 2 == 1) break; + + int nextVal = maxVal / 2; + maxHeap.offer(nextVal); + minVal = Math.min(minVal, nextVal); + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int minimumDeviation(vector& nums) { + priority_queue maxHeap; + int minVal = INT_MAX; + + for (int num : nums) { + if (num % 2 == 1) num *= 2; + maxHeap.push(num); + minVal = min(minVal, num); + } + + int res = INT_MAX; + + while (!maxHeap.empty()) { + int maxVal = maxHeap.top(); + maxHeap.pop(); + res = min(res, maxVal - minVal); + + if (maxVal % 2 == 1) break; + + int nextVal = maxVal / 2; + maxHeap.push(nextVal); + minVal = min(minVal, nextVal); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + minimumDeviation(nums) { + const maxHeap = new MaxPriorityQueue(); + let minVal = Infinity; + + for (let num of nums) { + if (num % 2 === 1) num *= 2; + maxHeap.enqueue(num); + minVal = Math.min(minVal, num); + } + + let res = Infinity; + + while (!maxHeap.isEmpty()) { + let maxVal = maxHeap.dequeue(); + res = Math.min(res, maxVal - minVal); + + if (maxVal % 2 === 1) break; + + let nextVal = maxVal / 2; + maxHeap.enqueue(nextVal); + minVal = Math.min(minVal, nextVal); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n *\log n * \log m)$ +* Space complexity: $O(n)$ + +> Where $n$ is the size of the array $nums$ and $m$ is the maximum element in $nums$. \ No newline at end of file diff --git a/articles/minimum-cost-to-hire-k-workers.md b/articles/minimum-cost-to-hire-k-workers.md new file mode 100644 index 000000000..f205b01e7 --- /dev/null +++ b/articles/minimum-cost-to-hire-k-workers.md @@ -0,0 +1,144 @@ +## 1. Greedy + Max-Heap + +::tabs-start + +```python +class Solution: + def mincostToHireWorkers(self, quality: List[int], wage: List[int], k: int) -> float: + pairs = sorted([(w / q, q) for q, w in zip(quality, wage)], key=lambda p: p[0]) + + maxHeap = [] + total_quality = 0 + res = float("inf") + + for rate, q in pairs: + heapq.heappush(maxHeap, -q) + total_quality += q + + if len(maxHeap) > k: + total_quality += heapq.heappop(maxHeap) + + if len(maxHeap) == k: + res = min(res, total_quality * rate) + + return res +``` + +```java +public class Solution { + public double mincostToHireWorkers(int[] quality, int[] wage, int k) { + int n = quality.length; + double res = Double.MAX_VALUE; + double totalQuality = 0; + + double[][] workers = new double[n][2]; + for (int i = 0; i < n; i++) { + workers[i] = new double[]{ + (double) wage[i] / quality[i], (double) quality[i] + }; + } + + Arrays.sort(workers, Comparator.comparingDouble(a -> a[0])); + PriorityQueue maxHeap = new PriorityQueue<>(Collections.reverseOrder()); + + for (double[] worker : workers) { + double ratio = worker[0]; + int q = (int) worker[1]; + + maxHeap.add(q); + totalQuality += q; + + if (maxHeap.size() > k) { + totalQuality -= maxHeap.poll(); + } + + if (maxHeap.size() == k) { + res = Math.min(res, totalQuality * ratio); + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + double mincostToHireWorkers(vector& quality, vector& wage, int k) { + int n = quality.size(); + vector> workers(n); + for (int i = 0; i < n; i++) { + workers[i] = { (double)wage[i] / quality[i], quality[i] }; + } + + sort(workers.begin(), workers.end()); + priority_queue maxHeap; + double totalQuality = 0, res = DBL_MAX; + + for (auto& worker : workers) { + double ratio = worker.first; + int q = worker.second; + maxHeap.push(q); + totalQuality += q; + + if (maxHeap.size() > k) { + totalQuality -= maxHeap.top(); + maxHeap.pop(); + } + + if (maxHeap.size() == k) { + res = min(res, totalQuality * ratio); + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} quality + * @param {number[]} wage + * @param {number} k + * @return {number} + */ + mincostToHireWorkers(quality, wage, k) { + const n = quality.length; + const workers = []; + for (let i = 0; i < n; i++) { + workers.push([wage[i] / quality[i], quality[i]]); + } + + workers.sort((a, b) => a[0] - b[0]); + const maxHeap = new MaxPriorityQueue(); + let totalQuality = 0, res = Number.MAX_VALUE; + + for (let [ratio, q] of workers) { + maxHeap.enqueue(q); + totalQuality += q; + + if (maxHeap.size() > k) { + totalQuality -= maxHeap.dequeue(); + } + + if (maxHeap.size() === k) { + res = Math.min(res, totalQuality * ratio); + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * (\log n + \log k))$ +* Space complexity: $O(n)$ + +> Where $n$ is the number of workers, and $k$ is the number of workers to be hired. \ No newline at end of file diff --git a/articles/number-of-flowers-in-full-bloom.md b/articles/number-of-flowers-in-full-bloom.md new file mode 100644 index 000000000..2b60ec927 --- /dev/null +++ b/articles/number-of-flowers-in-full-bloom.md @@ -0,0 +1,675 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def fullBloomFlowers(self, flowers: List[List[int]], people: List[int]) -> List[int]: + res = [] + + for time in people: + cnt = 0 + for start, end in flowers: + if start <= time <= end: + cnt += 1 + res.append(cnt) + + return res +``` + +```java +public class Solution { + public int[] fullBloomFlowers(int[][] flowers, int[] people) { + int m = people.length; + int[] res = new int[m]; + + for (int i = 0; i < m; i++) { + int count = 0; + for (int[] flower : flowers) { + if (flower[0] <= people[i] && people[i] <= flower[1]) { + count++; + } + } + res[i] = count; + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector fullBloomFlowers(vector>& flowers, vector& people) { + int m = people.size(); + vector res(m); + + for (int i = 0; i < m; i++) { + int count = 0; + for (auto& flower : flowers) { + if (flower[0] <= people[i] && people[i] <= flower[1]) { + count++; + } + } + res[i] = count; + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} flowers + * @param {number[]} people + * @return {number[]} + */ + fullBloomFlowers(flowers, people) { + let res = new Array(people.length).fill(0); + + for (let i = 0; i < people.length; i++) { + let count = 0; + for (let [start, end] of flowers) { + if (start <= people[i] && people[i] <= end) { + count++; + } + } + res[i] = count; + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n)$ +* Space complexity: $O(m)$ for the output array. + +> Where $n$ is the size of the array $flowers$, and $m$ is the size of the array $people$. + +--- + +## 2. Two Min-Heaps + +::tabs-start + +```python +class Solution: + def fullBloomFlowers(self, flowers: List[List[int]], people: List[int]) -> List[int]: + people = sorted((p, i) for i, p in enumerate(people)) + res = [0] * len(people) + count = 0 + + start = [f[0] for f in flowers] + end = [f[1] for f in flowers] + + heapq.heapify(start) + heapq.heapify(end) + + for p, i in people: + while start and start[0] <= p: + heapq.heappop(start) + count += 1 + while end and end[0] < p: + heapq.heappop(end) + count -= 1 + res[i] = count + + return res +``` + +```java +public class Solution { + public int[] fullBloomFlowers(int[][] flowers, int[] people) { + int m = people.length; + int[] res = new int[m]; + + List sortedPeople = new ArrayList<>(); + for (int i = 0; i < m; i++) { + sortedPeople.add(new int[]{people[i], i}); + } + sortedPeople.sort(Comparator.comparingInt(a -> a[0])); + + PriorityQueue startHeap = new PriorityQueue<>(); + PriorityQueue endHeap = new PriorityQueue<>(); + for (int[] f : flowers) { + startHeap.offer(f[0]); + endHeap.offer(f[1]); + } + + int count = 0; + for (int[] person : sortedPeople) { + int p = person[0], index = person[1]; + + while (!startHeap.isEmpty() && startHeap.peek() <= p) { + startHeap.poll(); + count++; + } + while (!endHeap.isEmpty() && endHeap.peek() < p) { + endHeap.poll(); + count--; + } + + res[index] = count; + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector fullBloomFlowers(vector>& flowers, vector& people) { + int m = people.size(); + vector res(m); + + vector> sortedPeople; + for (int i = 0; i < m; i++) { + sortedPeople.push_back({people[i], i}); + } + sort(sortedPeople.begin(), sortedPeople.end()); + + priority_queue, greater> startHeap, endHeap; + for (const auto& f : flowers) { + startHeap.push(f[0]); + endHeap.push(f[1]); + } + + int count = 0; + for (const auto& person : sortedPeople) { + int p = person.first, index = person.second; + + while (!startHeap.empty() && startHeap.top() <= p) { + startHeap.pop(); + count++; + } + while (!endHeap.empty() && endHeap.top() < p) { + endHeap.pop(); + count--; + } + + res[index] = count; + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} flowers + * @param {number[]} people + * @return {number[]} + */ + fullBloomFlowers(flowers, people) { + const m = people.length; + const res = new Array(m).fill(0); + + const sortedPeople = people.map((p, i) => [p, i]); + sortedPeople.sort((a, b) => a[0] - b[0]); + + const startHeap = new MinPriorityQueue(); + const endHeap = new MinPriorityQueue(); + for (const [s, e] of flowers) { + startHeap.enqueue(s); + endHeap.enqueue(e); + } + + let count = 0; + for (const [p, index] of sortedPeople) { + while (!startHeap.isEmpty() && startHeap.front() <= p) { + startHeap.dequeue(); + count++; + } + while (!endHeap.isEmpty() && endHeap.front() < p) { + endHeap.dequeue(); + count--; + } + res[index] = count; + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m \log m + n \log n)$ +* Space complexity: $O(m + n)$ + +> Where $n$ is the size of the array $flowers$, and $m$ is the size of the array $people$. + +--- + +## 3. Min-Heap + +::tabs-start + +```python +class Solution: + def fullBloomFlowers(self, flowers: List[List[int]], people: List[int]) -> List[int]: + people = sorted((p, i) for i, p in enumerate(people)) + res = [0] * len(people) + flowers.sort() + end = [] + + j = 0 + for p, i in people: + while j < len(flowers) and flowers[j][0] <= p: + heapq.heappush(end, flowers[j][1]) + j += 1 + while end and end[0] < p: + heapq.heappop(end) + res[i] = len(end) + + return res +``` + +```java +public class Solution { + public int[] fullBloomFlowers(int[][] flowers, int[] people) { + int m = people.length; + int[] res = new int[m]; + int[][] indexedPeople = new int[m][2]; + + for (int i = 0; i < m; i++) { + indexedPeople[i] = new int[]{people[i], i}; + } + Arrays.sort(indexedPeople, Comparator.comparingInt(a -> a[0])); + Arrays.sort(flowers, Comparator.comparingInt(a -> a[0])); + + PriorityQueue endHeap = new PriorityQueue<>(); + int j = 0, n = flowers.length; + + for (int[] person : indexedPeople) { + int p = person[0], index = person[1]; + + while (j < n && flowers[j][0] <= p) { + endHeap.offer(flowers[j][1]); + j++; + } + while (!endHeap.isEmpty() && endHeap.peek() < p) { + endHeap.poll(); + } + res[index] = endHeap.size(); + } + return res; + } +} +``` + +```cpp +class Solution { +public: + vector fullBloomFlowers(vector>& flowers, vector& people) { + int m = people.size(); + vector res(m); + vector> indexedPeople; + + for (int i = 0; i < m; i++) { + indexedPeople.emplace_back(people[i], i); + } + sort(indexedPeople.begin(), indexedPeople.end()); + sort(flowers.begin(), flowers.end()); + + priority_queue, greater> endHeap; + int j = 0, n = flowers.size(); + + for (auto [p, index] : indexedPeople) { + while (j < n && flowers[j][0] <= p) { + endHeap.push(flowers[j][1]); + j++; + } + while (!endHeap.empty() && endHeap.top() < p) { + endHeap.pop(); + } + res[index] = endHeap.size(); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} flowers + * @param {number[]} people + * @return {number[]} + */ + fullBloomFlowers(flowers, people) { + const m = people.length; + const res = new Array(m).fill(0); + const indexedPeople = people.map((p, i) => [p, i]); + + indexedPeople.sort((a, b) => a[0] - b[0]); + flowers.sort((a, b) => a[0] - b[0]); + + const endHeap = new MinPriorityQueue(); + let j = 0, n = flowers.length; + + for (const [p, index] of indexedPeople) { + while (j < n && flowers[j][0] <= p) { + endHeap.enqueue(flowers[j][1]); + j++; + } + while (!endHeap.isEmpty() && endHeap.front() < p) { + endHeap.dequeue(); + } + res[index] = endHeap.size(); + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m \log m + n \log n)$ +* Space complexity: $O(m + n)$ + +> Where $n$ is the size of the array $flowers$, and $m$ is the size of the array $people$. + +--- + +## 4. Sorting + Two Pointers + +::tabs-start + +```python +class Solution: + def fullBloomFlowers(self, flowers: List[List[int]], people: List[int]) -> List[int]: + start = sorted(f[0] for f in flowers) + end = sorted(f[1] for f in flowers) + + res = [0] * len(people) + peopleIndex = sorted((p, i) for i, p in enumerate(people)) + + i = j = count = 0 + for p, index in peopleIndex: + while i < len(start) and start[i] <= p: + count += 1 + i += 1 + while j < len(end) and end[j] < p: + count -= 1 + j += 1 + res[index] = count + + return res +``` + +```java +public class Solution { + public int[] fullBloomFlowers(int[][] flowers, int[] people) { + int m = people.length; + int[] res = new int[m]; + List start = new ArrayList<>(), end = new ArrayList<>(); + for (int[] f : flowers) { + start.add(f[0]); + end.add(f[1]); + } + + Collections.sort(start); + Collections.sort(end); + + int count = 0, i = 0, j = 0; + List peopleIndex = new ArrayList<>(); + for (int k = 0; k < m; k++) { + peopleIndex.add(new int[]{people[k], k}); + } + peopleIndex.sort(Comparator.comparingInt(a -> a[0])); + + for (int[] p : peopleIndex) { + int time = p[0], index = p[1]; + + while (i < start.size() && start.get(i) <= time) { + count++; + i++; + } + while (j < end.size() && end.get(j) < time) { + count--; + j++; + } + res[index] = count; + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector fullBloomFlowers(vector>& flowers, vector& people) { + int m = people.size(); + vector res(m), start, end; + for (auto& f : flowers) { + start.push_back(f[0]); + end.push_back(f[1]); + } + + sort(start.begin(), start.end()); + sort(end.begin(), end.end()); + + int count = 0, i = 0, j = 0; + vector> peopleIndex; + for (int k = 0; k < m; k++) { + peopleIndex.emplace_back(people[k], k); + } + sort(peopleIndex.begin(), peopleIndex.end()); + + for (auto& [p, index] : peopleIndex) { + while (i < start.size() && start[i] <= p) { + count++; + i++; + } + while (j < end.size() && end[j] < p) { + count--; + j++; + } + res[index] = count; + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} flowers + * @param {number[]} people + * @return {number[]} + */ + fullBloomFlowers(flowers, people) { + const start = [], end = []; + for (let f of flowers) { + start.push(f[0]); + end.push(f[1]); + } + + start.sort((a, b) => a - b); + end.sort((a, b) => a - b); + + let count = 0, i = 0, j = 0; + const peopleIndex = people.map((p, idx) => [p, idx]); + peopleIndex.sort((a, b) => a[0] - b[0]); + + const res = new Array(people.length); + + for (let [p, index] of peopleIndex) { + while (i < start.length && start[i] <= p) { + count++; + i++; + } + while (j < end.length && end[j] < p) { + count--; + j++; + } + res[index] = count; + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m \log m + n \log n)$ +* Space complexity: $O(m + n)$ + +> Where $n$ is the size of the array $flowers$, and $m$ is the size of the array $people$. + +--- + +## 5. Line Sweep + +::tabs-start + +```python +class Solution: + def fullBloomFlowers(self, flowers: List[List[int]], people: List[int]) -> List[int]: + events = [] + for start, end in flowers: + events.append((start, 1)) + events.append((end + 1, -1)) + + events.sort() + queries = sorted((p, i) for i, p in enumerate(people)) + res = [0] * len(people) + + count = j = 0 + for time, index in queries: + while j < len(events) and events[j][0] <= time: + count += events[j][1] + j += 1 + res[index] = count + + return res +``` + +```java +public class Solution { + public int[] fullBloomFlowers(int[][] flowers, int[] people) { + List events = new ArrayList<>(); + for (int[] f : flowers) { + events.add(new int[]{f[0], 1}); + events.add(new int[]{f[1] + 1, -1}); + } + + Collections.sort(events, (a, b) -> a[0] - b[0]); + int[][] queries = new int[people.length][2]; + for (int i = 0; i < people.length; i++) { + queries[i] = new int[]{people[i], i}; + } + Arrays.sort(queries, (a, b) -> Integer.compare(a[0], b[0])); + + int[] res = new int[people.length]; + int count = 0, j = 0; + for (int[] query : queries) { + int time = query[0], index = query[1]; + while (j < events.size() && events.get(j)[0] <= time) { + count += events.get(j)[1]; + j++; + } + res[index] = count; + } + return res; + } +} +``` + +```cpp +class Solution { +public: + vector fullBloomFlowers(vector>& flowers, vector& people) { + vector> events; + for (auto& f : flowers) { + events.emplace_back(f[0], 1); + events.emplace_back(f[1] + 1, -1); + } + + sort(events.begin(), events.end()); + vector> queries; + for (int i = 0; i < people.size(); i++) { + queries.emplace_back(people[i], i); + } + + sort(queries.begin(), queries.end()); + vector res(people.size()); + int count = 0, j = 0; + + for (auto& [time, index] : queries) { + while (j < events.size() && events[j].first <= time) { + count += events[j++].second; + } + res[index] = count; + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} flowers + * @param {number[]} people + * @return {number[]} + */ + fullBloomFlowers(flowers, people) { + let events = []; + for (let [start, end] of flowers) { + events.push([start, 1]); + events.push([end + 1, -1]); + } + + events.sort((a, b) => a[0] - b[0]); + let queries = people.map((p, i) => [p, i]).sort((a, b) => a[0] - b[0]); + let res = new Array(people.length).fill(0); + + let count = 0, j = 0; + for (let [time, index] of queries) { + while (j < events.length && events[j][0] <= time) { + count += events[j][1]; + j++; + } + res[index] = count; + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m \log m + n \log n)$ +* Space complexity: $O(m + n)$ + +> Where $n$ is the size of the array $flowers$, and $m$ is the size of the array $people$. \ No newline at end of file diff --git a/articles/paint-house.md b/articles/paint-house.md new file mode 100644 index 000000000..91b9896cd --- /dev/null +++ b/articles/paint-house.md @@ -0,0 +1,445 @@ +## 1. Recursion + +::tabs-start + +```python +class Solution: + def min_cost(self, costs: List[List[int]]) -> int: + n = len(costs) + + def dfs(i, prevColor): + if i == n: + return 0 + + res = float("inf") + for c in range(3): + if c == prevColor: + continue + res = min(res, costs[i][c] + dfs(i + 1, c)) + return res + + return dfs(0, -1) +``` + +```java +public class Solution { + private int[][] costs; + private int n; + + public int minCost(int[][] costs) { + this.costs = costs; + this.n = costs.length; + return dfs(0, -1); + } + + private int dfs(int i, int prevColor) { + if (i == n) { + return 0; + } + + int res = Integer.MAX_VALUE; + for (int c = 0; c < 3; c++) { + if (c == prevColor) { + continue; + } + res = Math.min(res, costs[i][c] + dfs(i + 1, c)); + } + return res; + } +} +``` + +```cpp +class Solution { +private: + vector> costs; + int n; + + int dfs(int i, int prevColor) { + if (i == n) { + return 0; + } + + int res = INT_MAX; + for (int c = 0; c < 3; c++) { + if (c == prevColor) { + continue; + } + res = min(res, costs[i][c] + dfs(i + 1, c)); + } + return res; + } + +public: + int minCost(vector>& costs) { + this->costs = costs; + this->n = costs.size(); + return dfs(0, -1); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} costs + * @return {number} + */ + minCost(costs) { + const n = costs.length; + + const dfs = (i, prevColor) => { + if (i === n) return 0; + + let res = Infinity; + for (let c = 0; c < 3; c++) { + if (c === prevColor) continue; + res = Math.min(res, costs[i][c] + dfs(i + 1, c)); + } + return res; + }; + + return dfs(0, -1); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(2 ^ n)$ +* Space complexity: $O(n)$ for recursion stack. + +--- + +## 2. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def min_cost(self, costs: List[List[int]]) -> int: + n = len(costs) + dp = [[-1] * 4 for _ in range(n)] + + def dfs(i, prevColor): + if i == n: + return 0 + if dp[i][prevColor + 1] != -1: + return dp[i][prevColor + 1] + + res = float("inf") + for c in range(3): + if c == prevColor: + continue + res = min(res, costs[i][c] + dfs(i + 1, c)) + + dp[i][prevColor + 1] = res + return res + + return dfs(0, -1) +``` + +```java +public class Solution { + private int[][] dp; + private int[][] costs; + + public int minCost(int[][] costs) { + int n = costs.length; + this.costs = costs; + this.dp = new int[n][4]; + + for (int i = 0; i < n; i++) { + Arrays.fill(dp[i], -1); + } + + return dfs(0, -1); + } + + private int dfs(int i, int prevColor) { + if (i == costs.length) { + return 0; + } + if (dp[i][prevColor + 1] != -1) { + return dp[i][prevColor + 1]; + } + + int res = Integer.MAX_VALUE; + for (int c = 0; c < 3; c++) { + if (c == prevColor) continue; + res = Math.min(res, costs[i][c] + dfs(i + 1, c)); + } + + return dp[i][prevColor + 1] = res; + } +} +``` + +```cpp +class Solution { +public: + vector> dp; + vector> costs; + + int minCost(vector>& costs) { + int n = costs.size(); + this->costs = costs; + dp.assign(n, vector(4, -1)); + return dfs(0, -1); + } + +private: + int dfs(int i, int prevColor) { + if (i == costs.size()) { + return 0; + } + if (dp[i][prevColor + 1] != -1) { + return dp[i][prevColor + 1]; + } + + int res = INT_MAX; + for (int c = 0; c < 3; c++) { + if (c == prevColor) continue; + res = min(res, costs[i][c] + dfs(i + 1, c)); + } + + return dp[i][prevColor + 1] = res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} costs + * @return {number} + */ + minCost(costs) { + const n = costs.length; + const dp = Array.from({ length: n }, () => Array(4).fill(-1)); + + const dfs = (i, prevColor) => { + if (i === n) { + return 0; + } + if (dp[i][prevColor + 1] !== -1) { + return dp[i][prevColor + 1]; + } + + let res = Infinity; + for (let c = 0; c < 3; c++) { + if (c === prevColor) continue; + res = Math.min(res, costs[i][c] + dfs(i + 1, c)); + } + + return (dp[i][prevColor + 1] = res); + }; + + return dfs(0, -1); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def min_cost(self, costs: List[List[int]]) -> int: + n = len(costs) + if n == 0: + return 0 + + dp = [[0] * 3 for _ in range(n)] + for c in range(3): + dp[0][c] = costs[0][c] + + for i in range(1, n): + for c in range(3): + dp[i][c] = ( + costs[i][c] + + min(dp[i - 1][(c + 1) % 3], dp[i - 1][(c + 2) % 3]) + ) + + return min(dp[n - 1]) +``` + +```java +public class Solution { + public int minCost(int[][] costs) { + int n = costs.length; + if (n == 0) return 0; + + int[][] dp = new int[n][3]; + for (int c = 0; c < 3; c++) { + dp[0][c] = costs[0][c]; + } + + for (int i = 1; i < n; i++) { + for (int c = 0; c < 3; c++) { + dp[i][c] = costs[i][c] + + Math.min(dp[i - 1][(c + 1) % 3], dp[i - 1][(c + 2) % 3]); + } + } + + return Math.min(dp[n - 1][0], Math.min(dp[n - 1][1], dp[n - 1][2])); + } +} +``` + +```cpp +class Solution { +public: + int minCost(vector>& costs) { + int n = costs.size(); + if (n == 0) return 0; + + vector> dp(n, vector(3, 0)); + for (int c = 0; c < 3; c++) { + dp[0][c] = costs[0][c]; + } + + for (int i = 1; i < n; i++) { + for (int c = 0; c < 3; c++) { + dp[i][c] = costs[i][c] + + min(dp[i - 1][(c + 1) % 3], dp[i - 1][(c + 2) % 3]); + } + } + + return min({dp[n - 1][0], dp[n - 1][1], dp[n - 1][2]}); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} costs + * @return {number} + */ + minCost(costs) { + let n = costs.length; + if (n === 0) return 0; + + let dp = Array.from({ length: n }, () => Array(3).fill(0)); + for (let c = 0; c < 3; c++) { + dp[0][c] = costs[0][c]; + } + + for (let i = 1; i < n; i++) { + for (let c = 0; c < 3; c++) { + dp[i][c] = costs[i][c] + + Math.min(dp[i - 1][(c + 1) % 3], dp[i - 1][(c + 2) % 3]); + } + } + + return Math.min(dp[n - 1][0], dp[n - 1][1], dp[n - 1][2]); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 4. Dynamic Programming (Space Optimized) + +::tabs-start + +```python +class Solution: + def minCost(self, costs: List[List[int]]) -> int: + dp = [0, 0, 0] + + for i in range(len(costs)): + dp0 = costs[i][0] + min(dp[1], dp[2]) + dp1 = costs[i][1] + min(dp[0], dp[2]) + dp2 = costs[i][2] + min(dp[0], dp[1]) + dp = [dp0, dp1, dp2] + + return min(dp) +``` + +```java +public class Solution { + public int minCost(int[][] costs) { + int dp0 = 0, dp1 = 0, dp2 = 0; + + for (int i = 0; i < costs.length; i++) { + int newDp0 = costs[i][0] + Math.min(dp1, dp2); + int newDp1 = costs[i][1] + Math.min(dp0, dp2); + int newDp2 = costs[i][2] + Math.min(dp0, dp1); + dp0 = newDp0; + dp1 = newDp1; + dp2 = newDp2; + } + + return Math.min(dp0, Math.min(dp1, dp2)); + } +} +``` + +```cpp +class Solution { +public: + int minCost(vector>& costs) { + int dp0 = 0, dp1 = 0, dp2 = 0; + + for (const auto& cost : costs) { + int newDp0 = cost[0] + min(dp1, dp2); + int newDp1 = cost[1] + min(dp0, dp2); + int newDp2 = cost[2] + min(dp0, dp1); + dp0 = newDp0; + dp1 = newDp1; + dp2 = newDp2; + } + + return min({dp0, dp1, dp2}); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} costs + * @return {number} + */ + minCost(costs) { + let dp = [0, 0, 0]; + + for (let i = 0; i < costs.length; i++) { + let dp0 = costs[i][0] + Math.min(dp[1], dp[2]); + let dp1 = costs[i][1] + Math.min(dp[0], dp[2]); + let dp2 = costs[i][2] + Math.min(dp[0], dp[1]); + dp = [dp0, dp1, dp2]; + } + + return Math.min(dp[0], dp[1], dp[2]); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. \ No newline at end of file diff --git a/articles/process-tasks-using-servers.md b/articles/process-tasks-using-servers.md new file mode 100644 index 000000000..f79a9c753 --- /dev/null +++ b/articles/process-tasks-using-servers.md @@ -0,0 +1,501 @@ +## 1. Brute Force (Simulation) + +::tabs-start + +```python +class Solution: + def assignTasks(self, servers: List[int], tasks: List[int]) -> List[int]: + n, m = len(servers), len(tasks) + available = [True] * n + finishTime = [0] * n + res = [] + time = 0 + + for t in range(m): + time = max(time, t) + + for i in range(n): + if finishTime[i] <= time: + available[i] = True + + if not any(available): + time = min(finishTime) + for i in range(n): + if finishTime[i] <= time: + available[i] = True + + minIdx = -1 + for i in range(n): + if (available[i] and + (minIdx == -1 or servers[i] < servers[minIdx] or + (servers[i] == servers[minIdx] and i < minIdx)) + ): + minIdx = i + + res.append(minIdx) + available[minIdx] = False + finishTime[minIdx] = time + tasks[t] + + return res +``` + +```java +public class Solution { + public int[] assignTasks(int[] servers, int[] tasks) { + int n = servers.length, m = tasks.length; + boolean[] available = new boolean[n]; + Arrays.fill(available, true); + int[] finishTime = new int[n]; + int[] res = new int[m]; + int time = 0; + + for (int t = 0; t < m; t++) { + time = Math.max(time, t); + for (int i = 0; i < n; i++) { + if (finishTime[i] <= time) { + available[i] = true; + } + } + + if (!anyAvailable(available)) { + time = Arrays.stream(finishTime).min().getAsInt(); + for (int i = 0; i < n; i++) { + if (finishTime[i] <= time) { + available[i] = true; + } + } + } + + int minIdx = -1; + for (int i = 0; i < n; i++) { + if (available[i] && (minIdx == -1 || servers[i] < servers[minIdx] || + (servers[i] == servers[minIdx] && i < minIdx))) { + minIdx = i; + } + } + + res[t] = minIdx; + available[minIdx] = false; + finishTime[minIdx] = time + tasks[t]; + } + return res; + } + + private boolean anyAvailable(boolean[] available) { + for (boolean v : available) { + if (v) return true; + } + return false; + } +} +``` + +```cpp +class Solution { +public: + vector assignTasks(vector& servers, vector& tasks) { + int n = servers.size(), m = tasks.size(); + vector available(n, true); + vector finishTime(n, 0), res(m); + int time = 0; + + for (int t = 0; t < m; t++) { + time = max(time, t); + + for (int i = 0; i < n; i++) { + if (finishTime[i] <= time) { + available[i] = true; + } + } + + if (!any_of(available.begin(), available.end(), [](bool v) { return v; })) { + time = *min_element(finishTime.begin(), finishTime.end()); + for (int i = 0; i < n; i++) { + if (finishTime[i] <= time) { + available[i] = true; + } + } + } + + int minIdx = -1; + for (int i = 0; i < n; i++) { + if (available[i] && (minIdx == -1 || servers[i] < servers[minIdx] || + (servers[i] == servers[minIdx] && i < minIdx))) { + minIdx = i; + } + } + + res[t] = minIdx; + available[minIdx] = false; + finishTime[minIdx] = time + tasks[t]; + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} servers + * @param {number[]} tasks + * @return {number[]} + */ + assignTasks(servers, tasks) { + const n = servers.length, m = tasks.length; + const available = Array(n).fill(true); + const finishTime = Array(n).fill(0); + const res = []; + let time = 0; + + for (let t = 0; t < m; t++) { + time = Math.max(time, t); + + for (let i = 0; i < n; i++) { + if (finishTime[i] <= time) { + available[i] = true; + } + } + + if (!available.some(v => v)) { + time = Math.min(...finishTime); + for (let i = 0; i < n; i++) { + if (finishTime[i] <= time) { + available[i] = true; + } + } + } + + let minIdx = -1; + for (let i = 0; i < n; i++) { + if (available[i] && (minIdx === -1 || servers[i] < servers[minIdx] || + (servers[i] === servers[minIdx] && i < minIdx))) { + minIdx = i; + } + } + + res.push(minIdx); + available[minIdx] = false; + finishTime[minIdx] = time + tasks[t]; + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n)$ +* Space complexity: + * $O(n)$ extra space. + * $O(m)$ space for the output array. + +> Where $m$ is the number of tasks and $n$ is the number of servers. + +--- + +## 2. Two Min-Heaps - I + +::tabs-start + +```python +class Solution: + def assignTasks(self, servers: List[int], tasks: List[int]) -> List[int]: + res = [0] * len(tasks) + available = [(servers[i], i) for i in range(len(servers))] + heapq.heapify(available) + unavailable = [] + + t = 0 + for i in range(len(tasks)): + t = max(t, i) + + if not available: + t = unavailable[0][0] + + while unavailable and t >= unavailable[0][0]: + timeFree, weight, index = heapq.heappop(unavailable) + heapq.heappush(available, (weight, index)) + + weight, index = heapq.heappop(available) + res[i] = index + heapq.heappush(unavailable, (t + tasks[i], weight, index)) + + return res +``` + +```java +public class Solution { + public int[] assignTasks(int[] servers, int[] tasks) { + int n = servers.length, m = tasks.length; + int[] res = new int[m]; + + PriorityQueue available = new PriorityQueue<>( + (a, b) -> a[0] == b[0] ? Integer.compare(a[1], b[1]) : Integer.compare(a[0], b[0]) + ); + PriorityQueue unavailable = new PriorityQueue<>( + Comparator.comparingInt(a -> a[0]) + ); + + for (int i = 0; i < n; i++) { + available.offer(new int[]{servers[i], i}); + } + + int time = 0; + for (int i = 0; i < m; i++) { + time = Math.max(time, i); + + if (available.isEmpty()) { + time = unavailable.peek()[0]; + } + + while (!unavailable.isEmpty() && unavailable.peek()[0] <= time) { + int[] server = unavailable.poll(); + available.offer(new int[]{server[1], server[2]}); + } + + int[] bestServer = available.poll(); + res[i] = bestServer[1]; + unavailable.offer(new int[]{time + tasks[i], bestServer[0], bestServer[1]}); + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector assignTasks(vector& servers, vector& tasks) { + int n = servers.size(), m = tasks.size(); + vector res(m); + + priority_queue, vector>, greater<>> available; + priority_queue, vector>, greater<>> unavailable; + + for (int i = 0; i < n; i++) { + available.emplace(servers[i], i); + } + + int time = 0; + for (int i = 0; i < m; i++) { + time = max(time, i); + + if (available.empty()) { + time = unavailable.top()[0]; + } + + while (!unavailable.empty() && unavailable.top()[0] <= time) { + auto server = unavailable.top(); unavailable.pop(); + available.emplace(server[1], server[2]); + } + + auto [weight, index] = available.top(); available.pop(); + res[i] = index; + unavailable.push({time + tasks[i], weight, index}); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} servers + * @param {number[]} tasks + * @return {number[]} + */ + assignTasks(servers, tasks) { + const n = servers.length; + const available = new PriorityQueue( + (a, b) => a[0] === b[0] ? a[1] - b[1] : a[0] - b[0] + ); + const unavailable = new PriorityQueue((a, b) => a[0] - b[0]); + const res = new Array(tasks.length); + + for (let i = 0; i < n; i++) { + available.enqueue([servers[i], i]); + } + + let time = 0; + for (let i = 0; i < tasks.length; i++) { + time = Math.max(time, i); + if (available.isEmpty()) { + time = unavailable.front()[0]; + } + while (!unavailable.isEmpty() && unavailable.front()[0] <= time) { + const [timeFree, weight, index] = unavailable.dequeue(); + available.enqueue([weight, index]); + } + const [weight, index] = available.dequeue(); + res[i] = index; + unavailable.enqueue([time + tasks[i], weight, index]); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O((n + m) \log n)$ +* Space complexity: + * $O(n)$ extra space. + * $O(m)$ space for the output array. + +> Where $m$ is the number of tasks and $n$ is the number of servers. + +--- + +## 3. Two Min-Heaps - II + +::tabs-start + +```python +class Solution: + def assignTasks(self, servers: List[int], tasks: List[int]) -> List[int]: + res = [] + available = [[weight, i, 0] for i, weight in enumerate(servers)] + unavailable = [] + heapq.heapify(available) + + for i, task in enumerate(tasks): + while unavailable and unavailable[0][0] <= i or not available: + timeFree, weight, index = heapq.heappop(unavailable) + heapq.heappush(available, [weight, index, timeFree]) + + weight, index, timeFree = heapq.heappop(available) + res.append(index) + heapq.heappush(unavailable, [max(timeFree, i) + task, weight, index]) + + return res +``` + +```java +public class Solution { + public int[] assignTasks(int[] servers, int[] tasks) { + int m = tasks.length, n = servers.length; + int[] res = new int[m]; + + PriorityQueue available = new PriorityQueue<>((a, b) -> { + if(a[0] != b[0]) return Integer.compare(a[0], b[0]); + if(a[1] != b[1]) return Integer.compare(a[1], b[1]); + return Integer.compare(a[2], b[2]); + }); + + PriorityQueue unavailable = new PriorityQueue<>((a, b) -> { + if(a[0] != b[0]) return Integer.compare(a[0], b[0]); + if(a[1] != b[1]) return Integer.compare(a[1], b[1]); + return Integer.compare(a[2], b[2]); + }); + + for (int i = 0; i < n; i++) { + available.offer(new int[]{servers[i], i, 0}); + } + + for (int i = 0; i < m; i++) { + while ((!unavailable.isEmpty() && unavailable.peek()[0] <= i) || + available.isEmpty()) { + int[] server = unavailable.poll(); + available.offer(new int[]{server[1], server[2], server[0]}); + } + int[] server = available.poll(); + res[i] = server[1]; + unavailable.offer(new int[]{ + Math.max(server[2], i) + tasks[i], server[0], server[1]} + ); + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector assignTasks(vector& servers, vector& tasks) { + int n = servers.size(), m = tasks.size(); + vector res(m); + + priority_queue, vector>, greater<>> available; + priority_queue, vector>, greater<>> unavailable; + + for (int i = 0; i < n; ++i) { + available.push({servers[i], i, 0}); + } + + for (int i = 0; i < m; ++i) { + while (!unavailable.empty() && (unavailable.top()[0] <= i || + available.empty())) { + auto [timeFree, weight, index] = unavailable.top(); + unavailable.pop(); + available.push({weight, index, timeFree}); + } + + auto [weight, index, timeFree] = available.top(); + available.pop(); + res[i] = index; + unavailable.push({max(timeFree, i) + tasks[i], weight, index}); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} servers + * @param {number[]} tasks + * @return {number[]} + */ + assignTasks(servers, tasks) { + const res = new Array(tasks.length); + const available = new PriorityQueue( + (a, b) => a[0] === b[0] ? (a[1] === b[1] ? a[2] - b[2] : a[1] - b[1]) : a[0] - b[0] + ); + const unavailable = new PriorityQueue( + (a, b) => a[0] === b[0] ? (a[1] === b[1] ? a[2] - b[2] : a[1] - b[1]) : a[0] - b[0] + ); + + for (let i = 0; i < servers.length; i++) { + available.enqueue([servers[i], i, 0]); + } + + for (let i = 0; i < tasks.length; i++) { + while ((!unavailable.isEmpty() && unavailable.front()[0] <= i) || + available.isEmpty()) { + const [timeFree, weight, index] = unavailable.dequeue(); + available.enqueue([weight, index, timeFree]); + } + + const [weight, index, timeFree] = available.dequeue(); + res[i] = index; + unavailable.enqueue([Math.max(timeFree, i) + tasks[i], weight, index]); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O((n + m) \log n)$ +* Space complexity: + * $O(n)$ extra space. + * $O(m)$ space for the output array. + +> Where $m$ is the number of tasks and $n$ is the number of servers. \ No newline at end of file diff --git a/articles/seat-reservation-manager.md b/articles/seat-reservation-manager.md new file mode 100644 index 000000000..c546e1be8 --- /dev/null +++ b/articles/seat-reservation-manager.md @@ -0,0 +1,407 @@ +## 1. Brute Force + +::tabs-start + +```python +class SeatManager: + + def __init__(self, n: int): + self.seats = [False] * n + + def reserve(self) -> int: + for i in range(len(self.seats)): + if not self.seats[i]: + self.seats[i] = True + return i + 1 + + def unreserve(self, seatNumber: int) -> None: + self.seats[seatNumber - 1] = False +``` + +```java +public class SeatManager { + private boolean[] seats; + + public SeatManager(int n) { + seats = new boolean[n]; + } + + public int reserve() { + for (int i = 0; i < seats.length; i++) { + if (!seats[i]) { + seats[i] = true; + return i + 1; + } + } + return -1; + } + + public void unreserve(int seatNumber) { + seats[seatNumber - 1] = false; + } +} +``` + +```cpp +class SeatManager { +private: + vector seats; + +public: + SeatManager(int n) : seats(n, false) {} + + int reserve() { + for (int i = 0; i < seats.size(); i++) { + if (!seats[i]) { + seats[i] = true; + return i + 1; + } + } + return -1; + } + + void unreserve(int seatNumber) { + seats[seatNumber - 1] = false; + } +}; +``` + +```javascript +class SeatManager { + /** + * @param {number} n + */ + constructor(n) { + this.seats = new Array(n).fill(false); + } + + /** + * @return {number} + */ + reserve() { + for (let i = 0; i < this.seats.length; i++) { + if (!this.seats[i]) { + this.seats[i] = true; + return i + 1; + } + } + } + + /** + * @param {number} seatNumber + * @return {void} + */ + unreserve(seatNumber) { + this.seats[seatNumber - 1] = false; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: + * $O(n)$ time for initialization. + * $O(n)$ time for each $reserve()$ function call. + * $O(1)$ time for each $unreserve()$ function call. +* Space complexity: $O(n)$ + +--- + +## 2. Min-Heap + +::tabs-start + +```python +class SeatManager: + def __init__(self, n: int): + self.unres = list(range(1, n + 1)) + heapq.heapify(self.unres) + + def reserve(self) -> int: + return heapq.heappop(self.unres) + + def unreserve(self, seatNumber: int) -> None: + heapq.heappush(self.unres, seatNumber) +``` + +```java +public class SeatManager { + private PriorityQueue unres; + + public SeatManager(int n) { + unres = new PriorityQueue<>(); + for (int i = 1; i <= n; i++) { + unres.offer(i); + } + } + + public int reserve() { + return unres.poll(); + } + + public void unreserve(int seatNumber) { + unres.offer(seatNumber); + } +} +``` + +```cpp +class SeatManager { +private: + priority_queue, greater> unres; + +public: + SeatManager(int n) { + for (int i = 1; i <= n; i++) { + unres.push(i); + } + } + + int reserve() { + int seat = unres.top(); + unres.pop(); + return seat; + } + + void unreserve(int seatNumber) { + unres.push(seatNumber); + } +}; +``` + +```javascript +class SeatManager { + /** + * @param {number} n + */ + constructor(n) { + this.unres = new MinPriorityQueue(); + for (let i = 1; i <= n; i++) { + this.unres.enqueue(i); + } + } + + /** + * @return {number} + */ + reserve() { + return this.unres.dequeue(); + } + + /** + * @param {number} seatNumber + * @return {void} + */ + unreserve(seatNumber) { + this.unres.enqueue(seatNumber); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: + * $O(n \log n)$ time for initialization. + * $O(\log n)$ time for each $reserve()$ function call. + * $O(\log n)$ time for each $unreserve()$ function call. +* Space complexity: $O(n)$ + +--- + +## 3. Min-Heap (Optimal) + +::tabs-start + +```python +class SeatManager: + def __init__(self, n: int): + self.minHeap = [] + self.nextSeat = 1 + + def reserve(self) -> int: + if self.minHeap: + return heapq.heappop(self.minHeap) + + seat = self.nextSeat + self.nextSeat += 1 + return seat + + def unreserve(self, seatNumber: int) -> None: + heapq.heappush(self.minHeap, seatNumber) +``` + +```java +public class SeatManager { + private PriorityQueue minHeap; + private int nextSeat; + + public SeatManager(int n) { + minHeap = new PriorityQueue<>(); + nextSeat = 1; + } + + public int reserve() { + if (!minHeap.isEmpty()) { + return minHeap.poll(); + } + return nextSeat++; + } + + public void unreserve(int seatNumber) { + minHeap.offer(seatNumber); + } +} +``` + +```cpp +class SeatManager { +private: + priority_queue, greater> minHeap; + int nextSeat; + +public: + SeatManager(int n) { + nextSeat = 1; + } + + int reserve() { + if (!minHeap.empty()) { + int seat = minHeap.top(); + minHeap.pop(); + return seat; + } + return nextSeat++; + } + + void unreserve(int seatNumber) { + minHeap.push(seatNumber); + } +}; +``` + +```javascript +class SeatManager { + /** + * @param {number} n + */ + constructor(n) { + this.minHeap = new MinPriorityQueue(); + this.nextSeat = 1; + } + + /** + * @return {number} + */ + reserve() { + if (!this.minHeap.isEmpty()) { + return this.minHeap.dequeue(); + } + return this.nextSeat++; + } + + /** + * @param {number} seatNumber + * @return {void} + */ + unreserve(seatNumber) { + this.minHeap.enqueue(seatNumber); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: + * $O(1)$ time for initialization. + * $O(\log n)$ time for each $reserve()$ function call. + * $O(\log n)$ time for each $unreserve()$ function call. +* Space complexity: $O(n)$ + +--- + +## 4. Ordered Set + +::tabs-start + +```python +class SeatManager: + def __init__(self, n: int): + self.available = SortedSet() + self.nextSeat = 1 + + def reserve(self) -> int: + if self.available: + return self.available.pop(0) + + seat = self.nextSeat + self.nextSeat += 1 + return seat + + def unreserve(self, seatNumber: int) -> None: + self.available.add(seatNumber) +``` + +```java +public class SeatManager { + private TreeSet available; + private int nextSeat; + + public SeatManager(int n) { + available = new TreeSet<>(); + nextSeat = 1; + } + + public int reserve() { + if (!available.isEmpty()) { + return available.pollFirst(); + } + return nextSeat++; + } + + public void unreserve(int seatNumber) { + available.add(seatNumber); + } +} +``` + +```cpp +class SeatManager { +private: + set available; + int nextSeat; + +public: + SeatManager(int n) { + nextSeat = 1; + } + + int reserve() { + if (!available.empty()) { + int seat = *available.begin(); + available.erase(available.begin()); + return seat; + } + return nextSeat++; + } + + void unreserve(int seatNumber) { + available.insert(seatNumber); + } +}; +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: + * $O(1)$ time for initialization. + * $O(\log n)$ time for each $reserve()$ function call. + * $O(\log n)$ time for each $unreserve()$ function call. +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/student-attendance-record-ii.md b/articles/student-attendance-record-ii.md new file mode 100644 index 000000000..b941f3076 --- /dev/null +++ b/articles/student-attendance-record-ii.md @@ -0,0 +1,854 @@ +## 1. Dynamic Programming (Top-Down) - I + +::tabs-start + +```python +class Solution: + def checkRecord(self, n: int) -> int: + MOD = 1000000007 + cache = [[[-1 for _ in range(3)] for _ in range(2)] for _ in range(n + 1)] + + def dfs(i, cntA, cntL): + if i == 0: + return 1 + if cache[i][cntA][cntL] != -1: + return cache[i][cntA][cntL] + + res = dfs(i - 1, cntA, 0) % MOD + + if cntA == 0: + res = (res + dfs(i - 1, 1, 0)) % MOD + + if cntL < 2: + res = (res + dfs(i - 1, cntA, cntL + 1)) % MOD + + cache[i][cntA][cntL] = res + return res + + return dfs(n, 0, 0) +``` + +```java +public class Solution { + private static final int MOD = 1000000007; + private int[][][] cache; + + public int checkRecord(int n) { + this.cache = new int[n + 1][2][3]; + for (int[][] matrix : cache) { + for (int[] row : matrix) { + Arrays.fill(row, -1); + } + } + return dfs(n, 0, 0); + } + + private int dfs(int i, int cntA, int cntL) { + if (i == 0) { + return 1; + } + if (cache[i][cntA][cntL] != -1) { + return cache[i][cntA][cntL]; + } + + int res = dfs(i - 1, cntA, 0) % MOD; + + if (cntA == 0) { + res = (res + dfs(i - 1, 1, 0)) % MOD; + } + + if (cntL < 2) { + res = (res + dfs(i - 1, cntA, cntL + 1)) % MOD; + } + + return cache[i][cntA][cntL] = res; + } +} +``` + +```cpp +class Solution { + const int MOD = 1000000007; + vector>> cache; + +public: + int checkRecord(int n) { + cache.assign(n + 1, vector>(2, vector(3, -1))); + return dfs(n, 0, 0); + } + +private: + int dfs(int i, int cntA, int cntL) { + if (i == 0) { + return 1; + } + if (cache[i][cntA][cntL] != -1) { + return cache[i][cntA][cntL]; + } + + int res = dfs(i - 1, cntA, 0) % MOD; + + if (cntA == 0) { + res = (res + dfs(i - 1, 1, 0)) % MOD; + } + + if (cntL < 2) { + res = (res + dfs(i - 1, cntA, cntL + 1)) % MOD; + } + + return cache[i][cntA][cntL] = res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {number} + */ + checkRecord(n) { + const MOD = 1000000007; + let cache = Array.from({ length: n + 1 }, () => + Array.from({ length: 2 }, () => new Array(3).fill(-1)) + ); + + const dfs = (i, cntA, cntL) => { + if (i === 0) return 1; + if (cache[i][cntA][cntL] !== -1) return cache[i][cntA][cntL]; + + let res = dfs(i - 1, cntA, 0) % MOD; + + if (cntA === 0) { + res = (res + dfs(i - 1, 1, 0)) % MOD; + } + + if (cntL < 2) { + res = (res + dfs(i - 1, cntA, cntL + 1)) % MOD; + } + + return (cache[i][cntA][cntL] = res); + }; + + return dfs(n, 0, 0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Dynamic Programming (Top-Down) - II + +::tabs-start + +```python +class Solution: + def checkRecord(self, n: int) -> int: + MOD = 10**9 + 7 + cache = {} + + def count(n): + if n == 1: + # (A, L) + return { + (0, 0): 1, (0, 1): 1, (0, 2): 0, + (1, 0): 1, (1, 1): 0, (1, 2): 0 + } + + if n in cache: + return cache[n] + + tmp = count(n - 1) + res = defaultdict(int) + + # Choose P + res[(0, 0)] = ((tmp[(0, 0)] + tmp[(0, 1)]) % MOD + tmp[(0, 2)]) % MOD + res[(1, 0)] = ((tmp[(1, 0)] + tmp[(1, 1)]) % MOD + tmp[(1, 2)]) % MOD + + # Choose L + res[(0, 1)] = tmp[(0, 0)] + res[(0, 2)] = tmp[(0, 1)] + res[(1, 1)] = tmp[(1, 0)] + res[(1, 2)] = tmp[(1, 1)] + + # Choose A + res[(1, 0)] += ((tmp[(0, 0)] + tmp[(0, 1)]) % MOD + tmp[(0, 2)]) % MOD + + cache[n] = res + return res + + return sum(count(n).values()) % MOD +``` + +```java +public class Solution { + private static final int MOD = 1000000007; + private int[][][] cache; + private int[][] baseCase; + + public int checkRecord(int n) { + cache = new int[n + 1][2][3]; + baseCase = new int[][]{{1, 1, 0}, {1, 0, 0}}; + for (int[][] matrix : cache) { + for (int[] row : matrix) { + Arrays.fill(row, -1); + } + } + int[][] result = count(n); + int total = 0; + for (int[] row : result) { + for (int val : row) { + total = (total + val) % MOD; + } + } + return total; + } + + private int[][] count(int n) { + if (n == 1) { + // (A, L) + return baseCase; + } + + if (cache[n][0][0] != -1) { + return cache[n]; + } + + int[][] prev = count(n - 1); + int[][] res = cache[n]; + + // Choose P + res[0][0] = ((prev[0][0] + prev[0][1]) % MOD + prev[0][2]) % MOD; + res[1][0] = ((prev[1][0] + prev[1][1]) % MOD + prev[1][2]) % MOD; + + // Choose L + res[0][1] = prev[0][0]; + res[0][2] = prev[0][1]; + res[1][1] = prev[1][0]; + res[1][2] = prev[1][1]; + + // Choose A + res[1][0] = (res[1][0] + ((prev[0][0] + prev[0][1]) % MOD + prev[0][2]) % MOD) % MOD; + + return cache[n]; + } +} +``` + +```cpp +class Solution { +private: + static constexpr int MOD = 1000000007; + vector> baseCase = {{1, 1, 0}, {1, 0, 0}}; + vector>> cache; + +public: + int checkRecord(int n) { + cache.assign(n + 1, vector>(2, vector(3, -1))); + const vector>& result = count(n); + int total = 0; + for (const auto& row : result) { + for (int val : row) { + total = (total + val) % MOD; + } + } + return total; + } + +private: + const vector>& count(int n) { + if (n == 1) { + return baseCase; + } + + if (cache[n][0][0] != -1) { + return cache[n]; + } + + const vector>& prev = count(n - 1); + auto& res = cache[n]; + + // Choose P + res[0][0] = ((prev[0][0] + prev[0][1]) % MOD + prev[0][2]) % MOD; + res[1][0] = ((prev[1][0] + prev[1][1]) % MOD + prev[1][2]) % MOD; + + // Choose L + res[0][1] = prev[0][0]; + res[0][2] = prev[0][1]; + res[1][1] = prev[1][0]; + res[1][2] = prev[1][1]; + + // Choose A + res[1][0] = (res[1][0] + ((prev[0][0] + prev[0][1]) % MOD + prev[0][2]) % MOD) % MOD; + + return cache[n]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {number} + */ + checkRecord(n) { + const MOD = 1000000007; + const baseCase = [ + [1, 1, 0], // (A = 0, L = 0, 1, 2) + [1, 0, 0] // (A = 1, L = 0, 1, 2) + ]; + let cache = Array.from({ length: n + 1 }, () => + Array.from({ length: 2 }, () => new Array(3).fill(-1)) + ); + + const count = (n) => { + if (n === 1) return baseCase; + if (cache[n][0][0] !== -1) return cache[n]; + + const prev = count(n - 1); + const res = cache[n]; + + // Choose P + res[0][0] = ((prev[0][0] + prev[0][1]) % MOD + prev[0][2]) % MOD; + res[1][0] = ((prev[1][0] + prev[1][1]) % MOD + prev[1][2]) % MOD; + + // Choose L + res[0][1] = prev[0][0]; + res[0][2] = prev[0][1]; + res[1][1] = prev[1][0]; + res[1][2] = prev[1][1]; + + // Choose A + res[1][0] = (res[1][0] + ((prev[0][0] + prev[0][1]) % MOD + prev[0][2]) % MOD) % MOD; + + return res; + }; + + const result = count(n); + let total = 0; + for (const row of result) { + for (const val of row) { + total = (total + val) % MOD; + } + } + return total; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def checkRecord(self, n: int) -> int: + MOD = 1000000007 + dp = [[[0 for _ in range(3)] for _ in range(2)] for _ in range(n + 1)] + + dp[0][0][0] = 1 # Base case + + for i in range(1, n + 1): + for cntA in range(2): + for cntL in range(3): + # Choose P + dp[i][cntA][0] = (dp[i][cntA][0] + dp[i - 1][cntA][cntL]) % MOD + + # Choose A + if cntA > 0: + dp[i][cntA][0] = (dp[i][cntA][0] + dp[i - 1][cntA - 1][cntL]) % MOD + + # Choose L + if cntL > 0: + dp[i][cntA][cntL] = (dp[i][cntA][cntL] + dp[i - 1][cntA][cntL - 1]) % MOD + + return sum(dp[n][cntA][cntL] for cntA in range(2) for cntL in range(3)) % MOD +``` + +```java +public class Solution { + public int checkRecord(int n) { + final int MOD = 1000000007; + int[][][] dp = new int[n + 1][2][3]; + + dp[0][0][0] = 1; + + for (int i = 1; i <= n; i++) { + for (int cntA = 0; cntA < 2; cntA++) { + for (int cntL = 0; cntL < 3; cntL++) { + // Choose P + dp[i][cntA][0] = (dp[i][cntA][0] + dp[i - 1][cntA][cntL]) % MOD; + + // Choose A + if (cntA > 0) { + dp[i][cntA][0] = (dp[i][cntA][0] + dp[i - 1][cntA - 1][cntL]) % MOD; + } + + // Choose L + if (cntL > 0) { + dp[i][cntA][cntL] = (dp[i][cntA][cntL] + dp[i - 1][cntA][cntL - 1]) % MOD; + } + } + } + } + + int result = 0; + for (int cntA = 0; cntA < 2; cntA++) { + for (int cntL = 0; cntL < 3; cntL++) { + result = (result + dp[n][cntA][cntL]) % MOD; + } + } + + return result; + } +} +``` + +```cpp +class Solution { +public: + int checkRecord(int n) { + const int MOD = 1000000007; + vector>> dp(n + 1, vector>(2, vector(3, 0))); + + dp[0][0][0] = 1; + + for (int i = 1; i <= n; i++) { + for (int cntA = 0; cntA < 2; cntA++) { + for (int cntL = 0; cntL < 3; cntL++) { + // Choose P + dp[i][cntA][0] = (dp[i][cntA][0] + dp[i - 1][cntA][cntL]) % MOD; + + // Choose A + if (cntA > 0) { + dp[i][cntA][0] = (dp[i][cntA][0] + dp[i - 1][cntA - 1][cntL]) % MOD; + } + + // Choose L + if (cntL > 0) { + dp[i][cntA][cntL] = (dp[i][cntA][cntL] + dp[i - 1][cntA][cntL - 1]) % MOD; + } + } + } + } + + int result = 0; + for (int cntA = 0; cntA < 2; cntA++) { + for (int cntL = 0; cntL < 3; cntL++) { + result = (result + dp[n][cntA][cntL]) % MOD; + } + } + + return result; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {number} + */ + checkRecord(n) { + const MOD = 1000000007; + const dp = Array.from({ length: n + 1 }, () => + Array.from({ length: 2 }, () => new Array(3).fill(0)) + ); + + dp[0][0][0] = 1; + + for (let i = 1; i <= n; i++) { + for (let cntA = 0; cntA < 2; cntA++) { + for (let cntL = 0; cntL < 3; cntL++) { + // Choose P + dp[i][cntA][0] = (dp[i][cntA][0] + dp[i - 1][cntA][cntL]) % MOD; + + // Choose A + if (cntA > 0) { + dp[i][cntA][0] = (dp[i][cntA][0] + dp[i - 1][cntA - 1][cntL]) % MOD; + } + + // Choose L + if (cntL > 0) { + dp[i][cntA][cntL] = (dp[i][cntA][cntL] + dp[i - 1][cntA][cntL - 1]) % MOD; + } + } + } + } + + let result = 0; + for (let cntA = 0; cntA < 2; cntA++) { + for (let cntL = 0; cntL < 3; cntL++) { + result = (result + dp[n][cntA][cntL]) % MOD; + } + } + + return result; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 4. Dynamic Programming (Space Optimized) - I + +::tabs-start + +```python +class Solution: + def checkRecord(self, n: int) -> int: + if n == 1: + return 3 + + MOD = 10**9 + 7 + dp = { + (0, 0): 1, (0, 1): 1, (0, 2): 0, + (1, 0): 1, (1, 1): 0, (1, 2): 0 + } + + for i in range(n - 1): + ndp = defaultdict(int) + + # Choose P + ndp[(0, 0)] = ((dp[(0, 0)] + dp[(0, 1)]) % MOD + dp[(0, 2)]) % MOD + ndp[(1, 0)] = ((dp[(1, 0)] + dp[(1, 1)]) % MOD + dp[(1, 2)]) % MOD + + # Choose L + ndp[(0, 1)] = dp[(0, 0)] + ndp[(1, 1)] = dp[(1, 0)] + ndp[(0, 2)] = dp[(0, 1)] + ndp[(1, 2)] = dp[(1, 1)] + + # Choose A + ndp[(1, 0)] = (ndp[(1, 0)] + (((dp[(0, 0)] + dp[(0, 1)]) % MOD + dp[(0, 2)]) % MOD)) % MOD + + dp = ndp + + return sum(dp.values()) % MOD +``` + +```java +public class Solution { + public int checkRecord(int n) { + if (n == 1) return 3; + + final int MOD = 1000000007; + int[][] dp = {{1, 1, 0}, {1, 0, 0}}; + + for (int i = 0; i < n - 1; i++) { + int[][] ndp = new int[2][3]; + + // Choose P + ndp[0][0] = ((dp[0][0] + dp[0][1]) % MOD + dp[0][2]) % MOD; + ndp[1][0] = ((dp[1][0] + dp[1][1]) % MOD + dp[1][2]) % MOD; + + // Choose L + ndp[0][1] = dp[0][0]; + ndp[1][1] = dp[1][0]; + ndp[0][2] = dp[0][1]; + ndp[1][2] = dp[1][1]; + + // Choose A + ndp[1][0] = (ndp[1][0] + ((dp[0][0] + dp[0][1]) % MOD + dp[0][2]) % MOD) % MOD; + + dp = ndp; + } + + int total = 0; + for (int[] row : dp) { + for (int val : row) { + total = (total + val) % MOD; + } + } + return total; + } +} +``` + +```cpp +class Solution { +public: + int checkRecord(int n) { + if (n == 1) return 3; + + const int MOD = 1000000007; + vector> dp = {{1, 1, 0}, {1, 0, 0}}; + + for (int i = 0; i < n - 1; i++) { + vector> ndp(2, vector(3, 0)); + + // Choose P + ndp[0][0] = ((dp[0][0] + dp[0][1]) % MOD + dp[0][2]) % MOD; + ndp[1][0] = ((dp[1][0] + dp[1][1]) % MOD + dp[1][2]) % MOD; + + // Choose L + ndp[0][1] = dp[0][0]; + ndp[1][1] = dp[1][0]; + ndp[0][2] = dp[0][1]; + ndp[1][2] = dp[1][1]; + + // Choose A + ndp[1][0] = (ndp[1][0] + ((dp[0][0] + dp[0][1]) % MOD + dp[0][2]) % MOD) % MOD; + + swap(dp, ndp); + } + + int total = 0; + for (auto& row : dp) { + for (int val : row) { + total = (total + val) % MOD; + } + } + return total; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {number} + */ + checkRecord(n) { + if (n === 1) return 3; + + const MOD = 1000000007; + let dp = [[1, 1, 0], [1, 0, 0]]; + + for (let i = 0; i < n - 1; i++) { + let ndp = Array.from({ length: 2 }, () => new Array(3).fill(0)); + + // Choose P + ndp[0][0] = ((dp[0][0] + dp[0][1]) % MOD + dp[0][2]) % MOD; + ndp[1][0] = ((dp[1][0] + dp[1][1]) % MOD + dp[1][2]) % MOD; + + // Choose L + ndp[0][1] = dp[0][0]; + ndp[1][1] = dp[1][0]; + ndp[0][2] = dp[0][1]; + ndp[1][2] = dp[1][1]; + + // Choose A + ndp[1][0] = (ndp[1][0] + ((dp[0][0] + dp[0][1]) % MOD + dp[0][2]) % MOD) % MOD; + + [dp, ndp] = [ndp, dp]; + } + + let total = 0; + for (let row of dp) { + for (let val of row) { + total = (total + val) % MOD; + } + } + return total; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ + +--- + +## 5. Dynamic Programming (Space Optimized) - II + +::tabs-start + +```python +class Solution: + def checkRecord(self, n: int) -> int: + MOD = 1000000007 + dp = [[0] * 3 for _ in range(2)] + + dp[0][0] = 1 # Base case + + for i in range(1, n + 1): + next_dp = [[0] * 3 for _ in range(2)] + + for cntA in range(2): + for cntL in range(3): + # Choose P + next_dp[cntA][0] = (next_dp[cntA][0] + dp[cntA][cntL]) % MOD + + # Choose A + if cntA > 0: + next_dp[cntA][0] = (next_dp[cntA][0] + dp[cntA - 1][cntL]) % MOD + + # Choose L + if cntL > 0: + next_dp[cntA][cntL] = (next_dp[cntA][cntL] + dp[cntA][cntL - 1]) % MOD + + dp = next_dp + + return sum(dp[cntA][cntL] for cntA in range(2) for cntL in range(3)) % MOD +``` + +```java +public class Solution { + public int checkRecord(int n) { + final int MOD = 1000000007; + int[][] dp = new int[2][3]; + + dp[0][0] = 1; + + for (int i = 1; i <= n; i++) { + int[][] nextDp = new int[2][3]; + + for (int cntA = 0; cntA < 2; cntA++) { + for (int cntL = 0; cntL < 3; cntL++) { + // Choose P + nextDp[cntA][0] = (nextDp[cntA][0] + dp[cntA][cntL]) % MOD; + + // Choose A + if (cntA > 0) { + nextDp[cntA][0] = (nextDp[cntA][0] + dp[cntA - 1][cntL]) % MOD; + } + + // Choose L + if (cntL > 0) { + nextDp[cntA][cntL] = (nextDp[cntA][cntL] + dp[cntA][cntL - 1]) % MOD; + } + } + } + + dp = nextDp; + } + + int result = 0; + for (int cntA = 0; cntA < 2; cntA++) { + for (int cntL = 0; cntL < 3; cntL++) { + result = (result + dp[cntA][cntL]) % MOD; + } + } + return result; + } +} +``` + +```cpp +class Solution { +public: + int checkRecord(int n) { + const int MOD = 1000000007; + vector> dp(2, vector(3, 0)); + + dp[0][0] = 1; + + for (int i = 1; i <= n; i++) { + vector> nextDp(2, vector(3, 0)); + + for (int cntA = 0; cntA < 2; cntA++) { + for (int cntL = 0; cntL < 3; cntL++) { + // Choose P + nextDp[cntA][0] = (nextDp[cntA][0] + dp[cntA][cntL]) % MOD; + + // Choose A + if (cntA > 0) { + nextDp[cntA][0] = (nextDp[cntA][0] + dp[cntA - 1][cntL]) % MOD; + } + + // Choose L + if (cntL > 0) { + nextDp[cntA][cntL] = (nextDp[cntA][cntL] + dp[cntA][cntL - 1]) % MOD; + } + } + } + + dp = nextDp; + } + + int result = 0; + for (int cntA = 0; cntA < 2; cntA++) { + for (int cntL = 0; cntL < 3; cntL++) { + result = (result + dp[cntA][cntL]) % MOD; + } + } + + return result; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {number} + */ + checkRecord(n) { + const MOD = 1000000007; + let dp = Array.from({ length: 2 }, () => new Array(3).fill(0)); + + dp[0][0] = 1; + + for (let i = 1; i <= n; i++) { + let nextDp = Array.from({ length: 2 }, () => new Array(3).fill(0)); + + for (let cntA = 0; cntA < 2; cntA++) { + for (let cntL = 0; cntL < 3; cntL++) { + // Choose P + nextDp[cntA][0] = (nextDp[cntA][0] + dp[cntA][cntL]) % MOD; + + // Choose A + if (cntA > 0) { + nextDp[cntA][0] = (nextDp[cntA][0] + dp[cntA - 1][cntL]) % MOD; + } + + // Choose L + if (cntL > 0) { + nextDp[cntA][cntL] = (nextDp[cntA][cntL] + dp[cntA][cntL - 1]) % MOD; + } + } + } + + dp = nextDp; + } + + let result = 0; + for (let cntA = 0; cntA < 2; cntA++) { + for (let cntL = 0; cntL < 3; cntL++) { + result = (result + dp[cntA][cntL]) % MOD; + } + } + + return result; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ \ No newline at end of file From 822c53a55c4c99d8cc894bc2d3f27ee5fc9d79fb Mon Sep 17 00:00:00 2001 From: Sri Hari <94112314+Srihari2222@users.noreply.github.com> Date: Mon, 10 Mar 2025 19:58:58 +0530 Subject: [PATCH 37/45] Sri Hari: Batch-6/Neetcode-150/Added-hints (#3909) * Batch-6/Neetcode-150/Added-hints * Batch-6/Neetcode-150/Added-hints * Batch-6/Neetcode-150/Added-hints * Batch-6/Neetcode-150/Added-hints * Batch-6/Neetcode-150/Added-hints --- articles/counting-bits.md | 26 ++++++---- articles/insert-new-interval.md | 12 +++-- articles/merge-intervals.md | 4 +- articles/plus-one.md | 14 +++--- articles/set-zeroes-in-matrix.md | 2 +- articles/spiral-matrix.md | 12 +++-- articles/string-encode-and-decode.md | 8 ++-- .../subarrays-with-k-different-integers.md | 2 +- hints/burst-balloons.md | 39 +++++++++++++++ hints/buy-and-sell-crypto-with-cooldown.md | 39 +++++++++++++++ hints/coin-change-ii.md | 39 +++++++++++++++ hints/count-paths.md | 39 +++++++++++++++ hints/count-squares.md | 39 +++++++++++++++ hints/count-subsequences.md | 39 +++++++++++++++ hints/counting-bits.md | 31 ++++++++++++ hints/edit-distance.md | 39 +++++++++++++++ hints/gas-station.md | 31 ++++++++++++ hints/hand-of-straights.md | 31 ++++++++++++ hints/insert-new-interval.md | 31 ++++++++++++ hints/interleaving-string.md | 39 +++++++++++++++ hints/jump-game-ii.md | 39 +++++++++++++++ hints/jump-game.md | 39 +++++++++++++++ hints/longest-common-subsequence.md | 31 ++++++++++++ hints/longest-increasing-path-in-matrix.md | 23 +++++++++ hints/longest-increasing-subsequence.md | 47 +++++++++++++++++++ hints/maximum-product-subarray.md | 31 ++++++++++++ hints/maximum-subarray.md | 39 +++++++++++++++ hints/meeting-schedule-ii.md | 39 +++++++++++++++ hints/meeting-schedule.md | 31 ++++++++++++ hints/merge-intervals.md | 39 +++++++++++++++ hints/merge-triplets-to-form-target.md | 31 ++++++++++++ hints/minimum-interval-including-query.md | 31 ++++++++++++ hints/missing-number.md | 39 +++++++++++++++ hints/multiply-strings.md | 39 +++++++++++++++ hints/non-cyclical-number.md | 23 +++++++++ hints/non-overlapping-intervals.md | 39 +++++++++++++++ hints/number-of-one-bits.md | 31 ++++++++++++ hints/partition-equal-subset-sum.md | 39 +++++++++++++++ hints/partition-labels.md | 31 ++++++++++++ hints/plus-one.md | 23 +++++++++ hints/pow-x-n.md | 39 +++++++++++++++ hints/regular-expression-matching.md | 39 +++++++++++++++ hints/reverse-bits.md | 31 ++++++++++++ hints/reverse-integer.md | 31 ++++++++++++ hints/rotate-matrix.md | 31 ++++++++++++ hints/set-zeroes-in-matrix.md | 39 +++++++++++++++ hints/single-number.md | 39 +++++++++++++++ hints/spiral-matrix.md | 31 ++++++++++++ hints/string-encode-and-decode.md | 2 +- hints/sum-of-two-integers.md | 39 +++++++++++++++ hints/target-sum.md | 31 ++++++++++++ hints/valid-parenthesis-string.md | 39 +++++++++++++++ hints/word-break.md | 31 ++++++++++++ 53 files changed, 1594 insertions(+), 28 deletions(-) create mode 100644 hints/burst-balloons.md create mode 100644 hints/buy-and-sell-crypto-with-cooldown.md create mode 100644 hints/coin-change-ii.md create mode 100644 hints/count-paths.md create mode 100644 hints/count-squares.md create mode 100644 hints/count-subsequences.md create mode 100644 hints/counting-bits.md create mode 100644 hints/edit-distance.md create mode 100644 hints/gas-station.md create mode 100644 hints/hand-of-straights.md create mode 100644 hints/insert-new-interval.md create mode 100644 hints/interleaving-string.md create mode 100644 hints/jump-game-ii.md create mode 100644 hints/jump-game.md create mode 100644 hints/longest-common-subsequence.md create mode 100644 hints/longest-increasing-path-in-matrix.md create mode 100644 hints/longest-increasing-subsequence.md create mode 100644 hints/maximum-product-subarray.md create mode 100644 hints/maximum-subarray.md create mode 100644 hints/meeting-schedule-ii.md create mode 100644 hints/meeting-schedule.md create mode 100644 hints/merge-intervals.md create mode 100644 hints/merge-triplets-to-form-target.md create mode 100644 hints/minimum-interval-including-query.md create mode 100644 hints/missing-number.md create mode 100644 hints/multiply-strings.md create mode 100644 hints/non-cyclical-number.md create mode 100644 hints/non-overlapping-intervals.md create mode 100644 hints/number-of-one-bits.md create mode 100644 hints/partition-equal-subset-sum.md create mode 100644 hints/partition-labels.md create mode 100644 hints/plus-one.md create mode 100644 hints/pow-x-n.md create mode 100644 hints/regular-expression-matching.md create mode 100644 hints/reverse-bits.md create mode 100644 hints/reverse-integer.md create mode 100644 hints/rotate-matrix.md create mode 100644 hints/set-zeroes-in-matrix.md create mode 100644 hints/single-number.md create mode 100644 hints/spiral-matrix.md create mode 100644 hints/sum-of-two-integers.md create mode 100644 hints/target-sum.md create mode 100644 hints/valid-parenthesis-string.md create mode 100644 hints/word-break.md diff --git a/articles/counting-bits.md b/articles/counting-bits.md index 514bef953..ac8139aba 100644 --- a/articles/counting-bits.md +++ b/articles/counting-bits.md @@ -124,8 +124,10 @@ class Solution { ### Time & Space Complexity -* Time complexity: $O(n)$ -* Space complexity: $O(1)$ +* Time complexity: $O(n \log n)$ +* Space complexity: + * $O(1)$ extra space. + * $O(n)$ space for the output array. --- @@ -248,8 +250,10 @@ class Solution { ### Time & Space Complexity -* Time complexity: $O(n)$ -* Space complexity: $O(1)$ +* Time complexity: $O(n \log n)$ +* Space complexity: + * $O(1)$ extra space. + * $O(n)$ space for the output array. --- @@ -338,8 +342,10 @@ class Solution { ### Time & Space Complexity -* Time complexity: $O(n)$ -* Space complexity: $O(1)$ +* Time complexity: $O(n \log n)$ +* Space complexity: + * $O(1)$ extra space. + * $O(n)$ space for the output array. --- @@ -470,7 +476,9 @@ class Solution { ### Time & Space Complexity * Time complexity: $O(n)$ -* Space complexity: $O(1)$ +* Space complexity: + * $O(1)$ extra space. + * $O(n)$ space for the output array. --- @@ -567,4 +575,6 @@ class Solution { ### Time & Space Complexity * Time complexity: $O(n)$ -* Space complexity: $O(1)$ \ No newline at end of file +* Space complexity: + * $O(1)$ extra space. + * $O(n)$ space for the output array. \ No newline at end of file diff --git a/articles/insert-new-interval.md b/articles/insert-new-interval.md index e35159e6e..367a09391 100644 --- a/articles/insert-new-interval.md +++ b/articles/insert-new-interval.md @@ -221,7 +221,9 @@ class Solution { ### Time & Space Complexity * Time complexity: $O(n)$ -* Space complexity: $O(1)$ +* Space complexity: + * $O(1)$ extra space. + * $O(n)$ space for the output list. --- @@ -515,7 +517,9 @@ class Solution { ### Time & Space Complexity * Time complexity: $O(n)$ -* Space complexity: $O(1)$ +* Space complexity: + * $O(1)$ extra space. + * $O(n)$ space for the output list. --- @@ -708,4 +712,6 @@ class Solution { ### Time & Space Complexity * Time complexity: $O(n)$ -* Space complexity: $O(1)$ \ No newline at end of file +* Space complexity: + * $O(1)$ extra space. + * $O(n)$ space for the output list. \ No newline at end of file diff --git a/articles/merge-intervals.md b/articles/merge-intervals.md index 56f8a6313..4ff97b569 100644 --- a/articles/merge-intervals.md +++ b/articles/merge-intervals.md @@ -169,7 +169,9 @@ class Solution { ### Time & Space Complexity * Time complexity: $O(n \log n)$ -* Space complexity: $O(1)$ or $O(n)$ depending on the sorting algorithm. +* Space complexity: + * $O(1)$ or $O(n)$ space depending on the sorting algorithm. + * $O(n)$ for the output list. --- diff --git a/articles/plus-one.md b/articles/plus-one.md index 242d171fb..4b51b5b0c 100644 --- a/articles/plus-one.md +++ b/articles/plus-one.md @@ -139,7 +139,7 @@ class Solution { --- -## 2. Iteration +## 2. Iteration - I ::tabs-start @@ -148,7 +148,7 @@ class Solution: def plusOne(self, digits: List[int]) -> List[int]: one = 1 i = 0 - digits = digits[::-1] + digits.reverse() while one: if i < len(digits): @@ -161,7 +161,9 @@ class Solution: digits.append(one) one = 0 i += 1 - return digits[::-1] + + digits.reverse() + return digits ``` ```java @@ -344,11 +346,11 @@ class Solution { ### Time & Space Complexity * Time complexity: $O(n)$ -* Space complexity: $O(1)$ +* Space complexity: $O(1)$ or $O(n)$ depending on the language. --- -## 3. Iteration (Optimal) +## 3. Iteration - II ::tabs-start @@ -479,4 +481,4 @@ class Solution { ### Time & Space Complexity * Time complexity: $O(n)$ -* Space complexity: $O(1)$ \ No newline at end of file +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/set-zeroes-in-matrix.md b/articles/set-zeroes-in-matrix.md index 905883b5c..5a265e1f5 100644 --- a/articles/set-zeroes-in-matrix.md +++ b/articles/set-zeroes-in-matrix.md @@ -212,7 +212,7 @@ class Solution { ### Time & Space Complexity -* Time complexity: $O(m * n)$ +* Time complexity: $O((m * n) * (m + n))$ * Space complexity: $O(m * n)$ > Where $m$ is the number of rows and $n$ is the number of columns. diff --git a/articles/spiral-matrix.md b/articles/spiral-matrix.md index b9719b928..7935874fc 100644 --- a/articles/spiral-matrix.md +++ b/articles/spiral-matrix.md @@ -203,7 +203,9 @@ class Solution { ### Time & Space Complexity * Time complexity: $O(m * n)$ -* Space complexity: $O(min(m, n))$ +* Space complexity: + * $O(min(m, n))$ space for recursion stack. + * $O(m * n)$ space for the output list. > Where $m$ is the number of rows and $n$ is the number of columns. @@ -467,7 +469,9 @@ class Solution { ### Time & Space Complexity * Time complexity: $O(m * n)$ -* Space complexity: $O(1)$ +* Space complexity: + * $O(1)$ extra space. + * $O(m * n)$ space for the output list. > Where $m$ is the number of rows and $n$ is the number of columns. @@ -655,6 +659,8 @@ class Solution { ### Time & Space Complexity * Time complexity: $O(m * n)$ -* Space complexity: $O(1)$ +* Space complexity: + * $O(1)$ extra space. + * $O(m * n)$ space for the output list. > Where $m$ is the number of rows and $n$ is the number of columns. \ No newline at end of file diff --git a/articles/string-encode-and-decode.md b/articles/string-encode-and-decode.md index c0cad7e12..b60d5115e 100644 --- a/articles/string-encode-and-decode.md +++ b/articles/string-encode-and-decode.md @@ -286,8 +286,8 @@ class Solution { ### Time & Space Complexity -* Time complexity: $O(m)$ for $encode()$ and $decode()$. -* Space complexity: $O(n)$ for $encode()$ and $decode()$. +* Time complexity: $O(m)$ for each $encode()$ and $decode()$ function calls. +* Space complexity: $O(m + n)$ for each $encode()$ and $decode()$ function calls. > Where $m$ is the sum of lengths of all the strings and $n$ is the number of strings. @@ -510,7 +510,7 @@ class Solution { ### Time & Space Complexity -* Time complexity: $O(m)$ for $encode()$ and $decode()$. -* Space complexity: $O(1)$ for $encode()$ and $decode()$. +* Time complexity: $O(m)$ for each $encode()$ and $decode()$ function calls. +* Space complexity: $O(m + n)$ for each $encode()$ and $decode()$ function calls. > Where $m$ is the sum of lengths of all the strings and $n$ is the number of strings. \ No newline at end of file diff --git a/articles/subarrays-with-k-different-integers.md b/articles/subarrays-with-k-different-integers.md index 26328205c..a2e24a470 100644 --- a/articles/subarrays-with-k-different-integers.md +++ b/articles/subarrays-with-k-different-integers.md @@ -246,7 +246,7 @@ class Solution { --- -## 3. Slidingt Window (One Pass) - I +## 3. Sliding Window (One Pass) - I ::tabs-start diff --git a/hints/burst-balloons.md b/hints/burst-balloons.md new file mode 100644 index 000000000..c553a4f8e --- /dev/null +++ b/hints/burst-balloons.md @@ -0,0 +1,39 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution with O(n ^ 3) time and O(n ^ 2) space, where n is the size of the input array. +

+
+ +
+
+ Hint 1 +

+ Try to simulate the process recursively by passing the array to the recursive function. At each step, iterate through the array, pop an element, and recursively apply the same process to the two subarrays on both sides of the popped element, returning the maximum result from all recursive paths. This approach is exponential. Can you think of a way to optimize it? Maybe you should consider observing the subproblems instead of modifying the array. +

+
+ +
+
+ Hint 2 +

+ Instead of passing the array, we can pass the range of indices l and r that need to be processed. We pad the input array with 1s on both sides for easier computation, but l and r represent the first and last indices of the original input array. Can you think of a reverse engineering approach for popping elements? +

+
+ +
+
+ Hint 3 +

+ We determine the result by considering each element as the last one to be popped in the current range. For each element, we calculate its value by multiplying it with the elements at l - 1 and r + 1, then recursively solve the subproblems for the ranges (l, i - 1) and (i + 1, r), where i is the current element in the given range. Can you think of a way to optimize and avoid redundant calculations? +

+
+ +
+
+ Hint 4 +

+ We can use memoization to cache the results of recursive calls and avoid redundant calculations. A hash map or a 2D array can be used to store results since the recursive function parameters l and r are within the range of the input array size. +

+
\ No newline at end of file diff --git a/hints/buy-and-sell-crypto-with-cooldown.md b/hints/buy-and-sell-crypto-with-cooldown.md new file mode 100644 index 000000000..84988bd54 --- /dev/null +++ b/hints/buy-and-sell-crypto-with-cooldown.md @@ -0,0 +1,39 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution as good or better than O(n) time and O(n) space, where n is the size of the input array. +

+
+ +
+
+ Hint 1 +

+ Try to think in terms of recursion and visualize it as a decision tree. Can you determine the possible decisions at each recursion step? Also, can you identify the base cases and the essential information that needs to be tracked during recursion? +

+
+ +
+
+ Hint 2 +

+ At each recursion step, we can buy only if we haven't already bought a coin, or we can sell if we own one. When buying, we subtract the coin value, and when selling, we add it. We explore all possible buying and selling options recursively, iterating through the coins from left to right using index i. For the cooldown condition, if we buy a coin, we increment the index i by two. +

+
+ +
+
+ Hint 3 +

+ We can use a boolean variable canBuy to indicate whether buying is allowed at the current recursive step. If we go out of bounds, we return 0. This approach is exponential. Can you think of a way to optimize it? +

+
+ +
+
+ Hint 4 +

+ We can use memoization to cache the results of recursive calls and avoid recalculations. A hash map or a 2D array can be used to store these results. +

+
\ No newline at end of file diff --git a/hints/coin-change-ii.md b/hints/coin-change-ii.md new file mode 100644 index 000000000..3f18a6318 --- /dev/null +++ b/hints/coin-change-ii.md @@ -0,0 +1,39 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution as good or better than O(n * a) time and O(n * a) space, where n is the number of coins and a is the given amount. +

+
+ +
+
+ Hint 1 +

+ As we need to find the total number of combinations, think in terms of recursion and visualize it as a decision tree where multiple coin choices are available at each recursion step. Can you determine a way to allow picking the same coin multiple times? Maybe you should consider the decisions made at each recursion step. +

+
+ +
+
+ Hint 2 +

+ The given coins are unique. We recursively iterate through the coins array using index i, tracking the collected amount along the current path. At each step, we can either skip the current coin or pick it, ensuring the total does not exceed the target. To allow picking the same coin multiple times, we recurse with the same index but an updated amount, generating different combinations. +

+
+ +
+
+ Hint 3 +

+ If we reach the target amount, we return 1. The recursion stops if the index goes out of bounds. We count all possible ways and return the total. This approach is exponential. Can you think of a way to optimize it? Maybe you should consider an approach to avoid redundant computations. +

+
+ +
+
+ Hint 4 +

+ We can use memoization to cache the results of recursive calls and avoid redundant computations. A hash map or a 2D array can be used to store these results. +

+
\ No newline at end of file diff --git a/hints/count-paths.md b/hints/count-paths.md new file mode 100644 index 000000000..299793547 --- /dev/null +++ b/hints/count-paths.md @@ -0,0 +1,39 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution as good or better than O(m * n) time and O(m * n) space, where m is the number of rows and n is the number of columns in the grid. +

+
+ +
+
+ Hint 1 +

+ Try to think in terms of recursion and visualize it as a decision tree, where we have two choices at each step. Can you determine the base condition and recurrence relation? +

+
+ +
+
+ Hint 2 +

+ We recursively traverse the grid using row i and column j. At each step, we explore both possibilities: moving down or moving right, ensuring we do not go out of bounds. If we reach the bottom-right cell, we return 1. +

+
+ +
+
+ Hint 3 +

+ This approach has exponential complexity. Can you think of a way to optimize the recursion? Maybe you should consider using a dynamic programming approach. +

+
+ +
+
+ Hint 4 +

+ We can use memoization to cache the results of recursive calls and avoid recalculations. A hash map or a 2D array can be used to store these results. +

+
\ No newline at end of file diff --git a/hints/count-squares.md b/hints/count-squares.md new file mode 100644 index 000000000..69b289bdb --- /dev/null +++ b/hints/count-squares.md @@ -0,0 +1,39 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution with O(1) time for each add() call, O(n) time for each count() call, and O(n) space, where n is the total number of points. +

+
+ +
+
+ Hint 1 +

+ Initially, we can store the points in a global list for the add() call. For the count() call, a brute force approach would use three nested loops to check other points except for the query point, resulting in an O(n^3) time solution. Can you think of a better way? Maybe you should consider the observation that can be drawn from the diagonal of a square. +

+
+ +
+
+ Hint 2 +

+ In a square's diagonal, the absolute difference between the x-coordinates is equal to the absolute difference between the y-coordinates of the two endpoints, and neither difference can be zero. Using these two points, we can determine the other diagonal endpoints. +

+
+ +
+
+ Hint 3 +

+ We store points in a hash map instead of a list for O(1) lookups, treating duplicate points as one while tracking their frequencies. For the count() function, we iterate through points that, along with the query point, can form a diagonal. Let the query point be (qx, qy) and the other point be (x, y), ensuring they form a diagonal. What could be the other two points? Maybe you should consider the points forming a right-to-left diagonal, treating (qx, qy) as the top-right corner. +

+
+ +
+
+ Hint 4 +

+ The other two points are point1 (x, qy) and point2 (qx, y). For counting, we simply add count of point1 * count of point2 to the result res. +

+
\ No newline at end of file diff --git a/hints/count-subsequences.md b/hints/count-subsequences.md new file mode 100644 index 000000000..53a37dcee --- /dev/null +++ b/hints/count-subsequences.md @@ -0,0 +1,39 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution as good or better than O(m * n) time and O(m * n) space, where m is the length of the string s and n is the length of the string t. +

+
+ +
+
+ Hint 1 +

+ Try to think in terms of recursion and visualize it as a decision tree, as we need to explore all subsequences of s. Can you determine the possible decisions at each recursion step? +

+
+ +
+
+ Hint 2 +

+ We recursively iterate through the strings using indices i and j for s and t, respectively. At each recursion step, we can either skip the current character of s or include it in the current path if it matches the current character of t. Can you determine the base conditions for this recursive function? +

+
+ +
+
+ Hint 3 +

+ If index j goes out of bounds, we return 1 as a valid subsequence is found. If index i goes out of bounds first, we return 0. At each recursion step, we return the sum of both paths. This approach is exponential. Can you think of a way to optimize it? +

+
+ +
+
+ Hint 4 +

+ We can use memoization to cache the results of recursive calls and avoid redundant computations. A hash map or a 2D array can be used to store these results. +

+
\ No newline at end of file diff --git a/hints/counting-bits.md b/hints/counting-bits.md new file mode 100644 index 000000000..45188a165 --- /dev/null +++ b/hints/counting-bits.md @@ -0,0 +1,31 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution with O(n) time and O(n) space, where n is the given integer. +

+
+ +
+
+ Hint 1 +

+ A straightforward approach would be to iterate from 0 to n using i, and for each i, iterate through its bits to count the number of set bits. This would result in an O(n \log n) approach. Can you think of a better way? Maybe you should try identifying a pattern by observing the bitwise representation of consecutive numbers. +

+
+ +
+
+ Hint 2 +

+ For example, to compute set bits for 7, add 1 to the count of set bits in 3, which was previously computed by adding 1 to the count of set bits in 1. Observing the pattern, for numbers less than 4, add 1 to the count from two positions before. For numbers less than 8, add 1 to the count from four positions before. Can you derive a dynamic programming relation from this? +

+
+ +
+
+ Hint 3 +

+ We find an offset for the current number based on the number before offset positions. The dynamic programming relation is dp[i] = 1 + dp[i - offset], where dp[i] represents the number of set bits in i. The offset starts at 1 and updates when encountering a power of 2. To simplify the power of 2 check, if offset * 2 equals the current number, we update offset to the current number. +

+
\ No newline at end of file diff --git a/hints/edit-distance.md b/hints/edit-distance.md new file mode 100644 index 000000000..277fa3cbe --- /dev/null +++ b/hints/edit-distance.md @@ -0,0 +1,39 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution as good or better than O(m * n) time and O(m * n) space, where m and n are the lengths of the strings word1 and word2, respectively. +

+
+ +
+
+ Hint 1 +

+ Try to think in terms of recursion and visualize it as a decision tree, as we have choices at each recursion step. Can you determine the recurrence relation and base cases? +

+
+ +
+
+ Hint 2 +

+ We recursively iterate through the strings using indices i and j for word1 and word2, respectively. If the characters at the current indices match, we increment both indices without counting an operation. Otherwise, we have three choices: insert the character at the current index of word1 (increment j), delete the current character of word1 (increment i), or replace the character at index i in word1 (increment both i and j). +

+
+ +
+
+ Hint 3 +

+ If index i goes out of bounds, we return the number of remaining characters in word2 (using insert operations). If index j goes out of bounds, we return the number of remaining characters in word1 (using delete operations). At each step, we return the minimum operation path. This approach is exponential. Can you think of a way to optimize it? +

+
+ +
+
+ Hint 4 +

+ We can use memoization to cache the results and avoid redundant calculations. A hash map or a 2D array can be used to store these results. +

+
\ No newline at end of file diff --git a/hints/gas-station.md b/hints/gas-station.md new file mode 100644 index 000000000..52464072d --- /dev/null +++ b/hints/gas-station.md @@ -0,0 +1,31 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution with O(n) time and O(1) space, where n is the size of the input array. +

+
+ +
+
+ Hint 1 +

+ A brute force approach would be to start at each gas station, simulate the process, and return the index where completing the circuit is possible. This would be an O(n^2) time solution. Can you think of a better way? Maybe a greedy approach works. +

+
+ +
+
+ Hint 2 +

+ We can immediately return -1 if sum(gas) < sum(cost), as completing the circuit is impossible due to insufficient gas. Otherwise, a solution always exists because the total gas is sufficient to cover the total cost, meaning there must be a valid starting point that allows completing the circuit. +

+
+ +
+
+ Hint 3 +

+ We start with a variable total to track the gas balance and initialize the result index to 0. As we iterate through the array with index i, we accumulate the difference (gas[i] - cost[i]). If total becomes negative at any index, we reset it to 0 and update the result index to (i + 1). +

+
\ No newline at end of file diff --git a/hints/hand-of-straights.md b/hints/hand-of-straights.md new file mode 100644 index 000000000..1a35e0287 --- /dev/null +++ b/hints/hand-of-straights.md @@ -0,0 +1,31 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution as good or better than O(nlogn) time and O(n) space, where n is the size of the input array. +

+
+ +
+
+ Hint 1 +

+ It is observed that to form a group, the minimum value should be the starting value of the group. Additionally, the minimum value in the array serves as the starting value for one or more groups based on its frequency. Can you think of an efficient way to determine the frequencies of array elements? Maybe a specific data structure can be useful here. +

+
+ +
+
+ Hint 2 +

+ We can use a hash map to store the elements along with their frequencies. Additionally, we sort the given array. Then, we iterate through the sorted array and try to form groups by decrementing the frequency count. If we fail to form a group at any step, we immediately return false. Can you think why this works? +

+
+ +
+
+ Hint 3 +

+ Sorting ensures we start with the smallest available value, while the hash map helps track element availability using frequency counts. At each step, we pick the smallest available value x and attempt to form a group from x to x + groupSize - 1. If all elements are present based on their frequency counts, we decrement their counts as we iterate. If we successfully form all groups, we return true; otherwise, we return false. +

+
\ No newline at end of file diff --git a/hints/insert-new-interval.md b/hints/insert-new-interval.md new file mode 100644 index 000000000..a807b4c61 --- /dev/null +++ b/hints/insert-new-interval.md @@ -0,0 +1,31 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution with O(n) time and O(1) extra space, where n is the size of the input array. +

+
+ +
+
+ Hint 1 +

+ The given intervals are non-overlapping and sorted in ascending order based on the start value. Try to visualize them as line segments and consider how a new interval can be inserted. Maybe you should analyze different cases of inserting a new interval. +

+
+ +
+
+ Hint 2 +

+ First, we append all intervals to the output list that have an end value smaller than the start value of the new interval. Then, we encounter one of three cases: we have appended all intervals, we reach an interval whose start value is greater than the new interval’s end, or we find an overlapping interval. Can you think of a way to handle these cases efficiently? +

+
+ +
+
+ Hint 3 +

+ We iterate through the remaining intervals, updating the new interval if its end value is greater than or equal to the current interval's start value. We adjust the start and end of the new interval to the minimum and maximum values, respectively. After this, any remaining intervals are appended to the output list, and we return the result. +

+
\ No newline at end of file diff --git a/hints/interleaving-string.md b/hints/interleaving-string.md new file mode 100644 index 000000000..16a8c100a --- /dev/null +++ b/hints/interleaving-string.md @@ -0,0 +1,39 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution as good or better than O(m * n) time and O(m * n) space, where m is the length of the string s1 and n is the length of the string s2. +

+
+ +
+
+ Hint 1 +

+ If the sum of the characters in s1 and s2 does not equal s3, we return false. Think in terms of recursion and visualize it as a decision tree, where we explore different combinations of portions from both strings. Can you determine the possible decisions at each recursion step? +

+
+ +
+
+ Hint 2 +

+ We recursively iterate through the strings using indices i, j, and k for s1, s2, and s3, respectively. At each step, we extend the current path in two directions based on whether the k-th character of s3 matches the current character of s1 or s2. If any path returns true, we immediately return true. If k goes out of bounds, it means we have successfully formed the interleaved string, so we return true. +

+
+ +
+
+ Hint 3 +

+ This approach is exponential. Can you think of a way to optimize it? Since k depends on i and j, it can be treated as a constant, as we can derive k using i + j. +

+
+ +
+
+ Hint 4 +

+ We can use memoization to cache the results of recursive calls and avoid redundant computations. Treating i and j as states, we can use a hash map or a 2D array to store the results. +

+
\ No newline at end of file diff --git a/hints/jump-game-ii.md b/hints/jump-game-ii.md new file mode 100644 index 000000000..4c7c41e30 --- /dev/null +++ b/hints/jump-game-ii.md @@ -0,0 +1,39 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution with O(n) time and O(1) space, where n is the size of the input array. +

+
+ +
+
+ Hint 1 +

+ A brute force approach would be to recursively explore all paths from index 0 to its reachable indices, then process those indices similarly and return the minimum steps to reach the last index. This would be an exponential approach. Can you think of a better way? Maybe a greedy approach works. +

+
+ +
+
+ Hint 2 +

+ We maintain two pointers, l and r, initially set to 0, representing the range of reachable indices. At each step, we iterate through the indices in the range l to r and determine the farthest index that can be reached from the current range. +

+
+ +
+
+ Hint 3 +

+ We then update the pointers l and r to the next range, setting l to r + 1 and r to the farthest index reachable from the current range. We continue this process until the pointers go out of bounds. +

+
+ +
+
+ Hint 4 +

+ The number of steps taken represents the minimum steps required to reach the last index, as it is guaranteed that we can reach it. +

+
\ No newline at end of file diff --git a/hints/jump-game.md b/hints/jump-game.md new file mode 100644 index 000000000..91e7092c9 --- /dev/null +++ b/hints/jump-game.md @@ -0,0 +1,39 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution with O(n) time and O(1) space, where n is the size of input array. +

+
+ +
+
+ Hint 1 +

+ A brute force approach would be to recursively explore all paths from index 0 to its reachable indices, then process those indices similarly, returning true if we reach the last index. This would be an exponential approach. Can you think of a better way? Maybe a greedy approach works. +

+
+ +
+
+ Hint 2 +

+ Instead of processing the array from index 0, start from the last index. Let the target index be goal = n - 1. Iterate in reverse from index n - 2. +

+
+ +
+
+ Hint 3 +

+ At each iteration, we check whether the current index can reach goal. If it can, we update goal to the current index, as reaching the current index means we can also reach the goal. +

+
+ +
+
+ Hint 4 +

+ To determine if we can reach the last index, the goal should be 0 after the iteration. Otherwise, reaching the last index is not possible. +

+
\ No newline at end of file diff --git a/hints/longest-common-subsequence.md b/hints/longest-common-subsequence.md new file mode 100644 index 000000000..d25df25e5 --- /dev/null +++ b/hints/longest-common-subsequence.md @@ -0,0 +1,31 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution as good or better than O(m * n) time and O(m * n) space, where m is the length of the string text1 and n is the length of the string text2. +

+
+ +
+
+ Hint 1 +

+ Try to think in terms of recursion and visualize it as a decision tree. Can you determine the possible decisions at each step? Maybe you should consider iterating through both strings recursively at the same time. +

+
+ +
+
+ Hint 2 +

+ At each recursion step, we have two choices: if the characters at the current indices of both strings match, we move both indices forward and extend the subsequence. Otherwise, we explore two paths by advancing one index at a time and recursively finding the longest subsequence. We return the maximum length between these two choices. This approach is exponential. Can you think of a way to optimize it? +

+
+ +
+
+ Hint 3 +

+ We return 0 if either index goes out of bounds. To optimize, we can use memoization to cache recursive call results and avoid redundant calculations. A hash map or a 2D array can be used to store these results. +

+
\ No newline at end of file diff --git a/hints/longest-increasing-path-in-matrix.md b/hints/longest-increasing-path-in-matrix.md new file mode 100644 index 000000000..8008fe73f --- /dev/null +++ b/hints/longest-increasing-path-in-matrix.md @@ -0,0 +1,23 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution with O(m * n) time and O(m * n) space, where m is the number of rows and n is the number of columns in the given matrix. +

+
+ +
+
+ Hint 1 +

+ If we move from one cell to an adjacent cell with a greater value, we won't revisit a cell, as the path is strictly increasing. A brute force approach would be to run a dfs from every cell and return the longest path found. This approach is exponential. Can you think of a way to optimize it to avoid redundant calculations? +

+
+ +
+
+ Hint 2 +

+ We can use memoization to cache the results of recursive calls and avoid redundant computations. A hash map or a 2D array can be used to store these results. +

+
\ No newline at end of file diff --git a/hints/longest-increasing-subsequence.md b/hints/longest-increasing-subsequence.md new file mode 100644 index 000000000..36a4e3e78 --- /dev/null +++ b/hints/longest-increasing-subsequence.md @@ -0,0 +1,47 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution as good or better than O(n ^ 2) time and O(n ^ 2) space, where n is the size of the input array. +

+
+ +
+
+ Hint 1 +

+ A subsequence is formed by selecting elements while maintaining their order. Using recursion, we can generate all subsequences. The recursive function returns the length of the longest increasing subsequence up to index i, processing from left to right. At each step, we decide whether to include or exclude the current element. +

+
+ +
+
+ Hint 2 +

+ Since the sequence must be increasing, we represent choices by adding 1 when including an element and 0 when excluding it. In recursion, how can we ensure the current element is greater than the previous one? Perhaps additional information is needed to process it. +

+
+ +
+
+ Hint 3 +

+ We can store the index of the previously chosen element as j, making it easier to process the current element at index i. If and only if j == -1 or nums[i] > nums[j], we include the current element and extend the recursive path. Can you determine the recurrence relation? At most, two recursive calls are made at each recursion step. +

+
+ +
+
+ Hint 4 +

+ We stop the recursion when index i goes out of bounds and return 0 since no more elements can be added. The initial recursion call starts with j = -1. At each step, we include the current element if it is greater than the previous one and continue the recursion, or we exclude it and explore the next possibility. We return the maximum value obtained from both paths. +

+
+ +
+
+ Hint 5 +

+ The time complexity of this approach is exponential. We can use memoization to store results of recursive calls and avoid recalculations. A hash map or a 2D array can be used to cache these results. +

+
\ No newline at end of file diff --git a/hints/maximum-product-subarray.md b/hints/maximum-product-subarray.md new file mode 100644 index 000000000..d46997d32 --- /dev/null +++ b/hints/maximum-product-subarray.md @@ -0,0 +1,31 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution with O(n) time and O(1) space, where n is the size of the input array. +

+
+ +
+
+ Hint 1 +

+ A brute force solution would be to find the product for every subarray and then return the maximum among all the products. This would be an O(n ^ 2) approach. Can you think of a better way? Maybe you should think of a dynamic programming approach. +

+
+ +
+
+ Hint 2 +

+ Try to identify a pattern by finding the maximum product for an array with two elements and determining what values are needed when increasing the array size to three. Perhaps you only need two values when introducing a new element. +

+
+ +
+
+ Hint 3 +

+ We maintain both the minimum and maximum product values and update them when introducing a new element by considering three cases: starting a new subarray, multiplying with the previous max product, or multiplying with the previous min product. The max product is updated to the maximum of these three, while the min product is updated to the minimum. We also track a global max product for the result. This approach is known as Kadane's algorithm. +

+
\ No newline at end of file diff --git a/hints/maximum-subarray.md b/hints/maximum-subarray.md new file mode 100644 index 000000000..f8071b628 --- /dev/null +++ b/hints/maximum-subarray.md @@ -0,0 +1,39 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution with O(n) time and O(1) space, where n is the size of the input array. +

+
+ +
+
+ Hint 1 +

+ A brute force approach would be to compute the sum of every subarray and return the maximum among them. This would be an O(n^2) approach. Can you think of a better way? Maybe you should consider a dynamic programming-based approach. +

+
+ +
+
+ Hint 2 +

+ Instead of calculating the sum for every subarray, try maintaining a running sum. Maybe you should consider whether extending the previous sum or starting fresh with the current element gives a better result. Can you think of a way to track this efficiently? +

+
+ +
+
+ Hint 3 +

+ We use a variable curSum to track the sum of the elements. At each index, we have two choices: either add the current element to curSum or start a new subarray by resetting curSum to the current element. Maybe you should track the maximum sum at each step and update the global maximum accordingly. +

+
+ +
+
+ Hint 4 +

+ This algorithm is known as Kadane's algorithm. +

+
\ No newline at end of file diff --git a/hints/meeting-schedule-ii.md b/hints/meeting-schedule-ii.md new file mode 100644 index 000000000..1342e260f --- /dev/null +++ b/hints/meeting-schedule-ii.md @@ -0,0 +1,39 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution with O(nlogn) time and O(n) space, where n is the size of the input array. +

+
+ +
+
+ Hint 1 +

+ Try to visualize the meetings as line segments on a number line representing start and end times. The number of rooms required is the maximum number of overlapping meetings at any point on the number line. Can you think of a way to determine this efficiently? +

+
+ +
+
+ Hint 2 +

+ We create two arrays, start and end, containing the start and end times of all meetings, respectively. After sorting both arrays, we use a two-pointer based approach. How do you implement this? +

+
+ +
+
+ Hint 3 +

+ We use two pointers, s and e, for the start and end arrays, respectively. We also maintain a variable count to track the current number of active meetings. At each iteration, we increment s while the start time is less than the current end time and increase count, as these meetings must begin before the earliest ongoing meeting ends. +

+
+ +
+
+ Hint 4 +

+ Then, we increment e and decrement count as a meeting has ended. At each step, we update the result with the maximum value of active meetings stored in count. +

+
\ No newline at end of file diff --git a/hints/meeting-schedule.md b/hints/meeting-schedule.md new file mode 100644 index 000000000..0216b7b66 --- /dev/null +++ b/hints/meeting-schedule.md @@ -0,0 +1,31 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution with O(nlogn) time and O(n) space, where n is the size of the input array. +

+
+ +
+
+ Hint 1 +

+ If two intervals are sorted in ascending order by their start values, they overlap if the start value of the second interval is less than the end value of the first interval. And these are called overlapping intervals. +

+
+ +
+
+ Hint 2 +

+ A brute force approach would be to check every pair of intervals and return false if any overlap is found. This would be an O(n^2) solution. Can you think of a better way? Maybe you should visualize the given intervals as line segments. +

+
+ +
+
+ Hint 3 +

+ We should sort the given intervals based on their start values, as this makes it easier to check for overlaps by comparing adjacent intervals. We then iterate through the intervals from left to right and return false if any adjacent intervals overlap. +

+
\ No newline at end of file diff --git a/hints/merge-intervals.md b/hints/merge-intervals.md new file mode 100644 index 000000000..89af36fe5 --- /dev/null +++ b/hints/merge-intervals.md @@ -0,0 +1,39 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution as good or better than O(nlogn) time and O(n) space, where n is the size of the input array. +

+
+ +
+
+ Hint 1 +

+ Sorting the given intervals in ascending order based on their start values is beneficial, as it helps in identifying overlapping intervals efficiently. How can you determine if two intervals overlap? +

+
+ +
+
+ Hint 2 +

+ If two intervals are sorted in ascending order by their start values, they overlap if the start value of the second interval is less than or equal to the end value of the first interval. +

+
+ +
+
+ Hint 3 +

+ We iterate through the sorted intervals from left to right, starting with the first interval in the output list. From the second interval onward, we compare each interval with the last appended interval. Can you determine the possible cases for this comparison? +

+
+ +
+
+ Hint 4 +

+ The two cases are: if the current interval overlaps with the last appended interval, we update its end value to the maximum of both intervals' end values and continue. Otherwise, we append the current interval and proceed. +

+
\ No newline at end of file diff --git a/hints/merge-triplets-to-form-target.md b/hints/merge-triplets-to-form-target.md new file mode 100644 index 000000000..fd475aa48 --- /dev/null +++ b/hints/merge-triplets-to-form-target.md @@ -0,0 +1,31 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution with O(n) time and O(1) space, where n is the size of the input array. +

+
+ +
+
+ Hint 1 +

+ An important observation is that we can ignore triplets with values greater than the target triplet. +

+
+ +
+
+ Hint 2 +

+ Specifically, if a triplet t has any element greater than the corresponding value in target (i.e., t[0] > target[0], t[1] > target[1], or t[2] > target[2]), we can discard it. This is because using such a triplet in operations would exceed the target values, making it invalid. +

+
+ +
+
+ Hint 3 +

+ Now, from the remaining valid triplets, we only need to check whether the target triplet values exist. Since all values in the valid triplets are less than or equal to the corresponding values in the target triplet, finding the target triplet among them guarantees that we can achieve it. +

+
\ No newline at end of file diff --git a/hints/minimum-interval-including-query.md b/hints/minimum-interval-including-query.md new file mode 100644 index 000000000..9b56faba2 --- /dev/null +++ b/hints/minimum-interval-including-query.md @@ -0,0 +1,31 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution as good or better than O(nlogn + mlogm) time and O(n + m) space, where m is the size of the array queries and n is the size of the array intervals. +

+
+ +
+
+ Hint 1 +

+ A brute force approach would be to iterate through each query and, for each query, check all intervals to find the result. This would be an O(m * n) solution. Can you think of a better way? Maybe processing the queries in sorted order could help. +

+
+ +
+
+ Hint 2 +

+ We sort the intervals by start value and process the queries in ascending order. Using a pointer i, we add intervals to a min-heap while their start values are less than or equal to the query, storing their end values and sizes. +

+
+ +
+
+ Hint 3 +

+ The min-heap is ordered by interval size. We remove elements from the heap while the top element’s end value is less than the current query. The result for the query is the top element’s size if the heap is non-empty; otherwise, it is -1. +

+
\ No newline at end of file diff --git a/hints/missing-number.md b/hints/missing-number.md new file mode 100644 index 000000000..2d5f6908b --- /dev/null +++ b/hints/missing-number.md @@ -0,0 +1,39 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution with O(n) time and O(1) space, where n is the size of the input array. +

+
+ +
+
+ Hint 1 +

+ A brute force approach would iterate through the range of numbers from 0 to n, checking if each number is present in the given array. If a number is missing, it is returned. This results in an O(n^2) solution. Can you think of a better way? Maybe a data structure could help optimize this process. +

+
+ +
+
+ Hint 2 +

+ We can use a hash set by inserting the given array elements into it. Then, we iterate through the range of numbers from 0 to n and use the hash set for O(1) lookups to find the missing number. Can you think of a way to further optimize this? Maybe a bitwise operator could be useful. +

+
+ +
+
+ Hint 3 +

+ We can use bitwise XOR. When two identical numbers are XORed, the result is 0. Using this property, we can efficiently find the missing number. +

+
+ +
+
+ Hint 4 +

+ We first compute the bitwise XOR of numbers from 0 to n. Then, we iterate through the array and XOR its elements as well. The missing number remains in the final XOR result since all other numbers appear twice—once in the range and once in the array—while the missing number is XORed only once. +

+
\ No newline at end of file diff --git a/hints/multiply-strings.md b/hints/multiply-strings.md new file mode 100644 index 000000000..193cc4c27 --- /dev/null +++ b/hints/multiply-strings.md @@ -0,0 +1,39 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution with O(m*n) time and O(m+n) space, where m is the length of the string num1 and n is the length of the string num2. +

+
+ +
+
+ Hint 1 +

+ Implement a helper function that takes two number strings and returns their sum. Ensure that the length of num1 is greater than num2, swapping them if necessary. Can you think of a way to multiply the strings? Maybe you should first consider basic multiplication, where num1 is multiplied by each digit of num2. +

+
+ +
+
+ Hint 2 +

+ When multiplying num1 with each digit of num2, we iterate through num2 in reverse order. Based on the digit's position, we pad zeros to the multiplication result accordingly—no padding for the last digit, one zero for the second last, and so on. What should be the next step after each multiplication? Maybe you should implement a helper function to handle this. +

+
+ +
+
+ Hint 3 +

+ We implement a helper function that takes num1, a digit, and a variable zeroes, returning the multiplication result with zeroes padded at the end. A global string res stores the final result. +

+
+ +
+
+ Hint 4 +

+ In the main function, we iterate through num2 in reverse order, calling the helper function to multiply num1 with the current digit and append the appropriate number of padding zeros. We then call another helper function that takes this multiplication result and the global result string res, adds them, and updates res. +

+
\ No newline at end of file diff --git a/hints/non-cyclical-number.md b/hints/non-cyclical-number.md new file mode 100644 index 000000000..6672425b9 --- /dev/null +++ b/hints/non-cyclical-number.md @@ -0,0 +1,23 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution as good or better than O(logn) time and O(logn) space, where n is the given integer. +

+
+ +
+
+ Hint 1 +

+ Create a helper function that returns the sum of the squares of a number's digits. Then, simulate the given process. If we reach 1, return true. However, we may get stuck in a cycle if a number is processed more than once. What data structure can be used to detect if a number has already been processed? +

+
+ +
+
+ Hint 2 +

+ We can use a hash set to detect if a number has already been processed. At each step, we update n with the return value of the helper function. If the result is 1, we return true. If n is already in the set, we return false. Otherwise, we add n to the hash set and continue. +

+
\ No newline at end of file diff --git a/hints/non-overlapping-intervals.md b/hints/non-overlapping-intervals.md new file mode 100644 index 000000000..1e07a9c0b --- /dev/null +++ b/hints/non-overlapping-intervals.md @@ -0,0 +1,39 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution with O(nlogn) time and O(n) space, where n is the size of the input array. +

+
+ +
+
+ Hint 1 +

+ If two intervals are sorted in ascending order by their start values, they overlap if the start value of the second interval is less than the end value of the first interval. And these are called overlapping intervals. +

+
+ +
+
+ Hint 2 +

+ A brute force approach would be to sort the given intervals in ascending order based on their start values and recursively explore all possibilities. This would be an exponential approach. Can you think of a better way? Maybe a greedy approach works here. +

+
+ +
+
+ Hint 3 +

+ We first sort the given intervals based on their start values to efficiently check for overlaps by comparing adjacent intervals. We then iterate through the sorted intervals from left to right, keeping track of the previous interval’s end value as prevEnd, initially set to the end value of the first interval. +

+
+ +
+
+ Hint 4 +

+ We then iterate from the second interval. If the current interval doesn't overlap, we update prevEnd to the current interval's end and continue. Otherwise, we set prevEnd to the minimum of prevEnd and the current interval’s end, greedily removing the interval that ends last to retain as many intervals as possible. +

+
\ No newline at end of file diff --git a/hints/number-of-one-bits.md b/hints/number-of-one-bits.md new file mode 100644 index 000000000..788499e2b --- /dev/null +++ b/hints/number-of-one-bits.md @@ -0,0 +1,31 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution with O(1) time and O(1) space. +

+
+ +
+
+ Hint 1 +

+ The given integer is a 32-bit integer. Can you think of using bitwise operators to iterate through its bits? Maybe you should consider iterating 32 times. +

+
+ +
+
+ Hint 2 +

+ We iterate 32 times (0 to 31) using index i. The expression (1 << i) creates a bitmask with a set bit at the i-th position. How can you check whether the i-th bit is set in the given number? Maybe you should consider using the bitwise-AND ("&"). +

+
+ +
+
+ Hint 3 +

+ Since the mask has a set bit at the i-th position and all 0s elsewhere, we can perform a bitwise-AND with n. If n has a set bit at the i-th position, the result is positive; otherwise, it is 0. We increment the global count if the result is positive and return it after the iteration. +

+
\ No newline at end of file diff --git a/hints/partition-equal-subset-sum.md b/hints/partition-equal-subset-sum.md new file mode 100644 index 000000000..6dfc98881 --- /dev/null +++ b/hints/partition-equal-subset-sum.md @@ -0,0 +1,39 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution as good or better than O(n * t) time and O(n * t) space, where n is the size of the input array and t is half the sum of the array elements. +

+
+ +
+
+ Hint 1 +

+ If the sum of the array elements is not even, we can immediately return false. Think in terms of recursion, where we try to build a subset with a sum equal to half of the total sum. If we find such a subset, the remaining elements will automatically form another subset with the same sum. The entire array can also be considered as one subset, with the other being empty. Can you visualize this as a decision tree to process the array recursively? +

+
+ +
+
+ Hint 2 +

+ We recursively iterate through the array with index i. At each step, we decide whether to include the current element in the subset or not. Instead of forming the subset explicitly, can you think of a better approach? Maybe you only need to track the subset sum rather than generating the subset itself. +

+
+ +
+
+ Hint 3 +

+ We can track the subset sum using a variable curSum. At each step, we make two recursive calls. If adding the current element does not exceed the target, we include it. If either path leads to a solution, we immediately return true. Can you determine the base case for this recursion? All elements in the array are positive. +

+
+ +
+
+ Hint 4 +

+ If curSum equals half the sum of the array elements, we return true. If index i goes out of bounds, we return false. This solution is exponential, but we can use memoization to cache recursive call results and avoid redundant computations. We can use a hash map or a 2D array with dimensions n * t, where n is the size of the input array and t is half the sum of the input array elements. +

+
\ No newline at end of file diff --git a/hints/partition-labels.md b/hints/partition-labels.md new file mode 100644 index 000000000..c5e3551bb --- /dev/null +++ b/hints/partition-labels.md @@ -0,0 +1,31 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution with O(n) time and O(m) space, where n is the length of the string s and m is the number of unique characters in the string s. +

+
+ +
+
+ Hint 1 +

+ A character has a first and last index in the given string. Can you think of a greedy approach to solve this problem? Maybe you should try iterating over one of these two indices. +

+
+ +
+
+ Hint 2 +

+ We store the last index of each character in a hash map or an array. As we iterate through the string, treating each index as a potential start of a partition, we track the end of the partition using the maximum last index of the characters seen so far in the current partition. Additionally, we maintain the size of the current partition using a variable, say size. +

+
+ +
+
+ Hint 3 +

+ We update the end of the current partition based on the maximum last index of the characters, extending the partition as needed. When the current index reaches the partition’s end, we finalize the partition, append its size to the output list, reset the size to 0, and continue the same process for the remaining string. +

+
\ No newline at end of file diff --git a/hints/plus-one.md b/hints/plus-one.md new file mode 100644 index 000000000..ec62b78c9 --- /dev/null +++ b/hints/plus-one.md @@ -0,0 +1,23 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution with O(n) time and O(n) space, where n is the size of the input array. +

+
+ +
+
+ Hint 1 +

+ There are two cases when adding 1 to a number. If the rightmost digit is less than 9, we simply increment it. Otherwise, we set it to 0 and apply the same process to the preceding digit. +

+
+ +
+
+ Hint 2 +

+ We iterate through the given digits from right to left using index i. If the current digit is less than 9, we increment it and return the array. Otherwise, we set the digit to 0 and continue. If the loop completes without returning, we insert 1 at the beginning of the array and return it. +

+
\ No newline at end of file diff --git a/hints/pow-x-n.md b/hints/pow-x-n.md new file mode 100644 index 000000000..de42350f0 --- /dev/null +++ b/hints/pow-x-n.md @@ -0,0 +1,39 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution as good or better than O(logn) time and O(logn) space, where n is the given integer. +

+
+ +
+
+ Hint 1 +

+ A brute force approach would be to iterate linearly up to n, multiplying by x each time to find (x^n). If n is negative, return (1 / (x^n)); otherwise, return (x^n). Can you think of a better way? Maybe a recursive approach would be more efficient. +

+
+ +
+
+ Hint 2 +

+ For example, to calculate 2^6, instead of multiplying 2 six times, we compute 2^3 and square the result. The same logic applies recursively to find 2^3 and further break down the multiplication. What should be the base case for this recursion? Maybe you should consider the term that cannot be further broken down. +

+
+ +
+
+ Hint 3 +

+ In (x^n), if x is 0, we return 0. If n is 0, we return 1, as any number raised to the power of 0 is 1. Otherwise, we compute (x^(n/2)) recursively and square the result. If n is odd, we multiply the final result by x. What should be the logic if n is negative? +

+
+ +
+
+ Hint 4 +

+ We start the recursion with the absolute value of n. After computing the result as res, we return res if n is non-negative; otherwise, we return (1 / res). +

+
\ No newline at end of file diff --git a/hints/regular-expression-matching.md b/hints/regular-expression-matching.md new file mode 100644 index 000000000..e17c78964 --- /dev/null +++ b/hints/regular-expression-matching.md @@ -0,0 +1,39 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution as good or better than O(m * n) time and O(m * n) space, where m is the length of the string s and n is the length of the string p. +

+
+ +
+
+ Hint 1 +

+ Try to think in terms of recursion and visualize it as a decision tree, where we explore different combinations to match the strings when encountering *. Multiple decisions are made at each step to find a valid matching path. Can you determine the possible decisions at each recursion step? +

+
+ +
+
+ Hint 2 +

+ We recursively iterate through the strings using indices i and j for s and p, respectively. If the characters match or p[j] is '.', we increment both i and j to process the remaining strings. When the next character of string p is '*', we have two choices: skip it (treating it as zero occurrences) or match one or more characters (if s[i] matches p[j]), incrementing i accordingly. +

+
+ +
+
+ Hint 3 +

+ If both indices go out of bounds, we return true; otherwise, we return false. If any recursive path returns true, we immediately return true. This approach is exponential. Can you think of a way to optimize it? +

+
+ +
+
+ Hint 4 +

+ We can use memoization to cache the results of recursive calls and avoid redundant calculations. A hash map or a 2D array can be used to store these results. +

+
\ No newline at end of file diff --git a/hints/reverse-bits.md b/hints/reverse-bits.md new file mode 100644 index 000000000..0eb364ce9 --- /dev/null +++ b/hints/reverse-bits.md @@ -0,0 +1,31 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution with O(1) time and O(1) space. +

+
+ +
+
+ Hint 1 +

+ Given a 32-bit integer, what is the position of bit i after reversing the bits? Maybe you should observe the bit positions before and after reversal to find a pattern. +

+
+ +
+
+ Hint 2 +

+ After reversing the bits, the bit at position i moves to position 31 - i. Can you use this observation to construct the reversed number efficiently? +

+
+ +
+
+ Hint 3 +

+ We initialize res to 0 and iterate through the bits of the given integer n. We extract the bit at the i-th position using ((n >> i) & 1). If it is 1, we set the corresponding bit in res at position (31 - i) using (res |= (1 << (31 - i))). +

+
\ No newline at end of file diff --git a/hints/reverse-integer.md b/hints/reverse-integer.md new file mode 100644 index 000000000..23b4d9c75 --- /dev/null +++ b/hints/reverse-integer.md @@ -0,0 +1,31 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution with O(1) time and O(1) space. +

+
+ +
+
+ Hint 1 +

+ A straightforward approach would be to convert the given integer to a string, reverse it, convert it back to an integer using a long type, and return 0 if the result exceeds the integer range. Can you think of a better way? +

+
+ +
+
+ Hint 2 +

+ We initially declare the result res as an int with a value of 0. We iterate through the given integer, extracting digits one by one. Before appending a digit to res, we consider multiple cases. Can you determine them? Maybe you should think about overflow. +

+
+ +
+
+ Hint 3 +

+ Let MAX be the maximum positive integer and MIN be the minimum negative integer. We iterate through each digit and check for overflow before updating res. If res > MAX / 10 or res < MIN / 10, return 0. If res == MAX / 10 and the current digit is greater than MAX % 10, return 0. If res == MIN / 10 and the current digit is less than MIN % 10, return 0. Otherwise, append the digit to res and continue. +

+
\ No newline at end of file diff --git a/hints/rotate-matrix.md b/hints/rotate-matrix.md new file mode 100644 index 000000000..3f5528d0f --- /dev/null +++ b/hints/rotate-matrix.md @@ -0,0 +1,31 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution with O(n^2) time and O(1) space, where n is the length of the side of the given square matrix. +

+
+ +
+
+ Hint 1 +

+ A brute force approach would use O(n^2) extra space to solve the problem. Can you think of a way to avoid using extra space? Maybe you should consider observing the positions of the elements before rotating and after rotating of the matrix. +

+
+ +
+
+ Hint 2 +

+ We can rotate the matrix in two steps. First, we reverse the matrix vertically, meaning the first row becomes the last, the second row becomes the second last, and so on. Next, we transpose the reversed matrix, meaning rows become columns and columns become rows. How would you transpose the matrix? +

+
+ +
+
+ Hint 3 +

+ Since the given matrix is a square matrix, we only need to iterate over the upper triangular part, meaning the right upper portion of the main diagonal. In this way, we can transpose a matrix. +

+
\ No newline at end of file diff --git a/hints/set-zeroes-in-matrix.md b/hints/set-zeroes-in-matrix.md new file mode 100644 index 000000000..663ff919e --- /dev/null +++ b/hints/set-zeroes-in-matrix.md @@ -0,0 +1,39 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution with O(m*n) time and O(1) space, where m is the number of rows and n is the number of columns in the given matrix. +

+
+ +
+
+ Hint 1 +

+ A brute force approach would iterate through the given matrix and update the corresponding row and column in a new matrix on the fly. This would result in an O((m*n)*(m+n)) time and O(m*n) space solution. Can you think of a better way? Maybe you should consider using a single variable for a row and a single variable for a column instead of updating entire row and column. +

+
+ +
+
+ Hint 2 +

+ A better approach is to use O(m+n) boolean arrays. We iterate through the matrix, and when encountering a zero, we mark the respective row and column as true. In the second iteration, we set a cell to 0 if its corresponding row or column is marked true. Can you think of a way to optimize the space further? +

+
+ +
+
+ Hint 3 +

+ We can use the topmost row and leftmost column of the matrix as boolean arrays by marking 0 instead of true. However, since they overlap at one cell, we use a single variable to track the top row separately. We then iterate through the matrix and mark zeros accordingly. +

+
+ +
+
+ Hint 4 +

+ In the second iteration, we update all cells that are not part of the top row or left column accordingly. After making the necessary changes, we check the top-leftmost cell and update the corresponding column. Finally, we check the extra variable and update the top row accordingly. +

+
\ No newline at end of file diff --git a/hints/single-number.md b/hints/single-number.md new file mode 100644 index 000000000..6aab0485f --- /dev/null +++ b/hints/single-number.md @@ -0,0 +1,39 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution with O(n) time and O(1) space, where n is the size of the input array. +

+
+ +
+
+ Hint 1 +

+ A brute force approach would iterate through the array, checking each element using a nested loop. If a duplicate is found, we continue to the next element; otherwise, the current element is the required number. This results in an O(n^2) solution. Can you think of a better way? Maybe a data structure can help detect duplicates efficiently. +

+
+ +
+
+ Hint 2 +

+ We use a hash set, iterating through the array and adding elements that are not in the set while removing those that are already present. After the iteration, the required number remains in the hash set. This results in an O(n) space solution. Can you further optimize it? Maybe a bitwise operator could be useful here. +

+
+ +
+
+ Hint 3 +

+ Think about the bitwise XOR ("^"). What is the result when two identical numbers are XORed? +

+
+ +
+
+ Hint 4 +

+ When two identical numbers are XORed, they cancel out, resulting in zero. Since every number appears twice except for one, the XOR of the entire array gives the number that appears only once. +

+
\ No newline at end of file diff --git a/hints/spiral-matrix.md b/hints/spiral-matrix.md new file mode 100644 index 000000000..2b8eced0a --- /dev/null +++ b/hints/spiral-matrix.md @@ -0,0 +1,31 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution with O(m*n) time and O(1) extra space, where m is the number of rows and n is the number of columns in the given matrix. +

+
+ +
+
+ Hint 1 +

+ Try to simulate the process as described in the problem. Think in terms of matrix layers, starting from the outermost boundaries and moving inward. Can you determine an efficient way to implement this? +

+
+ +
+
+ Hint 2 +

+ Each boundary consists of four parts: the top row, right column, bottom row, and left column, which follow the spiral order and act as four pointers. For each layer, the top pointer increments by one, the right pointer decrements by one, the left pointer increments by one, and the bottom pointer decrements by one. +

+
+ +
+
+ Hint 3 +

+ At each layer, four loops traverse the matrix: one moves left to right along the top row, another moves top to bottom along the right column, the next moves right to left along the bottom row, and the last moves bottom to top along the left column. This process generates the spiral order. +

+
\ No newline at end of file diff --git a/hints/string-encode-and-decode.md b/hints/string-encode-and-decode.md index 39083d5be..82bc8fbcb 100644 --- a/hints/string-encode-and-decode.md +++ b/hints/string-encode-and-decode.md @@ -2,7 +2,7 @@
Recommended Time & Space Complexity

- You should aim for a solution with O(m) time and O(1) space for each encode() and decode() call, where m is the sum of lengths of all the strings. + You should aim for a solution with O(m) time for each encode() and decode() call and O(m+n) space, where m is the sum of lengths of all the strings and n is the number of strings.

diff --git a/hints/sum-of-two-integers.md b/hints/sum-of-two-integers.md new file mode 100644 index 000000000..340f197d0 --- /dev/null +++ b/hints/sum-of-two-integers.md @@ -0,0 +1,39 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution with O(1) time and O(1) space. +

+
+ +
+
+ Hint 1 +

+ A brute force approach would use the addition operator. Can you think of a way to perform addition without using it? Maybe you should consider solving this using bit manipulation. +

+
+ +
+
+ Hint 2 +

+ We can use the bitwise XOR operator to perform addition. If both a and b have 1 at the same bit position, the sum at that position is 0, and a carry of 1 is generated. If the bits are different, the sum at that position is 1. Additionally, we account for the carry from the previous step in the next iteration. +

+
+ +
+
+ Hint 3 +

+ We iterate bit by bit from 0 to 31 since the given integers are 32-bit. We track the carry, initially set to 0, and initialize the result as res. During iteration, the XOR of the bits at the i-th position of both integers and the carry determines the current bit of res. How can you handle negative numbers? +

+
+ +
+
+ Hint 4 +

+ To handle negative numbers, if the final result exceeds the maximum positive 32-bit integer, it means the number should be negative. We adjust it using bitwise operations: flipping the bits with res ^ ((2 ^ 32) - 1) and applying ~ to restore the correct two’s complement representation. This ensures the result correctly represents signed 32-bit integers. +

+
\ No newline at end of file diff --git a/hints/target-sum.md b/hints/target-sum.md new file mode 100644 index 000000000..06e9e9a3e --- /dev/null +++ b/hints/target-sum.md @@ -0,0 +1,31 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution as good or better than O(n * m) time and O(n * m) space, where n is the size of the input array and m is the sum of all the elements in the array. +

+
+ +
+
+ Hint 1 +

+ Try to think in terms of recursion and visualize it as a decision tree, where we have two choices at each recursion step: assigning a positive or negative sign. +

+
+ +
+
+ Hint 2 +

+ We recursively iterate through the array using index i, tracking the current sum along the recursive path. Each step branches into two paths, and we sum the number of ways to reach the target. If the index i goes out of bounds, we return 1 if the current sum equals the target; otherwise, we return 0. +

+
+ +
+
+ Hint 3 +

+ This approach is exponential. We can use memoization to cache recursive call results and avoid redundant calculations. A hash map or a 2D array with modifications can be used for caching. If using a 2D array, the dimensions can be (n * (2m + 1)), where n is the array size and m represents the sum of the array elements. +

+
\ No newline at end of file diff --git a/hints/valid-parenthesis-string.md b/hints/valid-parenthesis-string.md new file mode 100644 index 000000000..29baed7d5 --- /dev/null +++ b/hints/valid-parenthesis-string.md @@ -0,0 +1,39 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution as good or better than O(n) time and O(n) space, where n is the length of the input string. +

+
+ +
+
+ Hint 1 +

+ A brute force approach would try all possibilities when encountering a '*' and recursively solve the problem, leading to an exponential approach. Can you think of a better way? Maybe a data structure commonly used in parenthesis problems could be useful. +

+
+ +
+
+ Hint 2 +

+ We can solve the problem using a stack-based approach. We maintain two stacks: one for tracking the indices of left parentheses and another for star indices. As we iterate through the string from left to right, we push indices onto their respective stacks when encountering a left parenthesis '(' or a star '*'. Can you determine the logic for the right parentesis case? +

+
+ +
+
+ Hint 3 +

+ If the left parenthesis stack is not empty, we pop from it. Otherwise, we pop from the star stack, treating the star as a left parenthesis to keep the string valid. After iterating the string, the stacks might be non-empty? Can you determine the logic for this case? +

+
+ +
+
+ Hint 4 +

+ Now, we try to match the remaining left parentheses with stars, ensuring the stars appear after the left parentheses in the string. We simultaneously pop from both stacks, and if the index of a left parenthesis is greater than that of a star, the string is invalid as there is no matching right parenthesis. In this case, we return false. +

+
\ No newline at end of file diff --git a/hints/word-break.md b/hints/word-break.md new file mode 100644 index 000000000..cb850a49c --- /dev/null +++ b/hints/word-break.md @@ -0,0 +1,31 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution as good or better than O(n * m * t) time and O(n) space, where n is the length of the string s, m is the number of words in wordDict, and t is the maximum length of any word in wordDict. +

+
+ +
+
+ Hint 1 +

+ Try to think of this problem in terms of recursion, where we explore all possibilities. We iterate through the given string s, attempting to pick a word from wordDict that matches a portion of s, and then recursively continue processing the remaining string. Can you determine the recurrence relation and base condition? +

+
+ +
+
+ Hint 2 +

+ The base condition is to return true if we reach the end of the string s. At each recursive call with index i iterating through s, we check all words in wordDict and recursively process the remaining string by incrementing i by the length of the matched word. If any recursive path returns true, we immediately return true. However, this solution is exponential. Can you think of an optimization? Maybe you should consider an approach that avoids repeated work. +

+
+ +
+
+ Hint 3 +

+ We can avoid recalculating results for recursive calls by using memoization. Since we iterate with index i, we can use a hash map or an array of the same length as s to cache the results of recursive calls and prevent redundant computations. +

+
\ No newline at end of file From 0a2d881f8e72fa645203cddf31c3ea7f43a35812 Mon Sep 17 00:00:00 2001 From: Sri Hari <94112314+Srihari2222@users.noreply.github.com> Date: Mon, 24 Mar 2025 02:44:23 +0530 Subject: [PATCH 38/45] Sri Hari: Batch-6/Neetcode-150/Added-SwiftCode (#3936) * Batch-6/Neetcode-150/Added-swiftcode * Batch-6/Neetcode-150/Added-swiftcode * Batch-6/Neetcode-150/Added-swiftcode * Batch-6/Neetcode-150/Added-swiftcode * Batch-6/Neetcode-150/Added-swiftcode * Batch-6/Neetcode-150/Added-swiftcode * Batch-6/Neetcode-150/Added-swiftcode * Batch-6/Neetcode-150/Added-swiftcode * Batch-6/Neetcode-150/Added-swiftcode * Batch-6/Neetcode-150/Added-swiftcode * Batch-6/Neetcode-150/Added-swiftcode * Batch-6/Neetcode-150/Added-swiftcode * Batch-6/Neetcode-150/Added-swiftcode --- articles/add-two-numbers.md | 74 +- articles/anagram-groups.md | 39 +- articles/balanced-binary-tree.md | 124 +++- articles/binary-search.md | 90 +++ articles/binary-tree-diameter.md | 112 +++ ...ree-from-preorder-and-inorder-traversal.md | 130 ++++ articles/binary-tree-maximum-path-sum.md | 79 +++ articles/binary-tree-right-side-view.md | 78 +++ articles/burst-balloons.md | 75 ++ articles/buy-and-sell-crypto-with-cooldown.md | 100 +++ articles/buy-and-sell-crypto.md | 51 ++ articles/car-fleet.md | 44 ++ articles/cheapest-flight-path.md | 107 +++ articles/climbing-stairs.md | 120 ++++ articles/clone-graph.md | 84 +++ articles/coin-change-ii.md | 157 ++++- articles/coin-change.md | 111 +++ articles/combination-target-sum-ii.md | 136 ++++ articles/combination-target-sum.md | 56 ++ articles/combinations-of-a-phone-number.md | 67 +- .../copy-linked-list-with-random-pointer.md | 231 ++++++- articles/count-connected-components.md | 124 +++- articles/count-good-nodes-in-binary-tree.md | 78 +++ articles/count-number-of-islands.md | 149 +++- articles/count-paths.md | 117 ++++ articles/count-squares.md | 73 +- articles/count-subsequences.md | 161 ++++- articles/counting-bits.md | 75 ++ articles/course-schedule-ii.md | 121 ++++ articles/course-schedule.md | 82 +++ articles/daily-temperatures.md | 75 +- articles/decode-ways.md | 105 +++ articles/depth-of-binary-tree.md | 100 +++ articles/design-twitter-feed.md | 229 ++++++ articles/design-word-search-data-structure.md | 94 +++ articles/duplicate-integer.md | 54 +- articles/eating-bananas.md | 47 ++ articles/edit-distance.md | 141 ++++ articles/evaluate-reverse-polish-notation.md | 147 ++++ articles/find-duplicate-integer.md | 137 ++++ articles/find-median-in-a-data-stream.md | 65 ++ .../find-minimum-in-rotated-sorted-array.md | 51 ++ .../find-target-in-rotated-sorted-array.md | 124 ++++ articles/foreign-dictionary.md | 122 ++++ articles/gas-station.md | 93 +++ articles/generate-parentheses.md | 85 +++ articles/hand-of-straights.md | 147 ++++ articles/house-robber-ii.md | 110 +++ articles/house-robber.md | 75 ++ articles/implement-prefix-tree.md | 109 +++ articles/insert-new-interval.md | 91 +++ articles/interleaving-string.md | 157 +++++ articles/invert-a-binary-tree.md | 212 +++++- articles/is-anagram.md | 63 +- articles/is-palindrome.md | 44 ++ articles/islands-and-treasure.md | 132 ++++ articles/jump-game-ii.md | 103 ++- articles/jump-game.md | 93 +++ articles/k-closest-points-to-origin.md | 121 +++- articles/kth-largest-element-in-an-array.md | 122 ++++ articles/kth-largest-integer-in-a-stream.md | 48 +- articles/kth-smallest-integer-in-bst.md | 197 ++++++ articles/largest-rectangle-in-histogram.md | 198 +++++- articles/last-stone-weight.md | 108 +++ .../level-order-traversal-of-binary-tree.md | 84 ++- articles/linked-list-cycle-detection.md | 61 ++ articles/longest-common-subsequence.md | 134 ++++ articles/longest-consecutive-sequence.md | 103 ++- articles/longest-increasing-path-in-matrix.md | 114 +++ articles/longest-increasing-subsequence.md | 651 +++++++++++++++++- articles/longest-palindromic-substring.md | 138 +++- ...st-repeating-substring-with-replacement.md | 78 +++ .../longest-substring-without-duplicates.md | 60 ++ ...t-common-ancestor-in-binary-search-tree.md | 67 ++ articles/lru-cache.md | 149 ++++ articles/max-area-of-island.md | 148 +++- articles/max-water-container.md | 34 + articles/maximum-product-subarray.md | 106 +++ articles/maximum-subarray.md | 150 ++++ articles/median-of-two-sorted-arrays.md | 123 +++- articles/meeting-schedule-ii.md | 134 ++++ articles/meeting-schedule.md | 56 ++ articles/merge-intervals.md | 92 +++ articles/merge-k-sorted-linked-lists.md | 336 +++++++++ articles/merge-triplets-to-form-target.md | 43 ++ articles/merge-two-sorted-linked-lists.md | 66 ++ articles/min-cost-climbing-stairs.md | 63 ++ articles/min-cost-to-connect-points.md | 156 ++++- articles/minimum-interval-including-query.md | 252 ++++++- articles/minimum-stack.md | 142 +++- articles/minimum-window-with-characters.md | 89 +++ articles/missing-number.md | 59 ++ articles/multiply-strings.md | 97 +++ articles/n-queens.md | 175 +++++ articles/network-delay-time.md | 174 +++++ articles/non-cyclical-number.md | 92 +++ articles/non-overlapping-intervals.md | 155 +++++ articles/number-of-one-bits.md | 50 ++ articles/pacific-atlantic-water-flow.md | 149 ++++ articles/palindrome-partitioning.md | 177 ++++- articles/palindromic-substrings.md | 139 ++++ articles/partition-equal-subset-sum.md | 171 ++++- articles/partition-labels.md | 25 + articles/permutation-string.md | 109 ++- articles/permutations.md | 124 ++++ articles/plus-one.md | 66 ++ articles/pow-x-n.md | 69 +- articles/products-of-array-discluding-self.md | 117 +++- articles/reconstruct-flight-path.md | 89 +++ articles/redundant-connection.md | 185 +++++ articles/regular-expression-matching.md | 158 +++++ .../remove-node-from-end-of-linked-list.md | 138 +++- articles/reorder-linked-list.md | 152 +++- articles/reverse-a-linked-list.md | 56 ++ articles/reverse-bits.md | 52 ++ articles/reverse-integer.md | 68 ++ articles/reverse-nodes-in-k-group.md | 92 +++ articles/rotate-matrix.md | 69 ++ articles/rotting-fruit.md | 106 +++ articles/same-binary-tree.md | 114 +++ articles/search-2d-matrix.md | 99 +++ articles/search-for-word-ii.md | 192 +++++- articles/search-for-word.md | 115 ++++ .../serialize-and-deserialize-binary-tree.md | 125 ++++ articles/set-zeroes-in-matrix.md | 100 +++ articles/single-number.md | 69 ++ articles/sliding-window-maximum.md | 179 ++++- articles/spiral-matrix.md | 90 +++ articles/string-encode-and-decode.md | 85 +++ articles/subsets-ii.md | 131 +++- articles/subsets.md | 71 +- articles/subtree-of-a-binary-tree.md | 121 +++- articles/sum-of-two-integers.md | 53 ++ articles/surrounded-regions.md | 176 ++++- articles/swim-in-rising-water.md | 239 ++++++- articles/target-sum.md | 73 ++ articles/task-scheduling.md | 120 ++++ articles/three-integer-sum.md | 100 ++- articles/time-based-key-value-store.md | 110 +++ articles/top-k-elements-in-list.md | 90 ++- articles/trapping-rain-water.md | 111 +++ articles/two-integer-sum-ii.md | 77 +++ articles/two-integer-sum.md | 82 +++ articles/valid-binary-search-tree.md | 111 +++ articles/valid-parenthesis-string.md | 215 ++++++ articles/valid-sudoku.md | 98 +++ articles/valid-tree.md | 138 +++- articles/validate-parentheses.md | 37 + articles/word-break.md | 209 +++++- articles/word-ladder.md | 231 +++++++ 150 files changed, 17199 insertions(+), 148 deletions(-) diff --git a/articles/add-two-numbers.md b/articles/add-two-numbers.md index 6225ac227..118adab49 100644 --- a/articles/add-two-numbers.md +++ b/articles/add-two-numbers.md @@ -303,6 +303,40 @@ class Solution { } ``` +```swift +/** + * Definition for singly-linked list. + * public class ListNode { + * public var val: Int + * public var next: ListNode? + * public init() { self.val = 0; self.next = nil; } + * public init(_ val: Int) { self.val = val; self.next = nil; } + * public init(_ val: Int, _ next: ListNode?) { self.val = val; self.next = next; } + * } + */ +class Solution { + private func add(_ l1: ListNode?, _ l2: ListNode?, _ carry: Int) -> ListNode? { + if l1 == nil && l2 == nil && carry == 0 { + return nil + } + + let v1 = l1?.val ?? 0 + let v2 = l2?.val ?? 0 + + let sum = v1 + v2 + carry + let newCarry = sum / 10 + let val = sum % 10 + + let nextNode = add(l1?.next, l2?.next, newCarry) + return ListNode(val, nextNode) + } + + func addTwoNumbers(_ l1: ListNode?, _ l2: ListNode?) -> ListNode? { + return add(l1, l2, 0) + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -577,11 +611,49 @@ class Solution { } ``` +```swift +/** + * Definition for singly-linked list. + * public class ListNode { + * public var val: Int + * public var next: ListNode? + * public init() { self.val = 0; self.next = nil; } + * public init(_ val: Int) { self.val = val; self.next = nil; } + * public init(_ val: Int, _ next: ListNode?) { self.val = val; self.next = next; } + * } + */ +class Solution { + func addTwoNumbers(_ l1: ListNode?, _ l2: ListNode?) -> ListNode? { + let dummy = ListNode(0) + var cur = dummy + var l1 = l1, l2 = l2 + var carry = 0 + + while l1 != nil || l2 != nil || carry != 0 { + let v1 = l1?.val ?? 0 + let v2 = l2?.val ?? 0 + + let sum = v1 + v2 + carry + carry = sum / 10 + let val = sum % 10 + cur.next = ListNode(val) + + cur = cur.next! + l1 = l1?.next + l2 = l2?.next + } + return dummy.next + } +} +``` + ::tabs-end ### Time & Space Complexity * Time complexity: $O(m + n)$ -* Space complexity: $O(1)$ +* Space complexity: + * $O(1)$ extra space. + * $O(max(m, n))$ for the output list. > Where $m$ is the length of $l1$ and $n$ is the length of $l2$. \ No newline at end of file diff --git a/articles/anagram-groups.md b/articles/anagram-groups.md index a0aae3216..bf41aaa79 100644 --- a/articles/anagram-groups.md +++ b/articles/anagram-groups.md @@ -125,6 +125,21 @@ class Solution { } ``` +```swift +class Solution { + func groupAnagrams(_ strs: [String]) -> [[String]] { + var res = [String: [String]]() + + for s in strs { + let sortedS = String(s.sorted()) + res[sortedS, default: []].append(s) + } + + return Array(res.values) + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -277,11 +292,31 @@ class Solution { } ``` +```swift +class Solution { + func groupAnagrams(_ strs: [String]) -> [[String]] { + var res = [Array: [String]]() + + for s in strs { + var count = [Int](repeating: 0, count: 26) + for c in s { + count[Int(c.asciiValue!) - 97] += 1 + } + res[count, default: []].append(s) + } + + return Array(res.values) + } +} +``` + ::tabs-end ### Time & Space Complexity * Time complexity: $O(m * n)$ -* Space complexity: $O(m)$ +* Space complexity: + * $O(m)$ extra space. + * $O(m * n)$ space for the output list. -> Where $m$ is the number of strings and $n$ is the length of the longest string. +> Where $m$ is the number of strings and $n$ is the length of the longest string. \ No newline at end of file diff --git a/articles/balanced-binary-tree.md b/articles/balanced-binary-tree.md index c9c407ed0..21715bc6f 100644 --- a/articles/balanced-binary-tree.md +++ b/articles/balanced-binary-tree.md @@ -254,6 +254,43 @@ class Solution { } ``` +```swift +/** + * Definition for a binary tree node. + * public class TreeNode { + * public var val: Int + * public var left: TreeNode? + * public var right: TreeNode? + * public init() { self.val = 0; self.left = nil; self.right = nil; } + * public init(_ val: Int) { self.val = val; self.left = nil; self.right = nil; } + * public init(_ val: Int, _ left: TreeNode?, _ right: TreeNode?) { + * self.val = val + * self.left = left + * self.right = right + * } + * } + */ +class Solution { + func isBalanced(_ root: TreeNode?) -> Bool { + guard let root = root else { return true } + + let left = height(root.left) + let right = height(root.right) + + if abs(left - right) > 1 { + return false + } + + return isBalanced(root.left) && isBalanced(root.right) + } + + private func height(_ root: TreeNode?) -> Int { + guard let root = root else { return 0 } + return 1 + max(height(root.left), height(root.right)) + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -521,6 +558,39 @@ class Solution { } ``` +```swift +/** + * Definition for a binary tree node. + * public class TreeNode { + * public var val: Int + * public var left: TreeNode? + * public var right: TreeNode? + * public init() { self.val = 0; self.left = nil; self.right = nil; } + * public init(_ val: Int) { self.val = val; self.left = nil; self.right = nil; } + * public init(_ val: Int, _ left: TreeNode?, _ right: TreeNode?) { + * self.val = val + * self.left = left + * self.right = right + * } + * } + */ +class Solution { + func isBalanced(_ root: TreeNode?) -> Bool { + return dfs(root).0 + } + + private func dfs(_ root: TreeNode?) -> (Bool, Int) { + guard let root = root else { return (true, 0) } + + let left = dfs(root.left) + let right = dfs(root.right) + + let balanced = left.0 && right.0 && abs(left.1 - right.1) <= 1 + return (balanced, 1 + max(left.1, right.1)) + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -534,7 +604,7 @@ class Solution { --- -## 3. Depth First Search (Stack) +## 3. Iterative DFS ::tabs-start @@ -866,6 +936,58 @@ class Solution { } ``` +```swift +/** + * Definition for a binary tree node. + * public class TreeNode { + * public var val: Int + * public var left: TreeNode? + * public var right: TreeNode? + * public init() { self.val = 0; self.left = nil; self.right = nil; } + * public init(_ val: Int) { self.val = val; self.left = nil; self.right = nil; } + * public init(_ val: Int, _ left: TreeNode?, _ right: TreeNode?) { + * self.val = val + * self.left = left + * self.right = right + * } + * } + */ +class Solution { + func isBalanced(_ root: TreeNode?) -> Bool { + var stack = [TreeNode]() + var node = root + var last: TreeNode? = nil + var depths = [ObjectIdentifier: Int]() + + while !stack.isEmpty || node != nil { + if let current = node { + stack.append(current) + node = current.left + } else { + guard let current = stack.last else { break } + if current.right == nil || last === current.right { + stack.removeLast() + + let leftDepth = current.left != nil ? depths[ObjectIdentifier(current.left!)] ?? 0 : 0 + let rightDepth = current.right != nil ? depths[ObjectIdentifier(current.right!)] ?? 0 : 0 + if abs(leftDepth - rightDepth) > 1 { + return false + } + + depths[ObjectIdentifier(current)] = 1 + max(leftDepth, rightDepth) + last = current + node = nil + } else { + node = current.right + } + } + } + + return true + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/binary-search.md b/articles/binary-search.md index 5e88782ce..59a970a26 100644 --- a/articles/binary-search.md +++ b/articles/binary-search.md @@ -139,6 +139,29 @@ class Solution { } ``` +```swift +class Solution { + func binarySearch(_ l: Int, _ r: Int, _ nums: [Int], _ target: Int) -> Int { + if l > r { + return -1 + } + let m = l + (r - l) / 2 + + if nums[m] == target { + return m + } + if nums[m] < target { + return binarySearch(m + 1, r, nums, target) + } + return binarySearch(l, m - 1, nums, target) + } + + func search(_ nums: [Int], _ target: Int) -> Int { + return binarySearch(0, nums.count - 1, nums, target) + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -296,6 +319,28 @@ class Solution { } ``` +```swift +class Solution { + func search(_ nums: [Int], _ target: Int) -> Int { + var l = 0, r = nums.count - 1 + + while l <= r { + // (l + r) // 2 can lead to overflow + let m = l + (r - l) / 2 + + if nums[m] > target { + r = m - 1 + } else if nums[m] < target { + l = m + 1 + } else { + return m + } + } + return -1 + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -439,6 +484,24 @@ class Solution { } ``` +```swift +class Solution { + func search(_ nums: [Int], _ target: Int) -> Int { + var l = 0, r = nums.count + + while l < r { + let m = l + (r - l) / 2 + if nums[m] > target { + r = m + } else { + l = m + 1 + } + } + return (l > 0 && nums[l - 1] == target) ? l - 1 : -1 + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -582,6 +645,24 @@ class Solution { } ``` +```swift +class Solution { + func search(_ nums: [Int], _ target: Int) -> Int { + var l = 0, r = nums.count + + while l < r { + let m = l + (r - l) / 2 + if nums[m] >= target { + r = m + } else { + l = m + 1 + } + } + return (l < nums.count && nums[l] == target) ? l : -1 + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -664,6 +745,15 @@ class Solution { } ``` +```swift +class Solution { + func search(_ nums: [Int], _ target: Int) -> Int { + let index = nums.partitioningIndex { $0 >= target } + return (index < nums.count && nums[index] == target) ? index : -1 + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/binary-tree-diameter.md b/articles/binary-tree-diameter.md index 3c73fbe92..b11c3e40e 100644 --- a/articles/binary-tree-diameter.md +++ b/articles/binary-tree-diameter.md @@ -259,6 +259,41 @@ class Solution { } ``` +```swift +/** + * Definition for a binary tree node. + * public class TreeNode { + * public var val: Int + * public var left: TreeNode? + * public var right: TreeNode? + * public init() { self.val = 0; self.left = nil; self.right = nil; } + * public init(_ val: Int) { self.val = val; self.left = nil; self.right = nil; } + * public init(_ val: Int, _ left: TreeNode?, _ right: TreeNode?) { + * self.val = val + * self.left = left + * self.right = right + * } + * } + */ +class Solution { + func diameterOfBinaryTree(_ root: TreeNode?) -> Int { + guard let root = root else { return 0 } + + let leftHeight = maxHeight(root.left) + let rightHeight = maxHeight(root.right) + let diameter = leftHeight + rightHeight + let sub = max(diameterOfBinaryTree(root.left), diameterOfBinaryTree(root.right)) + + return max(diameter, sub) + } + + private func maxHeight(_ root: TreeNode?) -> Int { + guard let root = root else { return 0 } + return 1 + max(maxHeight(root.left), maxHeight(root.right)) + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -515,6 +550,40 @@ class Solution { } ``` +```swift +/** + * Definition for a binary tree node. + * public class TreeNode { + * public var val: Int + * public var left: TreeNode? + * public var right: TreeNode? + * public init() { self.val = 0; self.left = nil; self.right = nil; } + * public init(_ val: Int) { self.val = val; self.left = nil; self.right = nil; } + * public init(_ val: Int, _ left: TreeNode?, _ right: TreeNode?) { + * self.val = val + * self.left = left + * self.right = right + * } + * } + */ +class Solution { + func diameterOfBinaryTree(_ root: TreeNode?) -> Int { + var res = 0 + + func dfs(_ root: TreeNode?) -> Int { + guard let root = root else { return 0 } + let left = dfs(root.left) + let right = dfs(root.right) + res = max(res, left + right) + return 1 + max(left, right) + } + + dfs(root) + return res + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -876,6 +945,49 @@ class Solution { } ``` +```swift +/** + * Definition for a binary tree node. + * public class TreeNode { + * public var val: Int + * public var left: TreeNode? + * public var right: TreeNode? + * public init() { self.val = 0; self.left = nil; self.right = nil; } + * public init(_ val: Int) { self.val = val; self.left = nil; self.right = nil; } + * public init(_ val: Int, _ left: TreeNode?, _ right: TreeNode?) { + * self.val = val + * self.left = left + * self.right = right + * } + * } + */ +class Solution { + func diameterOfBinaryTree(_ root: TreeNode?) -> Int { + guard let root = root else { return 0 } + var stack: [TreeNode] = [root] + var mp = [ObjectIdentifier: (Int, Int)]() + + while !stack.isEmpty { + let node = stack.last! + if let left = node.left, mp[ObjectIdentifier(left)] == nil { + stack.append(left) + } else if let right = node.right, mp[ObjectIdentifier(right)] == nil { + stack.append(right) + } else { + let node = stack.removeLast() + let leftTuple = node.left != nil ? mp[ObjectIdentifier(node.left!)]! : (0, 0) + let rightTuple = node.right != nil ? mp[ObjectIdentifier(node.right!)]! : (0, 0) + let height = 1 + max(leftTuple.0, rightTuple.0) + let diameter = max(leftTuple.0 + rightTuple.0, leftTuple.1, rightTuple.1) + mp[ObjectIdentifier(node)] = (height, diameter) + } + } + + return mp[ObjectIdentifier(root)]!.1 + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/binary-tree-from-preorder-and-inorder-traversal.md b/articles/binary-tree-from-preorder-and-inorder-traversal.md index 621fc91cd..b21106a35 100644 --- a/articles/binary-tree-from-preorder-and-inorder-traversal.md +++ b/articles/binary-tree-from-preorder-and-inorder-traversal.md @@ -235,6 +235,49 @@ class Solution { } ``` +```swift +/** + * Definition for a binary tree node. + * public class TreeNode { + * public var val: Int + * public var left: TreeNode? + * public var right: TreeNode? + * public init() { self.val = 0; self.left = nil; self.right = nil; } + * public init(_ val: Int) { self.val = val; self.left = nil; self.right = nil; } + * public init(_ val: Int, _ left: TreeNode?, _ right: TreeNode?) { + * self.val = val + * self.left = left + * self.right = right + * } + * } + */ +class Solution { + func buildTree(_ preorder: [Int], _ inorder: [Int]) -> TreeNode? { + if preorder.isEmpty || inorder.isEmpty { + return nil + } + + let rootValue = preorder[0] + let root = TreeNode(rootValue) + guard let mid = inorder.firstIndex(of: rootValue) else { + return root + } + + root.left = buildTree( + Array(preorder[1..<(mid + 1)]), + Array(inorder[0.. TreeNode? { + for (index, value) in inorder.enumerated() { + indexMap[value] = index + } + return dfs(preorder, 0, inorder.count - 1) + } + + private func dfs(_ preorder: [Int], _ left: Int, _ right: Int) -> TreeNode? { + if left > right { + return nil + } + + let rootVal = preorder[preIndex] + preIndex += 1 + let root = TreeNode(rootVal) + let mid = indexMap[rootVal]! + + root.left = dfs(preorder, left, mid - 1) + root.right = dfs(preorder, mid + 1, right) + + return root + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -775,6 +863,48 @@ class Solution { } ``` +```swift +/** + * Definition for a binary tree node. + * public class TreeNode { + * public var val: Int + * public var left: TreeNode? + * public var right: TreeNode? + * public init() { self.val = 0; self.left = nil; self.right = nil; } + * public init(_ val: Int) { self.val = val; self.left = nil; self.right = nil; } + * public init(_ val: Int, _ left: TreeNode?, _ right: TreeNode?) { + * self.val = val + * self.left = left + * self.right = right + * } + * } + */ +class Solution { + var preIdx = 0 + var inIdx = 0 + + func buildTree(_ preorder: [Int], _ inorder: [Int]) -> TreeNode? { + return dfs(preorder, inorder, Int.max) + } + + private func dfs(_ preorder: [Int], _ inorder: [Int], _ limit: Int) -> TreeNode? { + if preIdx >= preorder.count { + return nil + } + if inorder[inIdx] == limit { + inIdx += 1 + return nil + } + + let root = TreeNode(preorder[preIdx]) + preIdx += 1 + root.left = dfs(preorder, inorder, root.val) + root.right = dfs(preorder, inorder, limit) + return root + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/binary-tree-maximum-path-sum.md b/articles/binary-tree-maximum-path-sum.md index af442201a..ebd5467bc 100644 --- a/articles/binary-tree-maximum-path-sum.md +++ b/articles/binary-tree-maximum-path-sum.md @@ -290,6 +290,49 @@ class Solution { } ``` +```swift +/** + * Definition for a binary tree node. + * public class TreeNode { + * public var val: Int + * public var left: TreeNode? + * public var right: TreeNode? + * public init() { self.val = 0; self.left = nil; self.right = nil; } + * public init(_ val: Int) { self.val = val; self.left = nil; self.right = nil; } + * public init(_ val: Int, _ left: TreeNode?, _ right: TreeNode?) { + * self.val = val + * self.left = left + * self.right = right + * } + * } + */ +class Solution { + var res = Int.min + + func maxPathSum(_ root: TreeNode?) -> Int { + dfs(root) + return res + } + + private func dfs(_ root: TreeNode?) { + guard let node = root else { return } + let left = getMax(node.left) + let right = getMax(node.right) + res = max(res, node.val + left + right) + dfs(node.left) + dfs(node.right) + } + + private func getMax(_ root: TreeNode?) -> Int { + guard let node = root else { return 0 } + let left = getMax(node.left) + let right = getMax(node.right) + let path = node.val + max(left, right) + return max(0, path) + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -560,6 +603,42 @@ class Solution { } ``` +```swift +/** + * Definition for a binary tree node. + * public class TreeNode { + * public var val: Int + * public var left: TreeNode? + * public var right: TreeNode? + * public init() { self.val = 0; self.left = nil; self.right = nil; } + * public init(_ val: Int) { self.val = val; self.left = nil; self.right = nil; } + * public init(_ val: Int, _ left: TreeNode?, _ right: TreeNode?) { + * self.val = val + * self.left = left + * self.right = right + * } + * } + */ +class Solution { + func maxPathSum(_ root: TreeNode?) -> Int { + var res = root!.val + + func dfs(_ root: TreeNode?) -> Int { + guard let node = root else { return 0 } + + let leftMax = max(dfs(node.left), 0) + let rightMax = max(dfs(node.right), 0) + + res = max(res, node.val + leftMax + rightMax) + return node.val + max(leftMax, rightMax) + } + + dfs(root) + return res + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/binary-tree-right-side-view.md b/articles/binary-tree-right-side-view.md index e17b22299..4d8a68b19 100644 --- a/articles/binary-tree-right-side-view.md +++ b/articles/binary-tree-right-side-view.md @@ -236,6 +236,42 @@ class Solution { } ``` +```swift +/** + * Definition for a binary tree node. + * public class TreeNode { + * public var val: Int + * public var left: TreeNode? + * public var right: TreeNode? + * public init() { self.val = 0; self.left = nil; self.right = nil; } + * public init(_ val: Int) { self.val = val; self.left = nil; self.right = nil; } + * public init(_ val: Int, _ left: TreeNode?, _ right: TreeNode?) { + * self.val = val + * self.left = left + * self.right = right + * } + * } + */ +class Solution { + func rightSideView(_ root: TreeNode?) -> [Int] { + var res = [Int]() + + func dfs(_ node: TreeNode?, _ depth: Int) { + guard let node = node else { return } + if depth == res.count { + res.append(node.val) + } + + dfs(node.right, depth + 1) + dfs(node.left, depth + 1) + } + + dfs(root, 0) + return res + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -530,6 +566,48 @@ class Solution { } ``` +```swift +/** + * Definition for a binary tree node. + * public class TreeNode { + * public var val: Int + * public var left: TreeNode? + * public var right: TreeNode? + * public init() { self.val = 0; self.left = nil; self.right = nil; } + * public init(_ val: Int) { self.val = val; self.left = nil; self.right = nil; } + * public init(_ val: Int, _ left: TreeNode?, _ right: TreeNode?) { + * self.val = val + * self.left = left + * self.right = right + * } + * } + */ +class Solution { + func rightSideView(_ root: TreeNode?) -> [Int] { + var res = [Int]() + var q = Deque() + q.append(root) + + while !q.isEmpty { + var rightSide: TreeNode? + let qLen = q.count + + for _ in 0.. Int { + var nums = [1] + nums + [1] + + func dfs(_ nums: [Int]) -> Int { + if nums.count == 2 { + return 0 + } + + var maxCoins = 0 + for i in 1..<(nums.count - 1) { + let coins = nums[i - 1] * nums[i] * nums[i + 1] + + dfs(Array(nums[.. Int { + var nums = [1] + nums + [1] + let n = nums.count + var dp = Array(repeating: Array(repeating: -1, count: n), count: n) + + func dfs(_ l: Int, _ r: Int) -> Int { + if l > r { + return 0 + } + if dp[l][r] != -1 { + return dp[l][r] + } + + dp[l][r] = 0 + for i in l...r { + let coins = nums[l - 1] * nums[i] * nums[r + 1] + dfs(l, i - 1) + dfs(i + 1, r) + dp[l][r] = max(dp[l][r], coins) + } + return dp[l][r] + } + + return dfs(1, n - 2) + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -621,6 +673,29 @@ class Solution { } ``` +```swift +class Solution { + func maxCoins(_ nums: [Int]) -> Int { + let n = nums.count + var newNums = [1] + nums + [1] + + var dp = Array(repeating: Array(repeating: 0, count: n + 2), count: n + 2) + + for l in stride(from: n, through: 1, by: -1) { + for r in l...n { + for i in l...r { + let coins = (newNums[l - 1] * newNums[i] * newNums[r + 1]) + + dp[l][i - 1] + dp[i + 1][r] + dp[l][r] = max(dp[l][r], coins) + } + } + } + + return dp[1][n] + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/buy-and-sell-crypto-with-cooldown.md b/articles/buy-and-sell-crypto-with-cooldown.md index 366028454..9c4ae550b 100644 --- a/articles/buy-and-sell-crypto-with-cooldown.md +++ b/articles/buy-and-sell-crypto-with-cooldown.md @@ -171,6 +171,31 @@ class Solution { } ``` +```swift +class Solution { + func maxProfit(_ prices: [Int]) -> Int { + let n = prices.count + + func dfs(_ i: Int, _ buying: Bool) -> Int { + if i >= n { + return 0 + } + + let cooldown = dfs(i + 1, buying) + if buying { + let buy = dfs(i + 1, false) - prices[i] + return max(buy, cooldown) + } else { + let sell = dfs(i + 2, true) + prices[i] + return max(sell, cooldown) + } + } + + return dfs(0, true) + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -412,6 +437,36 @@ class Solution { } ``` +```swift +class Solution { + func maxProfit(_ prices: [Int]) -> Int { + let n = prices.count + var dp = [[Int?]](repeating: [Int?](repeating: nil, count: 2), count: n) + + func dfs(_ i: Int, _ buying: Int) -> Int { + if i >= n { + return 0 + } + if let cached = dp[i][buying] { + return cached + } + + let cooldown = dfs(i + 1, buying) + if buying == 1 { + let buy = dfs(i + 1, 0) - prices[i] + dp[i][buying] = max(buy, cooldown) + } else { + let sell = dfs(i + 2, 1) + prices[i] + dp[i][buying] = max(sell, cooldown) + } + return dp[i][buying]! + } + + return dfs(0, 1) + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -611,6 +666,31 @@ class Solution { } ``` +```swift +class Solution { + func maxProfit(_ prices: [Int]) -> Int { + let n = prices.count + var dp = Array(repeating: [0, 0], count: n + 1) + + for i in stride(from: n - 1, through: 0, by: -1) { + for buying in 0...1 { + if buying == 1 { + let buy = (i + 1 < n ? dp[i + 1][0] - prices[i] : -prices[i]) + let cooldown = (i + 1 < n ? dp[i + 1][1] : 0) + dp[i][1] = max(buy, cooldown) + } else { + let sell = (i + 2 < n ? dp[i + 2][1] + prices[i] : prices[i]) + let cooldown = (i + 1 < n ? dp[i + 1][0] : 0) + dp[i][0] = max(sell, cooldown) + } + } + } + + return dp[0][1] + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -769,6 +849,26 @@ class Solution { } ``` +```swift +class Solution { + func maxProfit(_ prices: [Int]) -> Int { + let n = prices.count + var dp1Buy = 0, dp1Sell = 0 + var dp2Buy = 0 + + for i in stride(from: n - 1, through: 0, by: -1) { + let dpBuy = max(dp1Sell - prices[i], dp1Buy) + let dpSell = max(dp2Buy + prices[i], dp1Sell) + dp2Buy = dp1Buy + dp1Buy = dpBuy + dp1Sell = dpSell + } + + return dp1Buy + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/buy-and-sell-crypto.md b/articles/buy-and-sell-crypto.md index 0e618efd5..b983fd272 100644 --- a/articles/buy-and-sell-crypto.md +++ b/articles/buy-and-sell-crypto.md @@ -120,6 +120,22 @@ class Solution { } ``` +```swift +class Solution { + func maxProfit(_ prices: [Int]) -> Int { + var res = 0 + for i in 0.. Int { + var l = 0, r = 1 + var maxP = 0 + + while r < prices.count { + if prices[l] < prices[r] { + let profit = prices[r] - prices[l] + maxP = max(maxP, profit) + } else { + l = r + } + r += 1 + } + return maxP + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -397,6 +433,21 @@ class Solution { } ``` +```swift +class Solution { + func maxProfit(_ prices: [Int]) -> Int { + var maxP = 0 + var minBuy = prices[0] + + for sell in prices { + maxP = max(maxP, sell - minBuy) + minBuy = min(minBuy, sell) + } + return maxP + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/car-fleet.md b/articles/car-fleet.md index 2d4e2ef35..22c7a9d0f 100644 --- a/articles/car-fleet.md +++ b/articles/car-fleet.md @@ -150,6 +150,26 @@ class Solution { } ``` +```swift +class Solution { + func carFleet(_ target: Int, _ position: [Int], _ speed: [Int]) -> Int { + var pair = zip(position, speed).map { ($0, $1) } + pair.sort { $0.0 > $1.0 } // Sort in descending order by position + + var stack = [Double]() + + for (p, s) in pair { + stack.append(Double(target - p) / Double(s)) + if stack.count >= 2 && stack.last! <= stack[stack.count - 2] { + stack.removeLast() + } + } + + return stack.count + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -327,6 +347,30 @@ class Solution { } ``` +```swift +class Solution { + func carFleet(_ target: Int, _ position: [Int], _ speed: [Int]) -> Int { + var pair = zip(position, speed).map { ($0, $1) } + pair.sort { $0.0 > $1.0 } // Sort in descending order by position + + var fleets = 1 + var prevTime = Double(target - pair[0].0) / Double(pair[0].1) + + for i in 1.. prevTime { + fleets += 1 + prevTime = currTime + } + } + + return fleets + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/cheapest-flight-path.md b/articles/cheapest-flight-path.md index 6c23351be..976640249 100644 --- a/articles/cheapest-flight-path.md +++ b/articles/cheapest-flight-path.md @@ -258,6 +258,53 @@ class Solution { } ``` +```swift +struct Item: Comparable { + let cost: Int + let node: Int + let stops: Int + + static func < (lhs: Item, rhs: Item) -> Bool { + return lhs.cost < rhs.cost + } +} + +class Solution { + func findCheapestPrice(_ n: Int, _ flights: [[Int]], _ src: Int, _ dst: Int, _ k: Int) -> Int { + let INF = Int.max + var adj = Array(repeating: [(Int, Int)](), count: n) + var dist = Array(repeating: Array(repeating: INF, count: k + 5), count: n) + + for flight in flights { + let u = flight[0], v = flight[1], cst = flight[2] + adj[u].append((v, cst)) + } + + var minHeap = Heap() + minHeap.insert(Item(cost: 0, node: src, stops: -1)) + dist[src][0] = 0 + + while !minHeap.isEmpty { + let item = minHeap.removeMin() + let cst = item.cost, node = item.node, stops = item.stops + if node == dst { return cst } + if stops == k || dist[node][stops + 1] < cst { continue } + + for (nei, w) in adj[node] { + let nextCst = cst + w + let nextStops = stops + 1 + if dist[nei][nextStops + 1] > nextCst { + dist[nei][nextStops + 1] = nextCst + minHeap.insert(Item(cost: nextCst, node: nei, stops: nextStops)) + } + } + } + + return -1 + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -471,6 +518,31 @@ class Solution { } ``` +```swift +class Solution { + func findCheapestPrice(_ n: Int, _ flights: [[Int]], _ src: Int, _ dst: Int, _ k: Int) -> Int { + var prices = Array(repeating: Int.max, count: n) + prices[src] = 0 + + for _ in 0...k { + var tmpPrices = prices + + for flight in flights { + let s = flight[0], d = flight[1], p = flight[2] + if prices[s] == Int.max { + continue + } + if prices[s] + p < tmpPrices[d] { + tmpPrices[d] = prices[s] + p + } + } + prices = tmpPrices + } + return prices[dst] == Int.max ? -1 : prices[dst] + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -727,6 +799,41 @@ class Solution { } ``` +```swift +class Solution { + func findCheapestPrice(_ n: Int, _ flights: [[Int]], _ src: Int, _ dst: Int, _ k: Int) -> Int { + var prices = Array(repeating: Int.max, count: n) + prices[src] = 0 + var adj = Array(repeating: [(Int, Int)](), count: n) + + for flight in flights { + let u = flight[0], v = flight[1], cst = flight[2] + adj[u].append((v, cst)) + } + + var queue = Deque<(Int, Int, Int)>() + queue.append((0, src, 0)) + + while !queue.isEmpty { + let (cst, node, stops) = queue.popFirst()! + if stops > k { + continue + } + + for (nei, w) in adj[node] { + let nextCost = cst + w + if nextCost < prices[nei] { + prices[nei] = nextCost + queue.append((nextCost, nei, stops + 1)) + } + } + } + + return prices[dst] == Int.max ? -1 : prices[dst] + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/climbing-stairs.md b/articles/climbing-stairs.md index a1a70e0fa..126511582 100644 --- a/articles/climbing-stairs.md +++ b/articles/climbing-stairs.md @@ -99,6 +99,21 @@ class Solution { } ``` +```swift +class Solution { + func climbStairs(_ n: Int) -> Int { + func dfs(_ i: Int) -> Int { + if i >= n { + return i == n ? 1 : 0 + } + return dfs(i + 1) + dfs(i + 2) + } + + return dfs(0) + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -240,6 +255,27 @@ class Solution { } ``` +```swift +class Solution { + func climbStairs(_ n: Int) -> Int { + var cache = Array(repeating: -1, count: n) + + func dfs(_ i: Int) -> Int { + if i >= n { + return i == n ? 1 : 0 + } + if cache[i] != -1 { + return cache[i] + } + cache[i] = dfs(i + 1) + dfs(i + 2) + return cache[i] + } + + return dfs(0) + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -368,6 +404,23 @@ class Solution { } ``` +```swift +class Solution { + func climbStairs(_ n: Int) -> Int { + if n <= 2 { + return n + } + var dp = Array(repeating: 0, count: n + 1) + dp[1] = 1 + dp[2] = 2 + for i in 3...n { + dp[i] = dp[i - 1] + dp[i - 2] + } + return dp[n] + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -495,6 +548,22 @@ class Solution { } ``` +```swift +class Solution { + func climbStairs(_ n: Int) -> Int { + var one = 1, two = 1 + + for _ in 0..<(n - 1) { + let temp = one + one = one + two + two = temp + } + + return one + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -764,6 +833,45 @@ class Solution { } ``` +```swift +class Solution { + func climbStairs(_ n: Int) -> Int { + if n == 1 { + return 1 + } + + func matrixMult(_ A: [[Int]], _ B: [[Int]]) -> [[Int]] { + return [ + [A[0][0] * B[0][0] + A[0][1] * B[1][0], + A[0][0] * B[0][1] + A[0][1] * B[1][1]], + [A[1][0] * B[0][0] + A[1][1] * B[1][0], + A[1][0] * B[0][1] + A[1][1] * B[1][1]] + ] + } + + func matrixPow(_ M: [[Int]], _ p: Int) -> [[Int]] { + var result = [[1, 0], [0, 1]] + var base = M + var power = p + + while power > 0 { + if power % 2 == 1 { + result = matrixMult(result, base) + } + base = matrixMult(base, base) + power /= 2 + } + + return result + } + + let M = [[1, 1], [1, 0]] + let result = matrixPow(M, n) + return result[0][0] + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -865,6 +973,18 @@ class Solution { } ``` +```swift +class Solution { + func climbStairs(_ n: Int) -> Int { + let sqrt5 = sqrt(5.0) + let phi = (1.0 + sqrt5) / 2.0 + let psi = (1.0 - sqrt5) / 2.0 + let n = n + 1 + return Int(round((pow(phi, Double(n)) - pow(psi, Double(n))) / sqrt5)) + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/clone-graph.md b/articles/clone-graph.md index ff2612c4c..e71e55184 100644 --- a/articles/clone-graph.md +++ b/articles/clone-graph.md @@ -290,6 +290,47 @@ class Solution { } ``` +```swift +/** + * Definition for a Node. + * public class Node { + * public var val: Int + * public var neighbors: [Node?] + * public init(_ val: Int) { + * self.val = val + * self.neighbors = [] + * } + * } + */ + +class Solution { + func cloneGraph(_ node: Node?) -> Node? { + var oldToNew = [Node: Node]() + + func dfs(_ node: Node?) -> Node? { + guard let node = node else { return nil } + + if let existingCopy = oldToNew[node] { + return existingCopy + } + + let copy = Node(node.val) + oldToNew[node] = copy + + for neighbor in node.neighbors { + if let clonedNeighbor = dfs(neighbor) { + copy.neighbors.append(clonedNeighbor) + } + } + + return copy + } + + return dfs(node) + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -582,6 +623,49 @@ class Solution { } ``` +```swift +/** + * Definition for a Node. + * public class Node { + * public var val: Int + * public var neighbors: [Node?] + * public init(_ val: Int) { + * self.val = val + * self.neighbors = [] + * } + * } + */ + +class Solution { + func cloneGraph(_ node: Node?) -> Node? { + if node == nil { + return nil + } + + var oldToNew: [Node: Node] = [:] + let newNode = Node(node!.val) + oldToNew[node!] = newNode + var queue = Deque() + queue.append(node!) + + while !queue.isEmpty { + let cur = queue.popFirst()! + for nei in cur.neighbors { + if let nei = nei { + if oldToNew[nei] == nil { + oldToNew[nei] = Node(nei.val) + queue.append(nei) + } + oldToNew[cur]!.neighbors.append(oldToNew[nei]!) + } + } + } + + return oldToNew[node!] + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/coin-change-ii.md b/articles/coin-change-ii.md index cdfd83983..6e4cce788 100644 --- a/articles/coin-change-ii.md +++ b/articles/coin-change-ii.md @@ -178,6 +178,32 @@ class Solution { } ``` +```swift +class Solution { + func change(_ amount: Int, _ coins: [Int]) -> Int { + let coins = coins.sorted() + + func dfs(_ i: Int, _ a: Int) -> Int { + if a == 0 { + return 1 + } + if i >= coins.count { + return 0 + } + + var res = 0 + if a >= coins[i] { + res = dfs(i + 1, a) + res += dfs(i, a - coins[i]) + } + return res + } + + return dfs(0, amount) + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -399,6 +425,38 @@ class Solution { } ``` +```swift +class Solution { + func change(_ amount: Int, _ coins: [Int]) -> Int { + let coins = coins.sorted() + var memo = Array(repeating: Array(repeating: -1, count: amount + 1), count: coins.count + 1) + + func dfs(_ i: Int, _ a: Int) -> Int { + if a == 0 { + return 1 + } + if i >= coins.count { + return 0 + } + if memo[i][a] != -1 { + return memo[i][a] + } + + var res = 0 + if a >= coins[i] { + res = dfs(i + 1, a) + res += dfs(i, a - coins[i]) + } + + memo[i][a] = res + return res + } + + return dfs(0, amount) + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -464,8 +522,8 @@ public: int change(int amount, vector& coins) { int n = coins.size(); sort(coins.begin(), coins.end()); - vector> dp(n + 1, vector(amount + 1, 0)); - + vector> dp(n + 1, vector(amount + 1, 0)); + for (int i = 0; i <= n; i++) { dp[i][0] = 1; } @@ -592,6 +650,41 @@ class Solution { } ``` +```swift +class Solution { + func change(_ amount: Int, _ coins: [Int]) -> Int { + let n = coins.count + let sortedCoins = coins.sorted() + var dp = Array( + repeating: Array(repeating: 0, count: amount + 1), + count: n + 1 + ) + + for i in 0...n { + dp[i][0] = 1 + } + + for i in stride(from: n - 1, through: 0, by: -1) { + for a in 0...amount { + let base = dp[i + 1][a] + if a >= sortedCoins[i] { + let addend = dp[i][a - sortedCoins[i]] + if base > Int.max - addend { + dp[i][a] = 0 + } else { + dp[i][a] = base + addend + } + } else { + dp[i][a] = base + } + } + } + + return dp[0][amount] + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -650,10 +743,10 @@ public class Solution { class Solution { public: int change(int amount, vector& coins) { - vector dp(amount + 1, 0); + vector dp(amount + 1, 0); dp[0] = 1; for (int i = coins.size() - 1; i >= 0; i--) { - vector nextDP(amount + 1, 0); + vector nextDP(amount + 1, 0); nextDP[0] = 1; for (int a = 1; a <= amount; a++) { @@ -764,6 +857,36 @@ class Solution { } ``` +```swift +class Solution { + func change(_ amount: Int, _ coins: [Int]) -> Int { + var dp = [Int](repeating: 0, count: amount + 1) + dp[0] = 1 + + for i in stride(from: coins.count - 1, through: 0, by: -1) { + var nextDP = [Int](repeating: 0, count: amount + 1) + nextDP[0] = 1 + + for a in 1..<(amount + 1) { + nextDP[a] = dp[a] + if a - coins[i] >= 0 { + let addend = nextDP[a - coins[i]] + if nextDP[a] > Int.max - addend { + nextDP[a] = 0 + } else { + nextDP[a] += addend + } + } + } + + dp = nextDP + } + + return dp[amount] + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -807,7 +930,7 @@ public class Solution { class Solution { public: int change(int amount, vector& coins) { - vector dp(amount + 1, 0); + vector dp(amount + 1, 0); dp[0] = 1; for (int i = coins.size() - 1; i >= 0; i--) { for (int a = 1; a <= amount; a++) { @@ -890,6 +1013,30 @@ class Solution { } ``` +```swift +class Solution { + func change(_ amount: Int, _ coins: [Int]) -> Int { + var dp = [Int](repeating: 0, count: amount + 1) + dp[0] = 1 + + for i in stride(from: coins.count - 1, through: 0, by: -1) { + for a in 1..<(amount + 1) { + if coins[i] <= a { + let addend = dp[a - coins[i]] + if dp[a] > Int.max - addend { + dp[a] = 0 + } else { + dp[a] += addend + } + } + } + } + + return dp[amount] + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/coin-change.md b/articles/coin-change.md index eb1f7e715..f61f99922 100644 --- a/articles/coin-change.md +++ b/articles/coin-change.md @@ -170,6 +170,29 @@ class Solution { } ``` +```swift +class Solution { + func coinChange(_ coins: [Int], _ amount: Int) -> Int { + func dfs(_ amount: Int) -> Int { + if amount == 0 { + return 0 + } + + var res = Int(1e9) + for coin in coins { + if amount - coin >= 0 { + res = min(res, 1 + dfs(amount - coin)) + } + } + return res + } + + let minCoins = dfs(amount) + return minCoins >= Int(1e9) ? -1 : minCoins + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -393,6 +416,36 @@ class Solution { } ``` +```swift +class Solution { + func coinChange(_ coins: [Int], _ amount: Int) -> Int { + var memo = [Int: Int]() + + func dfs(_ amount: Int) -> Int { + if amount == 0 { + return 0 + } + if let cached = memo[amount] { + return cached + } + + var res = Int(1e9) + for coin in coins { + if amount - coin >= 0 { + res = min(res, 1 + dfs(amount - coin)) + } + } + + memo[amount] = res + return res + } + + let minCoins = dfs(amount) + return minCoins >= Int(1e9) ? -1 : minCoins + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -542,6 +595,29 @@ class Solution { } ``` +```swift +class Solution { + func coinChange(_ coins: [Int], _ amount: Int) -> Int { + if amount == 0 { + return 0 + } + + var dp = [Int](repeating: amount + 1, count: amount + 1) + dp[0] = 0 + + for a in 1...amount { + for coin in coins { + if a - coin >= 0 { + dp[a] = min(dp[a], 1 + dp[a - coin]) + } + } + } + + return dp[amount] == amount + 1 ? -1 : dp[amount] + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -772,6 +848,41 @@ class Solution { } ``` +```swift +class Solution { + func coinChange(_ coins: [Int], _ amount: Int) -> Int { + if amount == 0 { + return 0 + } + + var q = Deque([0]) + var seen = Array(repeating: false, count: amount + 1) + seen[0] = true + var res = 0 + + while !q.isEmpty { + res += 1 + for _ in 0.. amount || seen[nxt] { + continue + } + seen[nxt] = true + q.append(nxt) + } + } + } + + return -1 + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/combination-target-sum-ii.md b/articles/combination-target-sum-ii.md index 0c9a90f24..3b35aab6d 100644 --- a/articles/combination-target-sum-ii.md +++ b/articles/combination-target-sum-ii.md @@ -216,6 +216,35 @@ class Solution { } ``` +```swift +class Solution { + func combinationSum2(_ candidates: [Int], _ target: Int) -> [[Int]] { + var res = Set<[Int]>() + let sortedCandidates = candidates.sorted() + + func generateSubsets(_ i: Int, _ cur: inout [Int], _ total: Int) { + if total == target { + res.insert(cur) + return + } + if total > target || i == sortedCandidates.count { + return + } + + cur.append(sortedCandidates[i]) + generateSubsets(i + 1, &cur, total + sortedCandidates[i]) + cur.removeLast() + + generateSubsets(i + 1, &cur, total) + } + + var cur: [Int] = [] + generateSubsets(0, &cur, 0) + return Array(res) + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -464,6 +493,39 @@ class Solution { } ``` +```swift +class Solution { + func combinationSum2(_ candidates: [Int], _ target: Int) -> [[Int]] { + var res = [[Int]]() + let sortedCandidates = candidates.sorted() + + func dfs(_ i: Int, _ cur: inout [Int], _ total: Int) { + if total == target { + res.append(cur) + return + } + if total > target || i == sortedCandidates.count { + return + } + + cur.append(sortedCandidates[i]) + dfs(i + 1, &cur, total + sortedCandidates[i]) + cur.removeLast() + + var j = i + while j + 1 < sortedCandidates.count && sortedCandidates[j] == sortedCandidates[j + 1] { + j += 1 + } + dfs(j + 1, &cur, total) + } + + var cur: [Int] = [] + dfs(0, &cur, 0) + return res + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -772,6 +834,47 @@ class Solution { } ``` +```swift +class Solution { + func combinationSum2(_ nums: [Int], _ target: Int) -> [[Int]] { + var res = [[Int]]() + var count = [Int: Int]() + var uniqueNums = [Int]() + + for num in nums { + if count[num] == nil { + uniqueNums.append(num) + } + count[num, default: 0] += 1 + } + + func backtrack(_ nums: [Int], _ target: Int, _ cur: inout [Int], _ i: Int) { + if target == 0 { + res.append(cur) + return + } + if target < 0 || i >= nums.count { + return + } + + if count[nums[i], default: 0] > 0 { + cur.append(nums[i]) + count[nums[i], default: 0] -= 1 + backtrack(nums, target - nums[i], &cur, i) + count[nums[i], default: 0] += 1 + cur.removeLast() + } + + backtrack(nums, target, &cur, i + 1) + } + + var cur: [Int] = [] + backtrack(uniqueNums, target, &cur, 0) + return res + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -1008,6 +1111,39 @@ class Solution { } ``` +```swift +class Solution { + func combinationSum2(_ candidates: [Int], _ target: Int) -> [[Int]] { + var res = [[Int]]() + let sortedCandidates = candidates.sorted() + + func dfs(_ idx: Int, _ path: inout [Int], _ cur: Int) { + if cur == target { + res.append(path) + return + } + + for i in idx.. idx && sortedCandidates[i] == sortedCandidates[i - 1] { + continue + } + if cur + sortedCandidates[i] > target { + break + } + + path.append(sortedCandidates[i]) + dfs(i + 1, &path, cur + sortedCandidates[i]) + path.removeLast() + } + } + + var path: [Int] = [] + dfs(0, &path, 0) + return res + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/combination-target-sum.md b/articles/combination-target-sum.md index 43a9f3cd1..46436ad64 100644 --- a/articles/combination-target-sum.md +++ b/articles/combination-target-sum.md @@ -194,6 +194,33 @@ class Solution { } ``` +```swift +class Solution { + func combinationSum(_ nums: [Int], _ target: Int) -> [[Int]] { + var res: [[Int]] = [] + + func dfs(_ i: Int, _ cur: inout [Int], _ total: Int) { + if total == target { + res.append(cur) + return + } + if i >= nums.count || total > target { + return + } + + cur.append(nums[i]) + dfs(i, &cur, total + nums[i]) + cur.removeLast() + dfs(i + 1, &cur, total) + } + + var cur: [Int] = [] + dfs(0, &cur, 0) + return res + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -406,6 +433,35 @@ class Solution { } ``` +```swift +class Solution { + func combinationSum(_ nums: [Int], _ target: Int) -> [[Int]] { + var res: [[Int]] = [] + let sortedNums = nums.sorted() + + func dfs(_ i: Int, _ cur: inout [Int], _ total: Int) { + if total == target { + res.append(cur) + return + } + + for j in i.. target { + return + } + cur.append(sortedNums[j]) + dfs(j, &cur, total + sortedNums[j]) + cur.removeLast() + } + } + + var cur: [Int] = [] + dfs(0, &cur, 0) + return res + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/combinations-of-a-phone-number.md b/articles/combinations-of-a-phone-number.md index 941f7ebf6..6f6bfd6dd 100644 --- a/articles/combinations-of-a-phone-number.md +++ b/articles/combinations-of-a-phone-number.md @@ -212,12 +212,45 @@ class Solution { } ``` +```swift +class Solution { + func letterCombinations(_ digits: String) -> [String] { + guard !digits.isEmpty else { return [] } + + let digitToChar: [Character: String] = [ + "2": "abc", "3": "def", "4": "ghi", "5": "jkl", + "6": "mno", "7": "pqrs", "8": "tuv", "9": "wxyz" + ] + + var res = [String]() + let digitsArray = Array(digits) + + func backtrack(_ i: Int, _ curStr: String) { + if curStr.count == digits.count { + res.append(curStr) + return + } + if let letters = digitToChar[digitsArray[i]] { + for c in letters { + backtrack(i + 1, curStr + String(c)) + } + } + } + + backtrack(0, "") + return res + } +} +``` + ::tabs-end ### Time & Space Complexity * Time complexity: $O(n * 4 ^ n)$ -* Space complexity: $O(n)$ +* Space complexity: + * $O(n)$ extra space. + * $O(n * 4 ^ n)$ space for the output list. --- @@ -429,9 +462,39 @@ class Solution { } ``` +```swift +class Solution { + func letterCombinations(_ digits: String) -> [String] { + guard !digits.isEmpty else { return [] } + + let digitToChar: [Character: String] = [ + "2": "abc", "3": "def", "4": "ghi", "5": "jkl", + "6": "mno", "7": "pqrs", "8": "tuv", "9": "wxyz" + ] + + var res = [""] + + for digit in digits { + guard let letters = digitToChar[digit] else { continue } + var tmp = [String]() + for curStr in res { + for c in letters { + tmp.append(curStr + String(c)) + } + } + res = tmp + } + + return res + } +} +``` + ::tabs-end ### Time & Space Complexity * Time complexity: $O(n * 4 ^ n)$ -* Space complexity: $O(n)$ \ No newline at end of file +* Space complexity: + * $O(n)$ extra space. + * $O(n * 4 ^ n)$ space for the output list. \ No newline at end of file diff --git a/articles/copy-linked-list-with-random-pointer.md b/articles/copy-linked-list-with-random-pointer.md index 2c8acaed9..0b595bf64 100644 --- a/articles/copy-linked-list-with-random-pointer.md +++ b/articles/copy-linked-list-with-random-pointer.md @@ -1,4 +1,4 @@ -## 1. Hash Map (Recursion) +## 1. Recursion + Hash Map ::tabs-start @@ -223,6 +223,44 @@ class Solution { } ``` +```swift +/** + * Definition for a Node. + * public class Node { + * public var val: Int + * public var next: Node? + * public var random: Node? + * public init(_ val: Int) { + * self.val = val + * self.next = nil + * self.random = nil + * } + * } + */ + +class Solution { + private var map = [Node: Node]() + + func copyRandomList(_ head: Node?) -> Node? { + if head == nil { + return nil + } + + if let copied = map[head!] { + return copied + } + + let copy = Node(head!.val) + map[head!] = copy + + copy.next = copyRandomList(head!.next) + copy.random = copyRandomList(head!.random) + + return copy + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -494,6 +532,45 @@ class Solution { } ``` +```swift +/** + * Definition for a Node. + * public class Node { + * public var val: Int + * public var next: Node? + * public var random: Node? + * public init(_ val: Int) { + * self.val = val + * self.next = nil + * self.random = nil + * } + * } + */ + +class Solution { + func copyRandomList(_ head: Node?) -> Node? { + var oldToCopy: [Node?: Node?] = [nil: nil] + + var cur = head + while cur != nil { + let copy = Node(cur!.val) + oldToCopy[cur] = copy + cur = cur?.next + } + + cur = head + while cur != nil { + let copy = oldToCopy[cur]! + copy?.next = oldToCopy[cur?.next]! + copy?.random = oldToCopy[cur?.random]! + cur = cur?.next + } + + return oldToCopy[head]! + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -789,6 +866,46 @@ class Solution { } ``` +```swift +/** + * Definition for a Node. + * public class Node { + * public var val: Int + * public var next: Node? + * public var random: Node? + * public init(_ val: Int) { + * self.val = val + * self.next = nil + * self.random = nil + * } + * } + */ + +class Solution { + func copyRandomList(_ head: Node?) -> Node? { + var oldToCopy = [Node?: Node?]() + + func getNode(_ node: Node?) -> Node? { + if node == nil { return nil } + if oldToCopy[node] == nil { + oldToCopy[node] = Node(0) + } + return oldToCopy[node]! + } + + var cur = head + while cur != nil { + getNode(cur)!.val = cur!.val + getNode(cur)!.next = getNode(cur!.next) + getNode(cur)!.random = getNode(cur!.random) + cur = cur!.next + } + + return getNode(head) + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -1161,12 +1278,67 @@ class Solution { } ``` +```swift +/** + * Definition for a Node. + * public class Node { + * public var val: Int + * public var next: Node? + * public var random: Node? + * public init(_ val: Int) { + * self.val = val + * self.next = nil + * self.random = nil + * } + * } + */ + +class Solution { + func copyRandomList(_ head: Node?) -> Node? { + if head == nil { + return nil + } + + var l1 = head + while l1 != nil { + let l2 = Node(l1!.val) + l2.next = l1?.next + l1?.next = l2 + l1 = l2.next + } + + let newHead = head?.next + l1 = head + while l1 != nil { + if let random = l1?.random { + l1?.next?.random = random.next + } + l1 = l1?.next?.next + } + + l1 = head + while l1 != nil { + let l2 = l1?.next + l1?.next = l2?.next + if l2?.next != nil { + l2?.next = l2?.next?.next + } + l1 = l1?.next + } + + return newHead + } +} +``` + ::tabs-end ### Time & Space Complexity * Time complexity: $O(n)$ -* Space complexity: $O(1)$ +* Space complexity: + * $O(1)$ extra space. + * $O(n)$ for the output. --- @@ -1525,9 +1697,62 @@ class Solution { } ``` +```swift +/** + * Definition for a Node. + * public class Node { + * public var val: Int + * public var next: Node? + * public var random: Node? + * public init(_ val: Int) { + * self.val = val + * self.next = nil + * self.random = nil + * } + * } + */ + +class Solution { + func copyRandomList(_ head: Node?) -> Node? { + if head == nil { + return nil + } + + var l1 = head + while l1 != nil { + let l2 = Node(l1!.val) + l2.next = l1?.random + l1?.random = l2 + l1 = l1?.next + } + + let newHead = head?.random + + l1 = head + while l1 != nil { + let l2 = l1?.random + l2?.random = l2?.next?.random + l1 = l1?.next + } + + l1 = head + while l1 != nil { + let l2 = l1?.random + l1?.random = l2?.next + l2?.next = l1?.next?.random + l1 = l1?.next + } + + return newHead + } +} +``` + ::tabs-end ### Time & Space Complexity * Time complexity: $O(n)$ -* Space complexity: $O(1)$ \ No newline at end of file +* Space complexity: + * $O(1)$ extra space. + * $O(n)$ for the output. \ No newline at end of file diff --git a/articles/count-connected-components.md b/articles/count-connected-components.md index 0f0dc28ba..c31580864 100644 --- a/articles/count-connected-components.md +++ b/articles/count-connected-components.md @@ -229,6 +229,41 @@ class Solution { } ``` +```swift +class Solution { + func countComponents(_ n: Int, _ edges: [[Int]]) -> Int { + var adj = Array(repeating: [Int](), count: n) + var visit = Array(repeating: false, count: n) + + for edge in edges { + let u = edge[0], v = edge[1] + adj[u].append(v) + adj[v].append(u) + } + + func dfs(_ node: Int) { + for nei in adj[node] { + if !visit[nei] { + visit[nei] = true + dfs(nei) + } + } + } + + var res = 0 + for node in 0.. Int { + var adj = Array(repeating: [Int](), count: n) + var visit = Array(repeating: false, count: n) + for edge in edges { + let u = edge[0], v = edge[1] + adj[u].append(v) + adj[v].append(u) + } + + func bfs(_ node: Int) { + var q = Deque() + q.append(node) + visit[node] = true + while !q.isEmpty { + let cur = q.removeFirst() + for nei in adj[cur] { + if !visit[nei] { + visit[nei] = true + q.append(nei) + } + } + } + } + + var res = 0 + for node in 0.. Int { + var cur = node + while cur != parent[cur] { + parent[cur] = parent[parent[cur]] + cur = parent[cur] + } + return cur + } + + func union(_ u: Int, _ v: Int) -> Bool { + let pu = find(u) + let pv = find(v) + if pu == pv { return false } + var rootU = pu + var rootV = pv + if rank[rootV] > rank[rootU] { + swap(&rootU, &rootV) + } + parent[rootV] = rootU + rank[rootU] += rank[rootV] + return true + } +} + +class Solution { + func countComponents(_ n: Int, _ edges: [[Int]]) -> Int { + let dsu = DSU(n) + var res = n + for edge in edges { + let u = edge[0], v = edge[1] + if dsu.union(u, v) { + res -= 1 + } + } + return res + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/count-good-nodes-in-binary-tree.md b/articles/count-good-nodes-in-binary-tree.md index 1eda1a860..d9356f32c 100644 --- a/articles/count-good-nodes-in-binary-tree.md +++ b/articles/count-good-nodes-in-binary-tree.md @@ -250,6 +250,39 @@ class Solution { } ``` +```swift +/** + * Definition for a binary tree node. + * public class TreeNode { + * public var val: Int + * public var left: TreeNode? + * public var right: TreeNode? + * public init() { self.val = 0; self.left = nil; self.right = nil; } + * public init(_ val: Int) { self.val = val; self.left = nil; self.right = nil; } + * public init(_ val: Int, _ left: TreeNode?, _ right: TreeNode?) { + * self.val = val + * self.left = left + * self.right = right + * } + * } + */ +class Solution { + func goodNodes(_ root: TreeNode?) -> Int { + func dfs(_ node: TreeNode?, _ maxVal: Int) -> Int { + guard let node = node else { return 0 } + + var res = node.val >= maxVal ? 1 : 0 + let newMaxVal = max(maxVal, node.val) + res += dfs(node.left, newMaxVal) + res += dfs(node.right, newMaxVal) + return res + } + + return dfs(root, root?.val ?? Int.min) + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -545,6 +578,51 @@ class Solution { } ``` +```swift +/** + * Definition for a binary tree node. + * public class TreeNode { + * public var val: Int + * public var left: TreeNode? + * public var right: TreeNode? + * public init() { self.val = 0; self.left = nil; self.right = nil; } + * public init(_ val: Int) { self.val = val; self.left = nil; self.right = nil; } + * public init(_ val: Int, _ left: TreeNode?, _ right: TreeNode?) { + * self.val = val + * self.left = left + * self.right = right + * } + * } + */ +class Solution { + func goodNodes(_ root: TreeNode?) -> Int { + guard let root = root else { return 0 } + + var res = 0 + var q = Deque<(TreeNode, Int)>() + q.append((root, Int.min)) + + while !q.isEmpty { + let (node, maxVal) = q.popFirst()! + if node.val >= maxVal { + res += 1 + } + + let newMaxVal = max(maxVal, node.val) + + if let left = node.left { + q.append((left, newMaxVal)) + } + if let right = node.right { + q.append((right, newMaxVal)) + } + } + + return res + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/count-number-of-islands.md b/articles/count-number-of-islands.md index cee406700..042d79082 100644 --- a/articles/count-number-of-islands.md +++ b/articles/count-number-of-islands.md @@ -236,6 +236,40 @@ class Solution { } ``` +```swift +class Solution { + func numIslands(_ grid: [[Character]]) -> Int { + let directions = [[1, 0], [-1, 0], [0, 1], [0, -1]] + let ROWS = grid.count + let COLS = grid[0].count + var islands = 0 + var grid = grid + + func dfs(_ r: Int, _ c: Int) { + if r < 0 || c < 0 || r >= ROWS || c >= COLS || grid[r][c] == "0" { + return + } + + grid[r][c] = "0" + for dir in directions { + dfs(r + dir[0], c + dir[1]) + } + } + + for r in 0.. Int { + let directions = [[1, 0], [-1, 0], [0, 1], [0, -1]] + let ROWS = grid.count + let COLS = grid[0].count + var islands = 0 + var grid = grid + + func bfs(_ r: Int, _ c: Int) { + var queue = Deque<(Int, Int)>() + grid[r][c] = "0" + queue.append((r, c)) + + while !queue.isEmpty { + let (row, col) = queue.popFirst()! + for dir in directions { + let nr = row + dir[0] + let nc = col + dir[1] + if nr < 0 || nc < 0 || nr >= ROWS || nc >= COLS || grid[nr][nc] == "0" { + continue + } + queue.append((nr, nc)) + grid[nr][nc] = "0" + } + } + } + + for r in 0.. Int { + if parent[node] != node { + parent[node] = find(parent[node]) + } + return parent[node] + } + + func union(_ u: Int, _ v: Int) -> Bool { + let pu = find(u) + let pv = find(v) + if pu == pv { + return false + } + if size[pu] >= size[pv] { + size[pu] += size[pv] + parent[pv] = pu + } else { + size[pv] += size[pu] + parent[pu] = pv + } + return true + } +} + +class Solution { + func numIslands(_ grid: [[Character]]) -> Int { + let ROWS = grid.count + let COLS = grid[0].count + let dsu = DSU(ROWS * COLS) + + func index(_ r: Int, _ c: Int) -> Int { + return r * COLS + c + } + + let directions = [(1, 0), (-1, 0), (0, 1), (0, -1)] + var islands = 0 + var grid = grid + + for r in 0..= ROWS || nc >= COLS || grid[nr][nc] == "0" { + continue + } + if dsu.union(index(r, c), index(nr, nc)) { + islands -= 1 + } + } + } + } + } + + return islands + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/count-paths.md b/articles/count-paths.md index 61ab8e148..5db166df2 100644 --- a/articles/count-paths.md +++ b/articles/count-paths.md @@ -121,6 +121,24 @@ class Solution { } ``` +```swift +class Solution { + func uniquePaths(_ m: Int, _ n: Int) -> Int { + func dfs(_ i: Int, _ j: Int) -> Int { + if i == m - 1 && j == n - 1 { + return 1 + } + if i >= m || j >= n { + return 0 + } + return dfs(i, j + 1) + dfs(i + 1, j) + } + + return dfs(0, 0) + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -304,6 +322,31 @@ class Solution { } ``` +```swift +class Solution { + func uniquePaths(_ m: Int, _ n: Int) -> Int { + var memo = Array(repeating: Array(repeating: -1, count: n), count: m) + + func dfs(_ i: Int, _ j: Int) -> Int { + if i == m - 1 && j == n - 1 { + return 1 + } + if i >= m || j >= n { + return 0 + } + if memo[i][j] != -1 { + return memo[i][j] + } + + memo[i][j] = dfs(i, j + 1) + dfs(i + 1, j) + return memo[i][j] + } + + return dfs(0, 0) + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -442,6 +485,23 @@ class Solution { } ``` +```swift +class Solution { + func uniquePaths(_ m: Int, _ n: Int) -> Int { + var dp = Array(repeating: Array(repeating: 0, count: n + 1), count: m + 1) + dp[m - 1][n - 1] = 1 + + for i in stride(from: m - 1, through: 0, by: -1) { + for j in stride(from: n - 1, through: 0, by: -1) { + dp[i][j] += dp[i + 1][j] + dp[i][j + 1] + } + } + + return dp[0][0] + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -585,6 +645,23 @@ class Solution { } ``` +```swift +class Solution { + func uniquePaths(_ m: Int, _ n: Int) -> Int { + var row = Array(repeating: 1, count: n) + + for _ in 0..<(m - 1) { + var newRow = Array(repeating: 1, count: n) + for j in stride(from: n - 2, through: 0, by: -1) { + newRow[j] = newRow[j + 1] + row[j] + } + row = newRow + } + return row[0] + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -716,6 +793,22 @@ class Solution { } ``` +```swift +class Solution { + func uniquePaths(_ m: Int, _ n: Int) -> Int { + var dp = Array(repeating: 1, count: n) + + for _ in stride(from: m - 2, through: 0, by: -1) { + for j in stride(from: n - 2, through: 0, by: -1) { + dp[j] += dp[j + 1] + } + } + + return dp[0] + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -896,6 +989,30 @@ class Solution { } ``` +```swift +class Solution { + func uniquePaths(_ m: Int, _ n: Int) -> Int { + if m == 1 || n == 1 { + return 1 + } + var m = m, n = n + if m < n { + swap(&m, &n) + } + + var res = 1 + var j = 1 + for i in m..<(m + n - 1) { + res *= i + res /= j + j += 1 + } + + return res + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/count-squares.md b/articles/count-squares.md index 6b27ebb50..1b5defe8c 100644 --- a/articles/count-squares.md +++ b/articles/count-squares.md @@ -240,6 +240,40 @@ class CountSquares() { } ``` +```swift +class CountSquares { + private var ptsCount: [String: Int] + private var pts: [[Int]] + + init() { + self.ptsCount = [:] + self.pts = [] + } + + func add(_ point: [Int]) { + let key = "\(point[0]),\(point[1])" + ptsCount[key, default: 0] += 1 + pts.append(point) + } + + func count(_ point: [Int]) -> Int { + var res = 0 + let px = point[0], py = point[1] + + for pt in pts { + let x = pt[0], y = pt[1] + if abs(py - y) != abs(px - x) || x == px || y == py { + continue + } + let key1 = "\(x),\(py)" + let key2 = "\(px),\(y)" + res += (ptsCount[key1] ?? 0) * (ptsCount[key2] ?? 0) + } + return res + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -320,10 +354,9 @@ public class CountSquares { ```cpp class CountSquares { unordered_map> ptsCount; + public: - CountSquares() { - - } + CountSquares() {} void add(vector point) { ptsCount[point[0]][point[1]]++; @@ -531,6 +564,40 @@ class CountSquares { } ``` +```swift +class CountSquares { + var ptsCount: [Int: [Int: Int]] + + init() { + ptsCount = [:] + } + + func add(_ point: [Int]) { + let x = point[0] + let y = point[1] + ptsCount[x, default: [:]][y, default: 0] += 1 + } + + func count(_ point: [Int]) -> Int { + var res = 0 + let x1 = point[0] + let y1 = point[1] + + if let yMap = ptsCount[x1] { + for (y2, countXY2) in yMap { + let side = y2 - y1 + if side == 0 { continue } + let x3 = x1 + side + let x4 = x1 - side + res += countXY2 * (ptsCount[x3]?[y1] ?? 0) * (ptsCount[x3]?[y2] ?? 0) + res += countXY2 * (ptsCount[x4]?[y1] ?? 0) * (ptsCount[x4]?[y2] ?? 0) + } + } + return res + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/count-subsequences.md b/articles/count-subsequences.md index 04fd77389..241876a84 100644 --- a/articles/count-subsequences.md +++ b/articles/count-subsequences.md @@ -181,6 +181,29 @@ class Solution { } ``` +```swift +class Solution { + func numDistinct(_ s: String, _ t: String) -> Int { + let sArray = Array(s), tArray = Array(t) + let sLen = sArray.count, tLen = tArray.count + if tLen > sLen { return 0 } + + func dfs(_ i: Int, _ j: Int) -> Int { + if j == tLen { return 1 } + if i == sLen { return 0 } + + var res = dfs(i + 1, j) + if sArray[i] == tArray[j] { + res += dfs(i + 1, j + 1) + } + return res + } + + return dfs(0, 0) + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -188,6 +211,8 @@ class Solution { * Time complexity: $O(2 ^ m)$ * Space complexity: $O(m)$ +> Where $m$ is the length of the string $s$. + --- ## 2. Dynamic Programming (Top-Down) @@ -396,6 +421,33 @@ class Solution { } ``` +```swift +class Solution { + func numDistinct(_ s: String, _ t: String) -> Int { + let sArray = Array(s), tArray = Array(t) + let sLen = sArray.count, tLen = tArray.count + if tLen > sLen { return 0 } + + var dp = [[Int]: Int]() + + func dfs(_ i: Int, _ j: Int) -> Int { + if j == tLen { return 1 } + if i == sLen { return 0 } + if let val = dp[[i, j]] { return val } + + var res = dfs(i + 1, j) + if sArray[i] == tArray[j] { + res += dfs(i + 1, j + 1) + } + dp[[i, j]] = res + return res + } + + return dfs(0, 0) + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -458,7 +510,7 @@ class Solution { public: int numDistinct(string s, string t) { int m = s.length(), n = t.length(); - vector> dp(m + 1, vector(n + 1, 0)); + vector> dp(m + 1, vector(n + 1, 0)); for (int i = 0; i <= m; i++) { dp[i][n] = 1; @@ -582,6 +634,42 @@ class Solution { } ``` +```swift +class Solution { + func numDistinct(_ s: String, _ t: String) -> Int { + let m = s.count, n = t.count + var dp = Array( + repeating: Array(repeating: 0, count: n + 1), + count: m + 1 + ) + + let sArray = Array(s) + let tArray = Array(t) + + for i in 0...m { + dp[i][n] = 1 + } + + for i in stride(from: m - 1, through: 0, by: -1) { + for j in stride(from: n - 1, through: 0, by: -1) { + let base = dp[i + 1][j] + dp[i][j] = base + if sArray[i] == tArray[j] { + let addend = dp[i + 1][j + 1] + if base > Int.max - addend { + dp[i][j] = 0 + } else { + dp[i][j] += addend + } + } + } + } + + return dp[0][0] + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -643,8 +731,8 @@ class Solution { public: int numDistinct(string s, string t) { int m = s.size(), n = t.size(); - vector dp(n + 1, 0); - vector nextDp(n + 1, 0); + vector dp(n + 1, 0); + vector nextDp(n + 1, 0); dp[n] = nextDp[n] = 1; for (int i = m - 1; i >= 0; i--) { @@ -762,6 +850,38 @@ class Solution { } ``` +```swift +class Solution { + func numDistinct(_ s: String, _ t: String) -> Int { + let m = s.count, n = t.count + var dp = [Int](repeating: 0, count: n + 1) + var nextDp = [Int](repeating: 0, count: n + 1) + dp[n] = 1 + nextDp[n] = 1 + + let sArr = Array(s) + let tArr = Array(t) + + for i in stride(from: m - 1, through: 0, by: -1) { + for j in stride(from: n - 1, through: 0, by: -1) { + nextDp[j] = dp[j] + if sArr[i] == tArr[j] { + let addend = dp[j + 1] + if nextDp[j] > Int.max - addend { + nextDp[j] = 0 + } else { + nextDp[j] += addend + } + } + } + dp = nextDp + } + + return dp[0] + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -827,13 +947,13 @@ class Solution { public: int numDistinct(string s, string t) { int m = s.size(), n = t.size(); - vector dp(n + 1, 0); + vector dp(n + 1, 0); dp[n] = 1; for (int i = m - 1; i >= 0; i--) { int prev = 1; for (int j = n - 1; j >= 0; j--) { - int res = dp[j]; + uint res = dp[j]; if (s[i] == t[j]) { res += prev; } @@ -950,6 +1070,37 @@ class Solution { } ``` +```swift +class Solution { + func numDistinct(_ s: String, _ t: String) -> Int { + let m = s.count, n = t.count + var dp = [Int](repeating: 0, count: n + 1) + dp[n] = 1 + let sArr = Array(s) + let tArr = Array(t) + + for i in stride(from: m - 1, through: 0, by: -1) { + var prev = 1 + for j in stride(from: n - 1, through: 0, by: -1) { + let base = dp[j] + var res = base + if sArr[i] == tArr[j] { + if base > Int.max - prev { + res = 0 + } else { + res += prev + } + } + prev = dp[j] + dp[j] = res + } + } + + return dp[0] + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/counting-bits.md b/articles/counting-bits.md index ac8139aba..66b5db885 100644 --- a/articles/counting-bits.md +++ b/articles/counting-bits.md @@ -120,6 +120,24 @@ class Solution { } ``` +```swift +class Solution { + func countBits(_ n: Int) -> [Int] { + var res = [Int]() + for num in 0...n { + var one = 0 + for i in 0..<32 { + if num & (1 << i) != 0 { + one += 1 + } + } + res.append(one) + } + return res + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -246,6 +264,22 @@ class Solution { } ``` +```swift +class Solution { + func countBits(_ n: Int) -> [Int] { + var res = [Int](repeating: 0, count: n + 1) + for i in 1..<(n + 1) { + var num = i + while num != 0 { + res[i] += 1 + num &= (num - 1) + } + } + return res + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -338,6 +372,18 @@ class Solution { } ``` +```swift +class Solution { + func countBits(_ n: Int) -> [Int] { + var res = [Int](repeating: 0, count: n + 1) + for num in 1..<(n + 1) { + res[num] = num.nonzeroBitCount + } + return res + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -471,6 +517,23 @@ class Solution { } ``` +```swift +class Solution { + func countBits(_ n: Int) -> [Int] { + var dp = [Int](repeating: 0, count: n + 1) + var offset = 1 + + for i in 1..<(n + 1) { + if offset * 2 == i { + offset = i + } + dp[i] = 1 + dp[i - offset] + } + return dp + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -570,6 +633,18 @@ class Solution { } ``` +```swift +class Solution { + func countBits(_ n: Int) -> [Int] { + var dp = [Int](repeating: 0, count: n + 1) + for i in 0..<(n + 1) { + dp[i] = dp[i >> 1] + (i & 1) + } + return dp + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/course-schedule-ii.md b/articles/course-schedule-ii.md index 7980e9e5e..6a3e85924 100644 --- a/articles/course-schedule-ii.md +++ b/articles/course-schedule-ii.md @@ -329,6 +329,51 @@ class Solution { } ``` +```swift +class Solution { + func findOrder(_ numCourses: Int, _ prerequisites: [[Int]]) -> [Int] { + var prereq = [Int: [Int]]() + for c in 0..() + var cycle = Set() + + func dfs(_ crs: Int) -> Bool { + if cycle.contains(crs) { + return false + } + if visit.contains(crs) { + return true + } + + cycle.insert(crs) + for pre in prereq[crs]! { + if !dfs(pre) { + return false + } + } + cycle.remove(crs) + visit.insert(crs) + output.append(crs) + return true + } + + for c in 0.. [Int] { + var indegree = Array(repeating: 0, count: numCourses) + var adj = Array(repeating: [Int](), count: numCourses) + + for pair in prerequisites { + let src = pair[0] + let dst = pair[1] + indegree[dst] += 1 + adj[src].append(dst) + } + + var queue = Deque() + for n in 0.. [Int] { + var adj = Array(repeating: [Int](), count: numCourses) + var indegree = Array(repeating: 0, count: numCourses) + + for pair in prerequisites { + let nxt = pair[0] + let pre = pair[1] + indegree[nxt] += 1 + adj[pre].append(nxt) + } + + var output = [Int]() + + func dfs(_ node: Int) { + output.append(node) + indegree[node] -= 1 + for nei in adj[node] { + indegree[nei] -= 1 + if indegree[nei] == 0 { + dfs(nei) + } + } + } + + for i in 0.. Bool { + // Map each course to its prerequisites + var preMap = [Int: [Int]]() + for i in 0..() + + func dfs(_ crs: Int) -> Bool { + if visiting.contains(crs) { + // Cycle detected + return false + } + if preMap[crs]!.isEmpty { + return true + } + + visiting.insert(crs) + for pre in preMap[crs]! { + if !dfs(pre) { + return false + } + } + visiting.remove(crs) + preMap[crs] = [] + return true + } + + for c in 0.. Bool { + var indegree = Array(repeating: 0, count: numCourses) + var adj = Array(repeating: [Int](), count: numCourses) + + for pair in prerequisites { + let src = pair[0] + let dst = pair[1] + indegree[dst] += 1 + adj[src].append(dst) + } + + var queue = Deque() + for n in 0.. [Int] { + let n = temperatures.count + var res = [Int]() + + for i in 0.. temperatures[i] { + break + } + j += 1 + count += 1 + } + count = (j == n) ? 0 : count + res.append(count) + } + return res + } +} +``` + ::tabs-end ### Time & Space Complexity * Time complexity: $O(n ^ 2)$ -* Space complexity: $O(1)$ +* Space complexity: + * $O(1)$ extra space. + * $O(n)$ space for the output array. --- @@ -323,6 +349,24 @@ class Solution { } ``` +```swift +class Solution { + func dailyTemperatures(_ temperatures: [Int]) -> [Int] { + var res = [Int](repeating: 0, count: temperatures.count) + var stack = [(Int, Int)]() // Pair: (temperature, index) + + for (i, t) in temperatures.enumerated() { + while !stack.isEmpty && t > stack.last!.0 { + let (stackT, stackInd) = stack.removeLast() + res[stackInd] = i - stackInd + } + stack.append((t, i)) + } + return res + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -508,9 +552,36 @@ class Solution { } ``` +```swift +class Solution { + func dailyTemperatures(_ temperatures: [Int]) -> [Int] { + let n = temperatures.count + var res = [Int](repeating: 0, count: n) + + for i in stride(from: n - 2, through: 0, by: -1) { + var j = i + 1 + while j < n && temperatures[j] <= temperatures[i] { + if res[j] == 0 { + j = n + break + } + j += res[j] + } + + if j < n { + res[i] = j - i + } + } + return res + } +} +``` + ::tabs-end ### Time & Space Complexity * Time complexity: $O(n)$ -* Space complexity: $O(1)$ \ No newline at end of file +* Space complexity: + * $O(1)$ extra space. + * $O(n)$ space for the output array. \ No newline at end of file diff --git a/articles/decode-ways.md b/articles/decode-ways.md index bfda19321..c735d1d3f 100644 --- a/articles/decode-ways.md +++ b/articles/decode-ways.md @@ -163,6 +163,34 @@ class Solution { } ``` +```swift +class Solution { + func numDecodings(_ s: String) -> Int { + let chars = Array(s) + + func dfs(_ i: Int) -> Int { + if i == chars.count { + return 1 + } + if chars[i] == "0" { + return 0 + } + + var res = dfs(i + 1) + if i < chars.count - 1 { + if chars[i] == "1" || (chars[i] == "2" && chars[i + 1] < "7") { + res += dfs(i + 2) + } + } + + return res + } + + return dfs(0) + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -375,6 +403,35 @@ class Solution { } ``` +```swift +class Solution { + func numDecodings(_ s: String) -> Int { + let chars = Array(s) + var dp = [Int: Int]() + dp[chars.count] = 1 + + func dfs(_ i: Int) -> Int { + if let cached = dp[i] { + return cached + } + if chars[i] == "0" { + return 0 + } + + var res = dfs(i + 1) + if i + 1 < chars.count, + chars[i] == "1" || (chars[i] == "2" && "0123456".contains(chars[i + 1])) { + res += dfs(i + 2) + } + dp[i] = res + return res + } + + return dfs(0) + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -533,6 +590,30 @@ class Solution { } ``` +```swift +class Solution { + func numDecodings(_ s: String) -> Int { + let chars = Array(s) + var dp = [Int: Int]() + dp[chars.count] = 1 + + for i in stride(from: chars.count - 1, through: 0, by: -1) { + if chars[i] == "0" { + dp[i] = 0 + } else { + dp[i] = dp[i + 1] ?? 0 + } + + if i + 1 < chars.count, + chars[i] == "1" || (chars[i] == "2" && "0123456".contains(chars[i + 1])) { + dp[i]! += dp[i + 2] ?? 0 + } + } + return dp[0] ?? 0 + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -711,6 +792,30 @@ class Solution { } ``` +```swift +class Solution { + func numDecodings(_ s: String) -> Int { + let chars = Array(s) + var dp = 0, dp1 = 1, dp2 = 0 + + for i in stride(from: chars.count - 1, through: 0, by: -1) { + if chars[i] == "0" { + dp = 0 + } else { + dp = dp1 + } + + if i + 1 < chars.count, + chars[i] == "1" || (chars[i] == "2" && "0123456".contains(chars[i + 1])) { + dp += dp2 + } + (dp, dp1, dp2) = (0, dp, dp1) + } + return dp1 + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/depth-of-binary-tree.md b/articles/depth-of-binary-tree.md index efd76b64d..88a35751d 100644 --- a/articles/depth-of-binary-tree.md +++ b/articles/depth-of-binary-tree.md @@ -171,6 +171,30 @@ class Solution { } ``` +```swift +/** + * Definition for a binary tree node. + * public class TreeNode { + * public var val: Int + * public var left: TreeNode? + * public var right: TreeNode? + * public init() { self.val = 0; self.left = nil; self.right = nil; } + * public init(_ val: Int) { self.val = val; self.left = nil; self.right = nil; } + * public init(_ val: Int, _ left: TreeNode?, _ right: TreeNode?) { + * self.val = val + * self.left = left + * self.right = right + * } + * } + */ +class Solution { + func maxDepth(_ root: TreeNode?) -> Int { + guard let root = root else { return 0 } + return 1 + max(maxDepth(root.left), maxDepth(root.right)) + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -440,6 +464,41 @@ class Solution { } ``` +```swift +/** + * Definition for a binary tree node. + * public class TreeNode { + * public var val: Int + * public var left: TreeNode? + * public var right: TreeNode? + * public init() { self.val = 0; self.left = nil; self.right = nil; } + * public init(_ val: Int) { self.val = val; self.left = nil; self.right = nil; } + * public init(_ val: Int, _ left: TreeNode?, _ right: TreeNode?) { + * self.val = val + * self.left = left + * self.right = right + * } + * } + */ +class Solution { + func maxDepth(_ root: TreeNode?) -> Int { + var stack: [(TreeNode?, Int)] = [(root, 1)] + var res = 0 + + while !stack.isEmpty { + let (node, depth) = stack.removeLast() + + if let node = node { + res = max(res, depth) + stack.append((node.left, depth + 1)) + stack.append((node.right, depth + 1)) + } + } + return res + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -723,6 +782,47 @@ class Solution { } ``` +```swift +/** + * Definition for a binary tree node. + * public class TreeNode { + * public var val: Int + * public var left: TreeNode? + * public var right: TreeNode? + * public init() { self.val = 0; self.left = nil; self.right = nil; } + * public init(_ val: Int) { self.val = val; self.left = nil; self.right = nil; } + * public init(_ val: Int, _ left: TreeNode?, _ right: TreeNode?) { + * self.val = val + * self.left = left + * self.right = right + * } + * } + */ +class Solution { + func maxDepth(_ root: TreeNode?) -> Int { + var queue = Deque() + if let root = root { + queue.append(root) + } + + var level = 0 + while !queue.isEmpty { + for _ in 0..] + private var tweetMap: [Int: [(Int, Int)]] + + init() { + self.time = 0 + self.followMap = [:] + self.tweetMap = [:] + } + + func postTweet(_ userId: Int, _ tweetId: Int) { + if tweetMap[userId] == nil { + tweetMap[userId] = [] + } + tweetMap[userId]!.append((time, tweetId)) + time += 1 + } + + func getNewsFeed(_ userId: Int) -> [Int] { + var feed = tweetMap[userId] ?? [] + if let followees = followMap[userId] { + for followeeId in followees { + if let tweets = tweetMap[followeeId] { + feed.append(contentsOf: tweets) + } + } + } + feed.sort { $0.0 > $1.0 } + return feed.prefix(10).map { $0.1 } + } + + func follow(_ followerId: Int, _ followeeId: Int) { + if followerId != followeeId { + followMap[followerId, default: Set()].insert(followeeId) + } + } + + func unfollow(_ followerId: Int, _ followeeId: Int) { + followMap[followerId]?.remove(followeeId) + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -769,6 +814,84 @@ class Twitter { } ``` +```swift +class Twitter { + private var count: Int + private var tweetMap: [Int: [(Int, Int)]] // userId -> list of (count, tweetId) + private var followMap: [Int: Set] // userId -> set of followeeId + + init() { + self.count = 0 + self.tweetMap = [:] + self.followMap = [:] + } + + func postTweet(_ userId: Int, _ tweetId: Int) { + tweetMap[userId, default: []].append((count, tweetId)) + count -= 1 + } + + func getNewsFeed(_ userId: Int) -> [Int] { + var res = [Int]() + var minHeap = Heap() + + followMap[userId, default: Set()].insert(userId) + if let followees = followMap[userId] { + for followee in followees { + if let tweets = tweetMap[followee], !tweets.isEmpty { + let index = tweets.count - 1 + let (cnt, tweetId) = tweets[index] + minHeap.insert( + Item( + count: cnt, tweetId: tweetId, + followeeId: followee, index: index - 1 + ) + ) + } + } + } + + while !minHeap.isEmpty && res.count < 10 { + let entry = minHeap.popMin()! + res.append(entry.tweetId) + if entry.index >= 0, let tweets = tweetMap[entry.followeeId] { + let (cnt, tweetId) = tweets[entry.index] + minHeap.insert( + Item( + count: cnt, tweetId: tweetId, + followeeId: entry.followeeId, index: entry.index - 1 + ) + ) + } + } + return res + } + + func follow(_ followerId: Int, _ followeeId: Int) { + followMap[followerId, default: Set()].insert(followeeId) + } + + func unfollow(_ followerId: Int, _ followeeId: Int) { + followMap[followerId]?.remove(followeeId) + } +} + +struct Item: Comparable { + let count: Int + let tweetId: Int + let followeeId: Int + let index: Int + + static func < (lhs: Item, rhs: Item) -> Bool { + return lhs.count < rhs.count + } + + static func == (lhs: Item, rhs: Item) -> Bool { + return lhs.count == rhs.count + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -1425,6 +1548,112 @@ class Twitter { } ``` +```swift +struct Item: Comparable { + let count: Int + let tweetId: Int + let followeeId: Int + let index: Int + + static func < (lhs: Item, rhs: Item) -> Bool { + return lhs.count < rhs.count + } + + static func == (lhs: Item, rhs: Item) -> Bool { + return lhs.count == rhs.count && + lhs.tweetId == rhs.tweetId && + lhs.followeeId == rhs.followeeId && + lhs.index == rhs.index + } +} + +class Twitter { + private var count: Int + private var tweetMap: [Int: [(Int, Int)]] // userId -> list of (count, tweetId) + private var followMap: [Int: Set] // userId -> set of followeeId + + init() { + self.count = 0 + self.tweetMap = [:] + self.followMap = [:] + } + + func postTweet(_ userId: Int, _ tweetId: Int) { + tweetMap[userId, default: []].append((count, tweetId)) + if tweetMap[userId]!.count > 10 { + tweetMap[userId]!.removeFirst() + } + count -= 1 + } + + func getNewsFeed(_ userId: Int) -> [Int] { + var res = [Int]() + var minHeap = Heap() + followMap[userId, default: Set()].insert(userId) + + if followMap[userId]!.count >= 10 { + var maxHeap = Heap() + for followeeId in followMap[userId]! { + if let tweets = tweetMap[followeeId], !tweets.isEmpty { + let index = tweets.count - 1 + let (cnt, tweetId) = tweets[index] + maxHeap.insert( + Item( + count: cnt, tweetId: tweetId, + followeeId: followeeId, index: index - 1 + ) + ) + if maxHeap.count > 10 { + maxHeap.removeMax() + } + } + } + while !maxHeap.isEmpty { + let item = maxHeap.popMax()! + minHeap.insert(item) + } + } else { + for followeeId in followMap[userId]! { + if let tweets = tweetMap[followeeId], !tweets.isEmpty { + let index = tweets.count - 1 + let (cnt, tweetId) = tweets[index] + minHeap.insert( + Item( + count: cnt, tweetId: tweetId, + followeeId: followeeId, index: index - 1 + ) + ) + } + } + } + + while !minHeap.isEmpty && res.count < 10 { + let item = minHeap.popMin()! + res.append(item.tweetId) + if item.index >= 0, let tweets = tweetMap[item.followeeId] { + let (cnt, tweetId) = tweets[item.index] + minHeap.insert( + Item( + count: cnt, tweetId: tweetId, + followeeId: item.followeeId, index: item.index - 1 + ) + ) + } + } + + return res + } + + func follow(_ followerId: Int, _ followeeId: Int) { + followMap[followerId, default: Set()].insert(followeeId) + } + + func unfollow(_ followerId: Int, _ followeeId: Int) { + followMap[followerId]?.remove(followeeId) + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/design-word-search-data-structure.md b/articles/design-word-search-data-structure.md index 501e42785..033096234 100644 --- a/articles/design-word-search-data-structure.md +++ b/articles/design-word-search-data-structure.md @@ -222,6 +222,42 @@ class WordDictionary { } ``` +```swift +class WordDictionary { + private var store: [String] + + init() { + self.store = [] + } + + func addWord(_ word: String) { + store.append(word) + } + + func search(_ word: String) -> Bool { + for w in store { + if w.count != word.count { + continue + } + var i = 0 + let wArray = Array(w) + let wordArray = Array(word) + while i < wArray.count { + if wArray[i] == wordArray[i] || wordArray[i] == "." { + i += 1 + } else { + break + } + } + if i == wArray.count { + return true + } + } + return false + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -627,6 +663,64 @@ class WordDictionary { } ``` +```swift +class TrieNode { + var children: [Character: TrieNode] + var word: Bool + + init() { + self.children = [:] + self.word = false + } +} + +class WordDictionary { + private let root: TrieNode + + init() { + self.root = TrieNode() + } + + func addWord(_ word: String) { + var cur = root + for c in word { + if cur.children[c] == nil { + cur.children[c] = TrieNode() + } + cur = cur.children[c]! + } + cur.word = true + } + + func search(_ word: String) -> Bool { + func dfs(_ j: Int, _ root: TrieNode) -> Bool { + var cur = root + let wordArray = Array(word) + + for i in j.. Bool { + for i in 0.. Bool { + var nums = nums.sorted() + for i in 1.. Bool { + var seen = Set() + for num in nums { + if seen.contains(num) { + return true + } + seen.insert(num) + } + return false + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -406,6 +450,14 @@ class Solution { } ``` +```swift +class Solution { + func hasDuplicate(_ nums: [Int]) -> Bool { + return Set(nums).count < nums.count + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/eating-bananas.md b/articles/eating-bananas.md index bf00f6c04..4acd82fb0 100644 --- a/articles/eating-bananas.md +++ b/articles/eating-bananas.md @@ -137,6 +137,27 @@ class Solution { } ``` +```swift +class Solution { + func minEatingSpeed(_ piles: [Int], _ h: Int) -> Int { + var speed = 1 + + while true { + var totalTime = 0 + for pile in piles { + totalTime += Int(ceil(Double(pile) / Double(speed))) + } + + if totalTime <= h { + return speed + } + speed += 1 + } + return speed + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -338,6 +359,32 @@ class Solution { } ``` +```swift +class Solution { + func minEatingSpeed(_ piles: [Int], _ h: Int) -> Int { + var l = 1, r = piles.max()! + var res = r + + while l <= r { + let k = (l + r) / 2 + + var totalTime = 0 + for p in piles { + totalTime += Int(ceil(Double(p) / Double(k))) + } + + if totalTime <= h { + res = k + r = k - 1 + } else { + l = k + 1 + } + } + return res + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/edit-distance.md b/articles/edit-distance.md index a116df5c7..7c4b6bfeb 100644 --- a/articles/edit-distance.md +++ b/articles/edit-distance.md @@ -169,6 +169,27 @@ class Solution { } ``` +```swift +class Solution { + func minDistance(_ word1: String, _ word2: String) -> Int { + let m = word1.count, n = word2.count + let word1Array = Array(word1), word2Array = Array(word2) + + func dfs(_ i: Int, _ j: Int) -> Int { + if i == m { return n - j } + if j == n { return m - i } + if word1Array[i] == word2Array[j] { + return dfs(i + 1, j + 1) + } + let res = min(dfs(i + 1, j), dfs(i, j + 1)) + return min(res, dfs(i + 1, j + 1)) + 1 + } + + return dfs(0, 0) + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -403,6 +424,32 @@ class Solution { } ``` +```swift +class Solution { + func minDistance(_ word1: String, _ word2: String) -> Int { + let m = word1.count, n = word2.count + let word1Array = Array(word1), word2Array = Array(word2) + var dp = [[Int?]](repeating: [Int?](repeating: nil, count: n + 1), count: m + 1) + + func dfs(_ i: Int, _ j: Int) -> Int { + if i == m { return n - j } + if j == n { return m - i } + if let val = dp[i][j] { return val } + + if word1Array[i] == word2Array[j] { + dp[i][j] = dfs(i + 1, j + 1) + } else { + let res = min(dfs(i + 1, j), dfs(i, j + 1), dfs(i + 1, j + 1)) + 1 + dp[i][j] = res + } + return dp[i][j]! + } + + return dfs(0, 0) + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -624,6 +671,34 @@ class Solution { } ``` +```swift +class Solution { + func minDistance(_ word1: String, _ word2: String) -> Int { + let m = word1.count, n = word2.count + let word1Array = Array(word1), word2Array = Array(word2) + var dp = [[Int]](repeating: [Int](repeating: Int.max, count: n + 1), count: m + 1) + + for j in 0...n { + dp[m][j] = n - j + } + for i in 0...m { + dp[i][n] = m - i + } + + for i in stride(from: m - 1, through: 0, by: -1) { + for j in stride(from: n - 1, through: 0, by: -1) { + if word1Array[i] == word2Array[j] { + dp[i][j] = dp[i + 1][j + 1] + } else { + dp[i][j] = 1 + min(dp[i + 1][j], dp[i][j + 1], dp[i + 1][j + 1]) + } + } + } + return dp[0][0] + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -890,6 +965,41 @@ class Solution { } ``` +```swift +class Solution { + func minDistance(_ word1: String, _ word2: String) -> Int { + var m = word1.count, n = word2.count + var word1Array = Array(word1), word2Array = Array(word2) + + if m < n { + swap(&m, &n) + swap(&word1Array, &word2Array) + } + + var dp = [Int](repeating: 0, count: n + 1) + var nextDp = [Int](repeating: 0, count: n + 1) + + for j in 0...n { + dp[j] = n - j + } + + for i in stride(from: m - 1, through: 0, by: -1) { + nextDp[n] = m - i + for j in stride(from: n - 1, through: 0, by: -1) { + if word1Array[i] == word2Array[j] { + nextDp[j] = dp[j + 1] + } else { + nextDp[j] = 1 + min(dp[j], nextDp[j + 1], dp[j + 1]) + } + } + dp = nextDp + } + + return dp[0] + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -1134,6 +1244,37 @@ class Solution { } ``` +```swift +class Solution { + func minDistance(_ word1: String, _ word2: String) -> Int { + var m = word1.count, n = word2.count + var word1Array = Array(word1), word2Array = Array(word2) + + if m < n { + swap(&m, &n) + swap(&word1Array, &word2Array) + } + + var dp = (0...n).map { n - $0 } + + for i in stride(from: m - 1, through: 0, by: -1) { + var nextDp = dp[n] + dp[n] = m - i + for j in stride(from: n - 1, through: 0, by: -1) { + let temp = dp[j] + if word1Array[i] == word2Array[j] { + dp[j] = nextDp + } else { + dp[j] = 1 + min(dp[j], dp[j + 1], nextDp) + } + nextDp = temp + } + } + return dp[0] + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/evaluate-reverse-polish-notation.md b/articles/evaluate-reverse-polish-notation.md index 9c3520b02..3ad96eff8 100644 --- a/articles/evaluate-reverse-polish-notation.md +++ b/articles/evaluate-reverse-polish-notation.md @@ -216,6 +216,39 @@ class Solution { } ``` +```swift +class Solution { + func evalRPN(_ tokens: [String]) -> Int { + var tokens = tokens + + while tokens.count > 1 { + for i in 0.. Int { + var head: DoublyLinkedList? = DoublyLinkedList(val: tokens[0]) + var curr = head + + for i in 1.. Int { + var tokens = tokens + + func dfs() -> Int { + let token = tokens.removeLast() + if let num = Int(token) { + return num + } + + let right = dfs() + let left = dfs() + + switch token { + case "+": return left + right + case "-": return left - right + case "*": return left * right + case "/": return left / right + default: return 0 + } + } + + return dfs() + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -1042,6 +1161,34 @@ class Solution { } ``` +```swift +class Solution { + func evalRPN(_ tokens: [String]) -> Int { + var stack = [Int]() + + for c in tokens { + switch c { + case "+": + stack.append(stack.removeLast() + stack.removeLast()) + case "-": + let a = stack.removeLast() + let b = stack.removeLast() + stack.append(b - a) + case "*": + stack.append(stack.removeLast() * stack.removeLast()) + case "/": + let a = stack.removeLast() + let b = stack.removeLast() + stack.append(b / a) + default: + stack.append(Int(c)!) + } + } + return stack[0] + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/find-duplicate-integer.md b/articles/find-duplicate-integer.md index 46542d9b6..ef3317436 100644 --- a/articles/find-duplicate-integer.md +++ b/articles/find-duplicate-integer.md @@ -99,6 +99,20 @@ class Solution { } ``` +```swift +class Solution { + func findDuplicate(_ nums: [Int]) -> Int { + var nums = nums.sorted() + for i in 0.. Int { + var seen = Set() + for num in nums { + if seen.contains(num) { + return num + } + seen.insert(num) + } + return -1 + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -333,6 +362,21 @@ class Solution { } ``` +```swift +class Solution { + func findDuplicate(_ nums: [Int]) -> Int { + var seen = [Int](repeating: 0, count: nums.count) + for num in nums { + if seen[num - 1] == 1 { + return num + } + seen[num - 1] = 1 + } + return -1 + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -457,6 +501,22 @@ class Solution { } ``` +```swift +class Solution { + func findDuplicate(_ nums: [Int]) -> Int { + var nums = nums + for num in nums { + let idx = abs(num) - 1 + if nums[idx] < 0 { + return abs(num) + } + nums[idx] *= -1 + } + return -1 + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -656,6 +716,27 @@ class Solution { } ``` +```swift +class Solution { + func findDuplicate(_ nums: [Int]) -> Int { + let n = nums.count + var low = 1, high = n - 1 + + while low < high { + let mid = low + (high - low) / 2 + let lessOrEqual = nums.filter { $0 <= mid }.count + + if lessOrEqual <= mid { + low = mid + 1 + } else { + high = mid + } + } + return low + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -866,6 +947,37 @@ class Solution { } ``` +```swift +class Solution { + func findDuplicate(_ nums: [Int]) -> Int { + let n = nums.count + var res = 0 + + for b in 0..<32 { + var x = 0, y = 0 + let mask = 1 << b + + for num in nums { + if num & mask != 0 { + x += 1 + } + } + + for num in 1.. y { + res |= mask + } + } + return res + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -1052,6 +1164,31 @@ class Solution { } ``` +```swift +class Solution { + func findDuplicate(_ nums: [Int]) -> Int { + var slow = 0, fast = 0 + + while true { + slow = nums[slow] + fast = nums[nums[fast]] + if slow == fast { + break + } + } + + var slow2 = 0 + while true { + slow = nums[slow] + slow2 = nums[slow2] + if slow == slow2 { + return slow + } + } + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/find-median-in-a-data-stream.md b/articles/find-median-in-a-data-stream.md index ab246b370..ad27aa749 100644 --- a/articles/find-median-in-a-data-stream.md +++ b/articles/find-median-in-a-data-stream.md @@ -162,6 +162,30 @@ class MedianFinder() { } ``` +```swift +class MedianFinder { + private var data: [Int] + + init() { + self.data = [] + } + + func addNum(_ num: Int) { + data.append(num) + } + + func findMedian() -> Double { + data.sort() + let n = data.count + if n % 2 == 1 { + return Double(data[n / 2]) + } else { + return (Double(data[n / 2]) + Double(data[n / 2 - 1])) / 2.0 + } + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -447,6 +471,47 @@ class MedianFinder() { } ``` +```swift +class MedianFinder { + // two heaps, large, small, minheap, maxheap + // heaps should be equal size + private var small: Heap + private var large: Heap + + init() { + self.small = Heap() + self.large = Heap() + } + + func addNum(_ num: Int) { + if let top = large.min, num > top { + large.insert(num) + } else { + small.insert(num) + } + if small.count > large.count + 1 { + if let val = small.popMax() { + large.insert(val) + } + } + if large.count > small.count + 1 { + if let val = large.popMin() { + small.insert(val) + } + } + } + + func findMedian() -> Double { + if small.count > large.count { + return Double(small.max!) + } else if large.count > small.count { + return Double(large.min!) + } + return (Double(small.max!) + Double(large.min!)) / 2.0 + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/find-minimum-in-rotated-sorted-array.md b/articles/find-minimum-in-rotated-sorted-array.md index 3c4c59be9..9443959d2 100644 --- a/articles/find-minimum-in-rotated-sorted-array.md +++ b/articles/find-minimum-in-rotated-sorted-array.md @@ -65,6 +65,14 @@ class Solution { } ``` +```swift +class Solution { + func findMin(_ nums: [Int]) -> Int { + return nums.min()! + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -261,6 +269,32 @@ class Solution { } ``` +```swift +class Solution { + func findMin(_ nums: [Int]) -> Int { + var res = nums[0] + var l = 0, r = nums.count - 1 + + while l <= r { + if nums[l] < nums[r] { + res = min(res, nums[l]) + break + } + + let m = (l + r) / 2 + res = min(res, nums[m]) + + if nums[m] >= nums[l] { + l = m + 1 + } else { + r = m - 1 + } + } + return res + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -396,6 +430,23 @@ class Solution { } ``` +```swift +class Solution { + func findMin(_ nums: [Int]) -> Int { + var l = 0, r = nums.count - 1 + while l < r { + let m = l + (r - l) / 2 + if nums[m] < nums[r] { + r = m + } else { + l = m + 1 + } + } + return nums[l] + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/find-target-in-rotated-sorted-array.md b/articles/find-target-in-rotated-sorted-array.md index 2e6dd37e5..b406838bb 100644 --- a/articles/find-target-in-rotated-sorted-array.md +++ b/articles/find-target-in-rotated-sorted-array.md @@ -93,6 +93,19 @@ class Solution { } ``` +```swift +class Solution { + func search(_ nums: [Int], _ target: Int) -> Int { + for i in 0.. Int { + var l = 0, r = nums.count - 1 + + while l < r { + let m = (l + r) / 2 + if nums[m] > nums[r] { + l = m + 1 + } else { + r = m + } + } + + let pivot = l + + func binarySearch(_ left: Int, _ right: Int) -> Int { + var l = left, r = right + while l <= r { + let mid = (l + r) / 2 + if nums[mid] == target { + return mid + } else if nums[mid] < target { + l = mid + 1 + } else { + r = mid - 1 + } + } + return -1 + } + + let result = binarySearch(0, pivot - 1) + if result != -1 { + return result + } + + return binarySearch(pivot, nums.count - 1) + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -681,6 +735,46 @@ class Solution { } ``` +```swift +class Solution { + func search(_ nums: [Int], _ target: Int) -> Int { + var l = 0, r = nums.count - 1 + + while l < r { + let m = (l + r) / 2 + if nums[m] > nums[r] { + l = m + 1 + } else { + r = m + } + } + + let pivot = l + l = 0 + r = nums.count - 1 + + if target >= nums[pivot] && target <= nums[r] { + l = pivot + } else { + r = pivot - 1 + } + + while l <= r { + let m = (l + r) / 2 + if nums[m] == target { + return m + } else if nums[m] < target { + l = m + 1 + } else { + r = m - 1 + } + } + + return -1 + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -908,6 +1002,36 @@ class Solution { } ``` +```swift +class Solution { + func search(_ nums: [Int], _ target: Int) -> Int { + var l = 0, r = nums.count - 1 + + while l <= r { + let mid = (l + r) / 2 + if target == nums[mid] { + return mid + } + + if nums[l] <= nums[mid] { + if target > nums[mid] || target < nums[l] { + l = mid + 1 + } else { + r = mid - 1 + } + } else { + if target < nums[mid] || target > nums[r] { + r = mid - 1 + } else { + l = mid + 1 + } + } + } + return -1 + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/foreign-dictionary.md b/articles/foreign-dictionary.md index f1353b74a..1bb32e486 100644 --- a/articles/foreign-dictionary.md +++ b/articles/foreign-dictionary.md @@ -405,6 +405,65 @@ class Solution { } ``` +```swift +class Solution { + func foreignDictionary(_ words: [String]) -> String { + var adj = [Character: Set]() + for word in words { + for char in word { + if adj[char] == nil { + adj[char] = Set() + } + } + } + + for i in 0.. w2.count && String(w1.prefix(minLen)) == String(w2.prefix(minLen)) { + return "" + } + for j in 0.. Bool { + if let flag = visited[char] { + return flag + } + visited[char] = true + for neigh in adj[char]! { + if dfs(neigh) { + return true + } + } + visited[char] = false + res.append(char) + return false + } + + for char in adj.keys { + if dfs(char) { + return "" + } + } + + res.reverse() + return String(res) + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -823,6 +882,69 @@ class Solution { } ``` +```swift +class Solution { + func foreignDictionary(_ words: [String]) -> String { + var adj = [Character: Set]() + for word in words { + for char in word { + adj[char] = Set() + } + } + + var indegree = [Character: Int]() + for key in adj.keys { + indegree[key] = 0 + } + + for i in 0.. w2.count && String(w1.prefix(minLen)) == String(w2.prefix(minLen)) { + return "" + } + let w1Arr = Array(w1) + let w2Arr = Array(w2) + for j in 0..() + for (c, deg) in indegree { + if deg == 0 { + q.append(c) + } + } + + var res = [Character]() + while !q.isEmpty { + let char = q.removeFirst() + res.append(char) + for neighbor in adj[char]! { + indegree[neighbor]! -= 1 + if indegree[neighbor]! == 0 { + q.append(neighbor) + } + } + } + + if res.count != indegree.count { + return "" + } + + return String(res) + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/gas-station.md b/articles/gas-station.md index cedbf31c2..9e779b1d8 100644 --- a/articles/gas-station.md +++ b/articles/gas-station.md @@ -11,6 +11,7 @@ class Solution: tank = gas[i] - cost[i] if tank < 0: continue + j = (i + 1) % n while j != i: tank += gas[j] @@ -19,6 +20,7 @@ class Solution: break j += 1 j %= n + if j == i: return i return -1 @@ -32,12 +34,14 @@ public class Solution { for (int i = 0; i < n; i++) { int tank = gas[i] - cost[i]; if (tank < 0) continue; + int j = (i + 1) % n; while (j != i) { tank += gas[j] - cost[j]; if (tank < 0) break; j = (j + 1) % n; } + if (j == i) return i; } return -1; @@ -54,12 +58,14 @@ public: for (int i = 0; i < n; i++) { int tank = gas[i] - cost[i]; if (tank < 0) continue; + int j = (i + 1) % n; while (j != i) { tank += gas[j] - cost[j]; if (tank < 0) break; j = (j + 1) % n; } + if (j == i) return i; } return -1; @@ -80,12 +86,14 @@ class Solution { for (let i = 0; i < n; i++) { let tank = gas[i] - cost[i]; if (tank < 0) continue; + let j = (i + 1) % n; while (j !== i) { tank += gas[j] - cost[j]; if (tank < 0) break; j = (j + 1) % n; } + if (j === i) return i; } return -1; @@ -101,14 +109,17 @@ public class Solution { for (int i = 0; i < n; i++) { int tank = gas[i] - cost[i]; if (tank < 0) continue; + int j = (i + 1) % n; while (j != i) { tank += gas[j] - cost[j]; if (tank < 0) break; j = (j + 1) % n; } + if (j == i) return i; } + return -1; } } @@ -117,11 +128,13 @@ public class Solution { ```go func canCompleteCircuit(gas []int, cost []int) int { n := len(gas) + for i := 0; i < n; i++ { tank := gas[i] - cost[i] if tank < 0 { continue } + j := (i + 1) % n for j != i { tank += gas[j] @@ -131,10 +144,12 @@ func canCompleteCircuit(gas []int, cost []int) int { } j = (j + 1) % n } + if j == i { return i } } + return -1 } ``` @@ -166,6 +181,38 @@ class Solution { } ``` +```swift +class Solution { + func canCompleteCircuit(_ gas: [Int], _ cost: [Int]) -> Int { + let n = gas.count + + for i in 0.. Int { + let n = gas.count + var start = n - 1 + var end = 0 + var tank = gas[start] - cost[start] + + while start > end { + if tank < 0 { + start -= 1 + tank += gas[start] - cost[start] + } else { + tank += gas[end] - cost[end] + end += 1 + } + } + + return tank >= 0 ? start : -1 + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -510,6 +580,29 @@ class Solution { } ``` +```swift +class Solution { + func canCompleteCircuit(_ gas: [Int], _ cost: [Int]) -> Int { + if gas.reduce(0, +) < cost.reduce(0, +) { + return -1 + } + + var total = 0 + var res = 0 + for i in 0.. [String] { + var res = [String]() + + func isValid(_ s: String) -> Bool { + var open = 0 + for c in s { + open += (c == "(") ? 1 : -1 + if open < 0 { + return false + } + } + return open == 0 + } + + func dfs(_ s: String) { + if s.count == n * 2 { + if isValid(s) { + res.append(s) + } + return + } + dfs(s + "(") + dfs(s + ")") + } + + dfs("") + return res + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -442,6 +475,37 @@ class Solution { } ``` +```swift +class Solution { + func generateParenthesis(_ n: Int) -> [String] { + var stack = [Character]() + var res = [String]() + + func backtrack(_ openN: Int, _ closedN: Int) { + if openN == n && closedN == n { + res.append(String(stack)) + return + } + + if openN < n { + stack.append("(") + backtrack(openN + 1, closedN) + stack.removeLast() + } + + if closedN < openN { + stack.append(")") + backtrack(openN, closedN + 1) + stack.removeLast() + } + } + + backtrack(0, 0) + return res + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -606,6 +670,27 @@ class Solution { } ``` +```swift +class Solution { + func generateParenthesis(_ n: Int) -> [String] { + var res = [[String]](repeating: [], count: n + 1) + res[0] = [""] + + for k in 0...n { + for i in 0.. count = new HashMap<>(); for (int num : hand) { count.put(num, count.getOrDefault(num, 0) + 1); } + Arrays.sort(hand); for (int num : hand) { if (count.get(num) > 0) { @@ -45,8 +48,10 @@ class Solution { public: bool isNStraightHand(vector& hand, int groupSize) { if (hand.size() % groupSize != 0) return false; + unordered_map count; for (int num : hand) count[num]++; + sort(hand.begin(), hand.end()); for (int num : hand) { if (count[num] > 0) { @@ -72,10 +77,12 @@ class Solution { if (hand.length % groupSize !== 0) { return false; } + const count = {}; for (const num of hand) { count[num] = (count[num] || 0) + 1; } + hand.sort((a, b) => a - b); for (const num of hand) { if (count[num] > 0) { @@ -94,10 +101,12 @@ class Solution { public class Solution { public bool IsNStraightHand(int[] hand, int groupSize) { if (hand.Length % groupSize != 0) return false; + var count = new Dictionary(); foreach (var num in hand) { count[num] = count.GetValueOrDefault(num, 0) + 1; } + Array.Sort(hand); foreach (var num in hand) { if (count[num] > 0) { @@ -164,6 +173,36 @@ class Solution { } ``` +```swift +class Solution { + func isNStraightHand(_ hand: [Int], _ groupSize: Int) -> Bool { + if hand.count % groupSize != 0 { + return false + } + + var count = [Int: Int]() + for num in hand { + count[num, default: 0] += 1 + } + + let sortedHand = hand.sorted() + for num in sortedHand { + if let freq = count[num], freq > 0 { + for i in num..<(num + groupSize) { + if let f = count[i], f > 0 { + count[i] = f - 1 + } else { + return false + } + } + } + } + + return true + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -410,6 +449,40 @@ class Solution { } ``` +```swift +class Solution { + func isNStraightHand(_ hand: [Int], _ groupSize: Int) -> Bool { + if hand.count % groupSize != 0 { + return false + } + + var count = [Int: Int]() + for n in hand { + count[n, default: 0] += 1 + } + + var minH = Heap(Array(count.keys)) + + while !minH.isEmpty { + guard let first = minH.min else { return false } + + for i in first..<(first + groupSize) { + guard let freq = count[i] else { return false } + count[i] = freq - 1 + if count[i] == 0 { + if i != minH.min { + return false + } + minH.removeMin() + } + } + } + + return true + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -655,6 +728,40 @@ class Solution { } ``` +```swift +class Solution { + func isNStraightHand(_ hand: [Int], _ groupSize: Int) -> Bool { + if hand.count % groupSize != 0 { + return false + } + + var count = [Int: Int]() + for num in hand { + count[num, default: 0] += 1 + } + + var queue = Deque() + var lastNum = -1 + var openGroups = 0 + + for (num, numCount) in count.sorted(by: { $0.key < $1.key }) { + if (openGroups > 0 && num > lastNum + 1) || openGroups > numCount { + return false + } + + queue.append(numCount - openGroups) + lastNum = num + openGroups = numCount + + if queue.count == groupSize { + openGroups -= queue.removeFirst() + } + } + return openGroups == 0 + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -673,6 +780,7 @@ class Solution: def isNStraightHand(self, hand: List[int], groupSize: int) -> bool: if len(hand) % groupSize != 0: return False + count = Counter(hand) for num in hand: start = num @@ -692,6 +800,7 @@ class Solution: public class Solution { public boolean isNStraightHand(int[] hand, int groupSize) { if (hand.length % groupSize != 0) return false; + Map count = new HashMap<>(); for (int num : hand) { count.put(num, count.getOrDefault(num, 0) + 1); @@ -720,6 +829,7 @@ class Solution { public: bool isNStraightHand(vector& hand, int groupSize) { if (hand.size() % groupSize != 0) return false; + unordered_map count; for (int num : hand) count[num]++; @@ -750,6 +860,7 @@ class Solution { */ isNStraightHand(hand, groupSize) { if (hand.length % groupSize !== 0) return false; + const count = new Map(); hand.forEach(num => count.set(num, (count.get(num) || 0) + 1)); @@ -775,6 +886,7 @@ class Solution { public class Solution { public bool IsNStraightHand(int[] hand, int groupSize) { if (hand.Length % groupSize != 0) return false; + Dictionary count = new Dictionary(); foreach (int num in hand) { if (!count.ContainsKey(num)) count[num] = 0; @@ -867,6 +979,41 @@ class Solution { } ``` +```swift +class Solution { + func isNStraightHand(_ hand: [Int], _ groupSize: Int) -> Bool { + if hand.count % groupSize != 0 { + return false + } + + var count = [Int: Int]() + for num in hand { + count[num, default: 0] += 1 + } + + for num in hand { + var start = num + while (count[start - 1] ?? 0) > 0 { + start -= 1 + } + while start <= num { + while (count[start] ?? 0) > 0 { + for i in start..<(start + groupSize) { + if (count[i] ?? 0) == 0 { + return false + } + count[i]! -= 1 + } + } + start += 1 + } + } + + return true + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/house-robber-ii.md b/articles/house-robber-ii.md index 9157646c6..0469d73df 100644 --- a/articles/house-robber-ii.md +++ b/articles/house-robber-ii.md @@ -131,6 +131,25 @@ class Solution { } ``` +```swift +class Solution { + func rob(_ nums: [Int]) -> Int { + if nums.count == 1 { + return nums[0] + } + + func dfs(_ i: Int, _ flag: Bool) -> Int { + if i >= nums.count || (flag && i == nums.count - 1) { + return 0 + } + return max(dfs(i + 1, flag), nums[i] + dfs(i + 2, flag || i == 0)) + } + + return max(dfs(0, true), dfs(1, false)) + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -336,6 +355,35 @@ class Solution { } ``` +```swift +class Solution { + func rob(_ nums: [Int]) -> Int { + if nums.count == 1 { + return nums[0] + } + + var memo = Array(repeating: Array(repeating: -1, count: 2), count: nums.count) + + func dfs(_ i: Int, _ flag: Bool) -> Int { + if i >= nums.count || (flag && i == nums.count - 1) { + return 0 + } + if memo[i][flag ? 1 : 0] != -1 { + return memo[i][flag ? 1 : 0] + } + + memo[i][flag ? 1 : 0] = max( + dfs(i + 1, flag), nums[i] + dfs(i + 2, flag || i == 0) + ) + + return memo[i][flag ? 1 : 0] + } + + return max(dfs(0, true), dfs(1, false)) + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -553,6 +601,36 @@ class Solution { } ``` +```swift +class Solution { + func rob(_ nums: [Int]) -> Int { + if nums.count == 1 { + return nums[0] + } + return max(helper(Array(nums[1...])), helper(Array(nums[..<(nums.count - 1)]))) + } + + func helper(_ nums: [Int]) -> Int { + if nums.isEmpty { + return 0 + } + if nums.count == 1 { + return nums[0] + } + + var dp = [Int](repeating: 0, count: nums.count) + dp[0] = nums[0] + dp[1] = max(nums[0], nums[1]) + + for i in 2.. Int { + if nums.isEmpty { + return 0 + } + if nums.count == 1 { + return nums[0] + } + + let candidate1 = nums[0] + let candidate2 = helper(Array(nums[1.. Int { + var rob1 = 0 + var rob2 = 0 + + for num in nums { + let newRob = max(rob1 + num, rob2) + rob1 = rob2 + rob2 = newRob + } + + return rob2 + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/house-robber.md b/articles/house-robber.md index 6122135cb..22be9ba02 100644 --- a/articles/house-robber.md +++ b/articles/house-robber.md @@ -118,6 +118,21 @@ class Solution { } ``` +```swift +class Solution { + func rob(_ nums: [Int]) -> Int { + func dfs(_ i: Int) -> Int { + if i >= nums.count { + return 0 + } + return max(dfs(i + 1), nums[i] + dfs(i + 2)) + } + + return dfs(0) + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -291,6 +306,27 @@ class Solution { } ``` +```swift +class Solution { + func rob(_ nums: [Int]) -> Int { + var memo = Array(repeating: -1, count: nums.count) + + func dfs(_ i: Int) -> Int { + if i >= nums.count { + return 0 + } + if memo[i] != -1 { + return memo[i] + } + memo[i] = max(dfs(i + 1), nums[i] + dfs(i + 2)) + return memo[i] + } + + return dfs(0) + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -455,6 +491,29 @@ class Solution { } ``` +```swift +class Solution { + func rob(_ nums: [Int]) -> Int { + if nums.isEmpty { + return 0 + } + if nums.count == 1 { + return nums[0] + } + + var dp = Array(repeating: 0, count: nums.count) + dp[0] = nums[0] + dp[1] = max(nums[0], nums[1]) + + for i in 2.. Int { + var rob1 = 0, rob2 = 0 + + for num in nums { + let temp = max(num + rob1, rob2) + rob1 = rob2 + rob2 = temp + } + + return rob2 + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/implement-prefix-tree.md b/articles/implement-prefix-tree.md index 493258c7b..3e4a303f2 100644 --- a/articles/implement-prefix-tree.md +++ b/articles/implement-prefix-tree.md @@ -363,6 +363,62 @@ class PrefixTree { } ``` +```swift +class TrieNode { + var children: [TrieNode?] + var endOfWord: Bool + + init() { + self.children = Array(repeating: nil, count: 26) + self.endOfWord = false + } +} + +class PrefixTree { + private let root: TrieNode + + init() { + self.root = TrieNode() + } + + func insert(_ word: String) { + var cur = root + for c in word { + let i = Int(c.asciiValue! - Character("a").asciiValue!) + if cur.children[i] == nil { + cur.children[i] = TrieNode() + } + cur = cur.children[i]! + } + cur.endOfWord = true + } + + func search(_ word: String) -> Bool { + var cur = root + for c in word { + let i = Int(c.asciiValue! - Character("a").asciiValue!) + if cur.children[i] == nil { + return false + } + cur = cur.children[i]! + } + return cur.endOfWord + } + + func startsWith(_ prefix: String) -> Bool { + var cur = root + for c in prefix { + let i = Int(c.asciiValue! - Character("a").asciiValue!) + if cur.children[i] == nil { + return false + } + cur = cur.children[i]! + } + return true + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -708,6 +764,59 @@ class PrefixTree { } ``` +```swift +class TrieNode { + var children: [Character: TrieNode] + var endOfWord: Bool + + init() { + self.children = [:] + self.endOfWord = false + } +} + +class PrefixTree { + private let root: TrieNode + + init() { + self.root = TrieNode() + } + + func insert(_ word: String) { + var cur = root + for c in word { + if cur.children[c] == nil { + cur.children[c] = TrieNode() + } + cur = cur.children[c]! + } + cur.endOfWord = true + } + + func search(_ word: String) -> Bool { + var cur = root + for c in word { + if cur.children[c] == nil { + return false + } + cur = cur.children[c]! + } + return cur.endOfWord + } + + func startsWith(_ prefix: String) -> Bool { + var cur = root + for c in prefix { + if cur.children[c] == nil { + return false + } + cur = cur.children[c]! + } + return true + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/insert-new-interval.md b/articles/insert-new-interval.md index 367a09391..13f29a037 100644 --- a/articles/insert-new-interval.md +++ b/articles/insert-new-interval.md @@ -216,6 +216,37 @@ class Solution { } ``` +```swift +class Solution { + func insert(_ intervals: [[Int]], _ newInterval: [Int]) -> [[Int]] { + var intervals = intervals + var newInterval = newInterval + var res: [[Int]] = [] + var i = 0 + let n = intervals.count + + while i < n && intervals[i][1] < newInterval[0] { + res.append(intervals[i]) + i += 1 + } + + while i < n && newInterval[1] >= intervals[i][0] { + newInterval[0] = min(newInterval[0], intervals[i][0]) + newInterval[1] = max(newInterval[1], intervals[i][1]) + i += 1 + } + res.append(newInterval) + + while i < n { + res.append(intervals[i]) + i += 1 + } + + return res + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -512,6 +543,41 @@ class Solution { } ``` +```swift +class Solution { + func insert(_ intervals: [[Int]], _ newInterval: [Int]) -> [[Int]] { + if intervals.isEmpty { + return [newInterval] + } + + var intervals = intervals + let target = newInterval[0] + var left = 0, right = intervals.count - 1 + + while left <= right { + let mid = (left + right) / 2 + if intervals[mid][0] < target { + left = mid + 1 + } else { + right = mid - 1 + } + } + + intervals.insert(newInterval, at: left) + + var res: [[Int]] = [] + for interval in intervals { + if res.isEmpty || res.last![1] < interval[0] { + res.append(interval) + } else { + res[res.count - 1][1] = max(res.last![1], interval[1]) + } + } + return res + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -707,6 +773,31 @@ class Solution { } ``` +```swift +class Solution { + func insert(_ intervals: [[Int]], _ newInterval: [Int]) -> [[Int]] { + var res = [[Int]]() + var newInterval = newInterval + + for i in 0.. intervals[i][1] { + res.append(intervals[i]) + } else { + newInterval[0] = min(newInterval[0], intervals[i][0]) + newInterval[1] = max(newInterval[1], intervals[i][1]) + } + } + + res.append(newInterval) + return res + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/interleaving-string.md b/articles/interleaving-string.md index c5e0566de..3d124cd01 100644 --- a/articles/interleaving-string.md +++ b/articles/interleaving-string.md @@ -199,6 +199,39 @@ class Solution { } ``` +```swift +class Solution { + func isInterleave(_ s1: String, _ s2: String, _ s3: String) -> Bool { + let n1 = s1.count, n2 = s2.count, n3 = s3.count + if n1 + n2 != n3 { return false } + + let s1 = Array(s1), s2 = Array(s2), s3 = Array(s3) + + func dfs(_ i: Int, _ j: Int, _ k: Int) -> Bool { + if k == n3 { + return i == n1 && j == n2 + } + + if i < n1 && s1[i] == s3[k] { + if dfs(i + 1, j, k + 1) { + return true + } + } + + if j < n2 && s2[j] == s3[k] { + if dfs(i, j + 1, k + 1) { + return true + } + } + + return false + } + + return dfs(0, 0, 0) + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -462,6 +495,40 @@ class Solution { } ``` +```swift +class Solution { + func isInterleave(_ s1: String, _ s2: String, _ s3: String) -> Bool { + let m = s1.count, n = s2.count, l = s3.count + if m + n != l { return false } + + let s1 = Array(s1), s2 = Array(s2), s3 = Array(s3) + var dp = Array(repeating: Array(repeating: nil as Bool?, count: n + 1), count: m + 1) + + func dfs(_ i: Int, _ j: Int, _ k: Int) -> Bool { + if k == l { + return i == m && j == n + } + if let cached = dp[i][j] { + return cached + } + + var res = false + if i < m && s1[i] == s3[k] { + res = dfs(i + 1, j, k + 1) + } + if !res && j < n && s2[j] == s3[k] { + res = dfs(i, j + 1, k + 1) + } + + dp[i][j] = res + return res + } + + return dfs(0, 0, 0) + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -660,6 +727,31 @@ class Solution { } ``` +```swift +class Solution { + func isInterleave(_ s1: String, _ s2: String, _ s3: String) -> Bool { + let m = s1.count, n = s2.count, l = s3.count + if m + n != l { return false } + + let s1 = Array(s1), s2 = Array(s2), s3 = Array(s3) + var dp = Array(repeating: Array(repeating: false, count: n + 1), count: m + 1) + dp[m][n] = true + + for i in stride(from: m, through: 0, by: -1) { + for j in stride(from: n, through: 0, by: -1) { + if i < m && s1[i] == s3[i + j] && dp[i + 1][j] { + dp[i][j] = true + } + if j < n && s2[j] == s3[i + j] && dp[i][j + 1] { + dp[i][j] = true + } + } + } + return dp[0][0] + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -904,6 +996,38 @@ class Solution { } ``` +```swift +class Solution { + func isInterleave(_ s1: String, _ s2: String, _ s3: String) -> Bool { + var s1 = Array(s1), s2 = Array(s2), s3 = Array(s3) + var m = s1.count, n = s2.count + if m + n != s3.count { return false } + if n < m { + swap(&s1, &s2) + swap(&m, &n) + } + + var dp = Array(repeating: false, count: n + 1) + dp[n] = true + + for i in stride(from: m, through: 0, by: -1) { + var nextDp = Array(repeating: false, count: n + 1) + nextDp[n] = true + for j in stride(from: n, through: 0, by: -1) { + if i < m && s1[i] == s3[i + j] && dp[j] { + nextDp[j] = true + } + if j < n && s2[j] == s3[i + j] && nextDp[j + 1] { + nextDp[j] = true + } + } + dp = nextDp + } + return dp[0] + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -1155,6 +1279,39 @@ class Solution { } ``` +```swift +class Solution { + func isInterleave(_ s1: String, _ s2: String, _ s3: String) -> Bool { + var s1 = Array(s1), s2 = Array(s2), s3 = Array(s3) + var m = s1.count, n = s2.count + if m + n != s3.count { return false } + if n < m { + swap(&s1, &s2) + swap(&m, &n) + } + + var dp = Array(repeating: false, count: n + 1) + dp[n] = true + + for i in stride(from: m, through: 0, by: -1) { + var nextDp = true + for j in stride(from: n - 1, through: 0, by: -1) { + var res = false + if i < m && s1[i] == s3[i + j] && dp[j] { + res = true + } + if j < n && s2[j] == s3[i + j] && nextDp { + res = true + } + dp[j] = res + nextDp = dp[j] + } + } + return dp[0] + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/invert-a-binary-tree.md b/articles/invert-a-binary-tree.md index 2c7622067..33ee15f84 100644 --- a/articles/invert-a-binary-tree.md +++ b/articles/invert-a-binary-tree.md @@ -227,6 +227,44 @@ class Solution { } ``` +```swift +/** + * Definition for a binary tree node. + * public class TreeNode { + * public var val: Int + * public var left: TreeNode? + * public var right: TreeNode? + * public init() { self.val = 0; self.left = nil; self.right = nil; } + * public init(_ val: Int) { self.val = val; self.left = nil; self.right = nil; } + * public init(_ val: Int, _ left: TreeNode?, _ right: TreeNode?) { + * self.val = val + * self.left = left + * self.right = right + * } + * } + */ +class Solution { + func invertTree(_ root: TreeNode?) -> TreeNode? { + guard let root = root else { return nil } + var queue = Deque() + queue.append(root) + + while !queue.isEmpty { + let node = queue.removeFirst() + (node.left, node.right) = (node.right, node.left) + + if let left = node.left { + queue.append(left) + } + if let right = node.right { + queue.append(right) + } + } + return root + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -253,10 +291,10 @@ class Solution: if not root: return None root.left, root.right = root.right, root.left - + self.invertTree(root.left) self.invertTree(root.right) - + return root ``` @@ -277,16 +315,18 @@ class Solution: * } */ -class Solution { +public class Solution { public TreeNode invertTree(TreeNode root) { if (root == null) return null; - - TreeNode node = new TreeNode(root.val); - node.right = invertTree(root.left); - node.left = invertTree(root.right); - - return node; + TreeNode temp = root.left; + root.left = root.right; + root.right = temp; + + invertTree(root.left); + invertTree(root.right); + + return root; } } ``` @@ -307,14 +347,13 @@ class Solution { class Solution { public: TreeNode* invertTree(TreeNode* root) { - if (root == nullptr) return nullptr; + if (!root) return nullptr; - TreeNode* node = new TreeNode(root->val); - - node->right = invertTree(root->left); - node->left = invertTree(root->right); + swap(root->left, root->right); + invertTree(root->left); + invertTree(root->right); - return node; + return root; } }; ``` @@ -337,14 +376,13 @@ class Solution { * @return {TreeNode} */ invertTree(root) { - if (root === null) return null; - - const node = new TreeNode(root.val); + if (!root) return null; - node.right = this.invertTree(root.left); - node.left = this.invertTree(root.right); + [root.left, root.right] = [root.right, root.left]; + this.invertTree(root.left); + this.invertTree(root.right); - return node; + return root; } } ``` @@ -368,12 +406,95 @@ public class Solution { public TreeNode InvertTree(TreeNode root) { if (root == null) return null; - TreeNode node = new TreeNode(root.val); - - node.right = InvertTree(root.left); - node.left = InvertTree(root.right); + TreeNode temp = root.left; + root.left = root.right; + root.right = temp; + + InvertTree(root.left); + InvertTree(root.right); - return node; + return root; + } +} +``` + +```go +/** + * Definition for a binary tree node. + * type TreeNode struct { + * Val int + * Left *TreeNode + * Right *TreeNode + * } + */ + +func invertTree(root *TreeNode) *TreeNode { + if root == nil { + return nil + } + + root.Left, root.Right = root.Right, root.Left + invertTree(root.Left) + invertTree(root.Right) + + return root +} +``` + +```kotlin +/** + * Example: + * var ti = TreeNode(5) + * var v = ti.`val` + * Definition for a binary tree node. + * class TreeNode(var `val`: Int) { + * var left: TreeNode? = null + * var right: TreeNode? = null + * } + */ + +class Solution { + fun invertTree(root: TreeNode?): TreeNode? { + if (root == null) return null + + val temp = root.left + root.left = root.right + root.right = temp + + invertTree(root.left) + invertTree(root.right) + + return root + } +} +``` + +```swift +/** + * Definition for a binary tree node. + * public class TreeNode { + * public var val: Int + * public var left: TreeNode? + * public var right: TreeNode? + * public init() { self.val = 0; self.left = nil; self.right = nil; } + * public init(_ val: Int) { self.val = val; self.left = nil; self.right = nil; } + * public init(_ val: Int, _ left: TreeNode?, _ right: TreeNode?) { + * self.val = val + * self.left = left + * self.right = right + * } + * } + */ +class Solution { + func invertTree(_ root: TreeNode?) -> TreeNode? { + guard let root = root else { return nil } + + (root.left, root.right) = (root.right, root.left) + + invertTree(root.left) + invertTree(root.right) + + return root } } ``` @@ -383,7 +504,7 @@ public class Solution { ### Time & Space Complexity * Time complexity: $O(n)$ -* Space complexity: $O(n)$ +* Space complexity: $O(n)$ for recursion stack. --- @@ -592,6 +713,43 @@ class Solution { } ``` +```swift +/** + * Definition for a binary tree node. + * public class TreeNode { + * public var val: Int + * public var left: TreeNode? + * public var right: TreeNode? + * public init() { self.val = 0; self.left = nil; self.right = nil; } + * public init(_ val: Int) { self.val = val; self.left = nil; self.right = nil; } + * public init(_ val: Int, _ left: TreeNode?, _ right: TreeNode?) { + * self.val = val + * self.left = left + * self.right = right + * } + * } + */ +class Solution { + func invertTree(_ root: TreeNode?) -> TreeNode? { + guard let root = root else { return nil } + var stack: [TreeNode] = [root] + + while !stack.isEmpty { + let node = stack.removeLast() + (node.left, node.right) = (node.right, node.left) + + if let left = node.left { + stack.append(left) + } + if let right = node.right { + stack.append(right) + } + } + return root + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/is-anagram.md b/articles/is-anagram.md index ae3a44534..eb996634a 100644 --- a/articles/is-anagram.md +++ b/articles/is-anagram.md @@ -112,6 +112,14 @@ class Solution { } ``` +```swift +class Solution { + func isAnagram(_ s: String, _ t: String) -> Bool { + return s.count == t.count && s.sorted() == t.sorted() + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -123,7 +131,7 @@ class Solution { --- -## 2. Hash Table +## 2. Hash Map ::tabs-start @@ -266,6 +274,29 @@ class Solution { } ``` +```swift +class Solution { + func isAnagram(_ s: String, _ t: String) -> Bool { + if s.count != t.count { + return false + } + + var countS = [Character: Int]() + var countT = [Character: Int]() + + let sArray = Array(s) + let tArray = Array(t) + + for i in 0.. Bool { + if s.count != t.count { + return false + } + + var count = [Int](repeating: 0, count: 26) + let sArray = Array(s) + let tArray = Array(t) + + for i in 0.. Where $n$ is the length of string $s$ and $m$ is the length of string $t$. +> Where $n$ is the length of string $s$ and $m$ is the length of string $t$. \ No newline at end of file diff --git a/articles/is-palindrome.md b/articles/is-palindrome.md index e7675ceb9..9aea0c833 100644 --- a/articles/is-palindrome.md +++ b/articles/is-palindrome.md @@ -123,6 +123,22 @@ class Solution { } ``` +```swift +class Solution { + func isPalindrome(_ s: String) -> Bool { + var newStr = "" + + for c in s { + if c.isLetter || c.isNumber { + newStr.append(c.lowercased()) + } + } + + return newStr == String(newStr.reversed()) + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -327,6 +343,34 @@ class Solution { } ``` +```swift +class Solution { + func isPalindrome(_ s: String) -> Bool { + let chars = Array(s) + var l = 0, r = chars.count - 1 + + while l < r { + while l < r && !isAlphaNum(chars[l]) { + l += 1 + } + while r > l && !isAlphaNum(chars[r]) { + r -= 1 + } + if chars[l].lowercased() != chars[r].lowercased() { + return false + } + l += 1 + r -= 1 + } + return true + } + + private func isAlphaNum(_ c: Character) -> Bool { + return c.isLetter || c.isNumber + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/islands-and-treasure.md b/articles/islands-and-treasure.md index ff99ef107..86e92192f 100644 --- a/articles/islands-and-treasure.md +++ b/articles/islands-and-treasure.md @@ -302,6 +302,43 @@ class Solution { } ``` +```swift +class Solution { + func islandsAndTreasure(_ grid: inout [[Int]]) { + let ROWS = grid.count + let COLS = grid[0].count + let INF = 2147483647 + let directions = [(1, 0), (-1, 0), (0, 1), (0, -1)] + var visit = Array(repeating: Array(repeating: false, count: COLS), count: ROWS) + + func dfs(_ r: Int, _ c: Int) -> Int { + if r < 0 || c < 0 || r >= ROWS || c >= COLS || grid[r][c] == -1 || visit[r][c] { + return INF + } + if grid[r][c] == 0 { + return 0 + } + + visit[r][c] = true + var res = INF + for (dx, dy) in directions { + res = min(res, 1 + dfs(r + dx, c + dy)) + } + visit[r][c] = false + return res + } + + for r in 0.. Int { + var q = Deque<(Int, Int)>() + q.append((r, c)) + var visit = Array(repeating: Array(repeating: false, count: COLS), count: ROWS) + visit[r][c] = true + var steps = 0 + while !q.isEmpty { + let levelCount = q.count + for _ in 0..= 0 && nr < ROWS && nc >= 0 && nc < COLS && + !visit[nr][nc] && grid[nr][nc] != -1) { + visit[nr][nc] = true + q.append((nr, nc)) + } + } + } + steps += 1 + } + return INF + } + + for r in 0..() + var q = Deque() + + func addCell(_ r: Int, _ c: Int) { + if r < 0 || c < 0 || r >= ROWS || c >= COLS { return } + let item = Item(r: r, c: c) + if visit.contains(item) || grid[r][c] == -1 { return } + visit.insert(item) + q.append(item) + } + + for r in 0.. Int { + func dfs(_ i: Int) -> Int { + if i == nums.count - 1 { + return 0 + } + if nums[i] == 0 { + return 1000000 + } + + let end = min(nums.count - 1, i + nums[i]) + var res = 1000000 + for j in i + 1..<(end + 1) { + res = min(res, 1 + dfs(j)) + } + + return res + } + + return dfs(0) + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -382,6 +411,37 @@ class Solution { } ``` +```swift +class Solution { + var memo: [Int: Int] = [:] + + func jump(_ nums: [Int]) -> Int { + return dfs(nums, 0) + } + + private func dfs(_ nums: [Int], _ i: Int) -> Int { + if let cachedResult = memo[i] { + return cachedResult + } + if i == nums.count - 1 { + return 0 + } + if nums[i] == 0 { + return 1000000 + } + + var res = 1000000 + let end = min(nums.count, i + nums[i] + 1) + for j in i + 1.. Int { + let n = nums.count + var dp = Array(repeating: 1000000, count: n) + dp[n - 1] = 0 + + for i in stride(from: n - 2, through: 0, by: -1) { + let end = min(n, i + nums[i] + 1) + for j in i + 1.. Int { + var res = 0 + var l = 0 + var r = 0 + + while r < nums.count - 1 { + var farthest = 0 + for i in l...r { + farthest = max(farthest, i + nums[i]) + } + l = r + 1 + r = farthest + res += 1 + } + + return res + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/jump-game.md b/articles/jump-game.md index 850539bca..c5048055e 100644 --- a/articles/jump-game.md +++ b/articles/jump-game.md @@ -157,6 +157,29 @@ class Solution { } ``` +```swift +class Solution { + func canJump(_ nums: [Int]) -> Bool { + + func dfs(_ i: Int) -> Bool { + if i == nums.count - 1 { + return true + } + + let end = min(nums.count - 1, i + nums[i]) + for j in i + 1..<(end + 1) { + if dfs(j) { + return true + } + } + return false + } + + return dfs(0) + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -397,6 +420,39 @@ class Solution { } ``` +```swift +class Solution { + func canJump(_ nums: [Int]) -> Bool { + var memo: [Int: Bool] = [:] + + func dfs(_ i: Int) -> Bool { + if let cachedResult = memo[i] { + return cachedResult + } + if i == nums.count - 1 { + return true + } + if nums[i] == 0 { + return false + } + + let end = min(nums.count, i + nums[i] + 1) + for j in i + 1.. Bool { + let n = nums.count + var dp = [Bool](repeating: false, count: n) + dp[n - 1] = true + + for i in (0.. Bool { + var goal = nums.count - 1 + + for i in stride(from: nums.count - 2, through: 0, by: -1) { + if i + nums[i] >= goal { + goal = i + } + } + + return goal == 0 + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/k-closest-points-to-origin.md b/articles/k-closest-points-to-origin.md index 823a5a33f..7882e7c18 100644 --- a/articles/k-closest-points-to-origin.md +++ b/articles/k-closest-points-to-origin.md @@ -75,6 +75,16 @@ class Solution { } ``` +```swift +class Solution { + func kClosest(_ points: [[Int]], _ k: Int) -> [[Int]] { + return points.sorted { ($0[0] * $0[0] + $0[1] * $0[1]) < ($1[0] * $1[0] + $1[1] * $1[1]) } + .prefix(k) + .map { $0 } + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -84,7 +94,7 @@ class Solution { --- -## 2. Min Heap +## 2. Min-Heap ::tabs-start @@ -242,6 +252,39 @@ class Solution { } ``` +```swift +struct Item: Comparable { + let dist: Int + let x: Int + let y: Int + + static func < (lhs: Item, rhs: Item) -> Bool { + return lhs.dist < rhs.dist + } +} + +class Solution { + func kClosest(_ points: [[Int]], _ k: Int) -> [[Int]] { + var minHeap = Heap() + + for point in points { + let x = point[0], y = point[1] + let dist = x * x + y * y + minHeap.insert(Item(dist: dist, x: x, y: y)) + } + + var res = [[Int]]() + for _ in 0.. Bool { + return lhs.dist > rhs.dist + } +} + +class Solution { + func kClosest(_ points: [[Int]], _ k: Int) -> [[Int]] { + var maxHeap = Heap() + + for point in points { + let x = point[0], y = point[1] + let dist = x * x + y * y + maxHeap.insert(Item(dist: dist, x: x, y: y)) + if maxHeap.count > k { + _ = maxHeap.popMin() + } + } + + var res = [[Int]]() + while !maxHeap.isEmpty { + if let item = maxHeap.popMin() { + res.append([item.x, item.y]) + } + } + + return res + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -721,6 +800,46 @@ class Solution { } ``` +```swift +class Solution { + func kClosest(_ points: [[Int]], _ k: Int) -> [[Int]] { + var points = points + + func euclidean(_ point: [Int]) -> Int { + return point[0] * point[0] + point[1] * point[1] + } + + func partition(_ l: Int, _ r: Int) -> Int { + let pivotIdx = r + let pivotDist = euclidean(points[pivotIdx]) + var i = l + for j in l.. Int { + let sortedNums = nums.sorted() + return sortedNums[sortedNums.count - k] + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -184,6 +193,23 @@ class Solution { } ``` +```swift +class Solution { + func findKthLargest(_ nums: [Int], _ k: Int) -> Int { + var minHeap = Heap() + + for num in nums { + minHeap.insert(num) + if minHeap.count > k { + _ = minHeap.removeMin() + } + } + + return minHeap.popMin()! + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -417,6 +443,38 @@ class Solution { } ``` +```swift +class Solution { + func findKthLargest(_ nums: [Int], _ k: Int) -> Int { + var nums = nums + let k = nums.count - k + + func quickSelect(_ l: Int, _ r: Int) -> Int { + let pivot = nums[r] + var p = l + + for i in l.. k { + return quickSelect(l, p - 1) + } else if p < k { + return quickSelect(p + 1, r) + } else { + return nums[p] + } + } + + return quickSelect(0, nums.count - 1) + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -822,6 +880,70 @@ class Solution { } ``` +```swift +class Solution { + func partition(_ nums: inout [Int], _ left: Int, _ right: Int) -> Int { + let mid = (left + right) >> 1 + nums.swapAt(mid, left + 1) + + if nums[left] < nums[right] { + nums.swapAt(left, right) + } + if nums[left + 1] < nums[right] { + nums.swapAt(left + 1, right) + } + if nums[left] < nums[left + 1] { + nums.swapAt(left, left + 1) + } + + let pivot = nums[left + 1] + var i = left + 1 + var j = right + + while true { + repeat { i += 1 } while nums[i] > pivot + repeat { j -= 1 } while nums[j] < pivot + + if i > j { + break + } + nums.swapAt(i, j) + } + + nums.swapAt(left + 1, j) + return j + } + + func quickSelect(_ nums: inout [Int], _ k: Int) -> Int { + var left = 0 + var right = nums.count - 1 + + while true { + if right <= left + 1 { + if right == left + 1 && nums[right] > nums[left] { + nums.swapAt(left, right) + } + return nums[k] + } + + let j = partition(&nums, left, right) + + if j >= k { + right = j - 1 + } + if j <= k { + left = j + 1 + } + } + } + + func findKthLargest(_ nums: [Int], _ k: Int) -> Int { + var nums = nums + return quickSelect(&nums, k - 1) + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/kth-largest-integer-in-a-stream.md b/articles/kth-largest-integer-in-a-stream.md index 7913b93ca..42a4f617a 100644 --- a/articles/kth-largest-integer-in-a-stream.md +++ b/articles/kth-largest-integer-in-a-stream.md @@ -124,12 +124,32 @@ class KthLargest(k: Int, nums: IntArray) { } ``` +```swift +class KthLargest { + private var k: Int + private var arr: [Int] + + init(_ k: Int, _ nums: [Int]) { + self.k = k + self.arr = nums + } + + func add(_ val: Int) -> Int { + arr.append(val) + arr.sort() + return arr[arr.count - k] + } +} +``` + ::tabs-end ### Time & Space Complexity * Time complexity: $O(m * n\log n)$ -* Space complexity: $O(1)$ or $O(n)$ depending on the sorting algorithm. +* Space complexity: + * $O(m)$ extra space. + * $O(1)$ or $O(n)$ space depending on the sorting algorithm. > Where $m$ is the number of calls made to $add()$ and $n$ is the current size of the array. @@ -324,6 +344,32 @@ class KthLargest(k: Int, nums: IntArray) { } ``` +```swift +class KthLargest { + private var minHeap: Heap + private let k: Int + + init(_ k: Int, _ nums: [Int]) { + self.k = k + self.minHeap = Heap() + for num in nums { + minHeap.insert(num) + if minHeap.count > k { + minHeap.popMin() + } + } + } + + func add(_ val: Int) -> Int { + minHeap.insert(val) + if minHeap.count > k { + minHeap.popMin() + } + return minHeap.min! + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/kth-smallest-integer-in-bst.md b/articles/kth-smallest-integer-in-bst.md index 1014ba6d4..ec5dfe795 100644 --- a/articles/kth-smallest-integer-in-bst.md +++ b/articles/kth-smallest-integer-in-bst.md @@ -225,6 +225,40 @@ class Solution { } ``` +```swift +/** + * Definition for a binary tree node. + * public class TreeNode { + * public var val: Int + * public var left: TreeNode? + * public var right: TreeNode? + * public init() { self.val = 0; self.left = nil; self.right = nil; } + * public init(_ val: Int) { self.val = val; self.left = nil; self.right = nil; } + * public init(_ val: Int, _ left: TreeNode?, _ right: TreeNode?) { + * self.val = val + * self.left = left + * self.right = right + * } + * } + */ +class Solution { + func kthSmallest(_ root: TreeNode?, _ k: Int) -> Int { + var arr = [Int]() + + func dfs(_ node: TreeNode?) { + guard let node = node else { return } + arr.append(node.val) + dfs(node.left) + dfs(node.right) + } + + dfs(root) + arr.sort() + return arr[k - 1] + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -456,6 +490,39 @@ class Solution { } ``` +```swift +/** + * Definition for a binary tree node. + * public class TreeNode { + * public var val: Int + * public var left: TreeNode? + * public var right: TreeNode? + * public init() { self.val = 0; self.left = nil; self.right = nil; } + * public init(_ val: Int) { self.val = val; self.left = nil; self.right = nil; } + * public init(_ val: Int, _ left: TreeNode?, _ right: TreeNode?) { + * self.val = val + * self.left = left + * self.right = right + * } + * } + */ +class Solution { + func kthSmallest(_ root: TreeNode?, _ k: Int) -> Int { + var arr = [Int]() + + func dfs(_ node: TreeNode?) { + guard let node = node else { return } + dfs(node.left) + arr.append(node.val) + dfs(node.right) + } + + dfs(root) + return arr[k - 1] + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -721,6 +788,45 @@ class Solution { } ``` +```swift +/** + * Definition for a binary tree node. + * public class TreeNode { + * public var val: Int + * public var left: TreeNode? + * public var right: TreeNode? + * public init() { self.val = 0; self.left = nil; self.right = nil; } + * public init(_ val: Int) { self.val = val; self.left = nil; self.right = nil; } + * public init(_ val: Int, _ left: TreeNode?, _ right: TreeNode?) { + * self.val = val + * self.left = left + * self.right = right + * } + * } + */ +class Solution { + func kthSmallest(_ root: TreeNode?, _ k: Int) -> Int { + var cnt = k + var res = root!.val + + func dfs(_ node: TreeNode?) { + guard let node = node else { return } + + dfs(node.left) + cnt -= 1 + if cnt == 0 { + res = node.val + return + } + dfs(node.right) + } + + dfs(root) + return res + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -983,6 +1089,45 @@ class Solution { } ``` +```swift +/** + * Definition for a binary tree node. + * public class TreeNode { + * public var val: Int + * public var left: TreeNode? + * public var right: TreeNode? + * public init() { self.val = 0; self.left = nil; self.right = nil; } + * public init(_ val: Int) { self.val = val; self.left = nil; self.right = nil; } + * public init(_ val: Int, _ left: TreeNode?, _ right: TreeNode?) { + * self.val = val + * self.left = left + * self.right = right + * } + * } + */ +class Solution { + func kthSmallest(_ root: TreeNode?, _ k: Int) -> Int { + var stack = [TreeNode]() + var curr = root + var k = k + + while !stack.isEmpty || curr != nil { + while curr != nil { + stack.append(curr!) + curr = curr?.left + } + curr = stack.removeLast() + k -= 1 + if k == 0 { + return curr!.val + } + curr = curr?.right + } + return -1 + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -1298,6 +1443,58 @@ class Solution { } ``` +```swift +/** + * Definition for a binary tree node. + * public class TreeNode { + * public var val: Int + * public var left: TreeNode? + * public var right: TreeNode? + * public init() { self.val = 0; self.left = nil; self.right = nil; } + * public init(_ val: Int) { self.val = val; self.left = nil; self.right = nil; } + * public init(_ val: Int, _ left: TreeNode?, _ right: TreeNode?) { + * self.val = val + * self.left = left + * self.right = right + * } + * } + */ +class Solution { + func kthSmallest(_ root: TreeNode?, _ k: Int) -> Int { + var curr = root + var k = k + + while curr != nil { + if curr?.left == nil { + k -= 1 + if k == 0 { + return curr!.val + } + curr = curr?.right + } else { + var pred = curr?.left + while pred?.right != nil && pred?.right !== curr { + pred = pred?.right + } + + if pred?.right == nil { + pred?.right = curr + curr = curr?.left + } else { + pred?.right = nil + k -= 1 + if k == 0 { + return curr!.val + } + curr = curr?.right + } + } + } + return -1 + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/largest-rectangle-in-histogram.md b/articles/largest-rectangle-in-histogram.md index 54d44de5b..75df88ffe 100644 --- a/articles/largest-rectangle-in-histogram.md +++ b/articles/largest-rectangle-in-histogram.md @@ -203,6 +203,35 @@ class Solution { } ``` +```swift +class Solution { + func largestRectangleArea(_ heights: [Int]) -> Int { + let n = heights.count + var maxArea = 0 + + for i in 0..= height { + rightMost += 1 + } + + var leftMost = i + while leftMost >= 0 && heights[leftMost] >= height { + leftMost -= 1 + } + + rightMost -= 1 + leftMost += 1 + maxArea = max(maxArea, height * (rightMost - leftMost + 1)) + } + + return maxArea + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -767,6 +796,86 @@ class Solution { } ``` +```swift +class MinIdxSegmentTree { + private var n: Int + private var INF = Int(1e9) + private var A: [Int] + private var tree: [Int] + + init(_ N: Int, _ A: [Int]) { + self.n = N + self.A = A + while (self.n & (self.n - 1)) != 0 { + self.A.append(self.INF) + self.n += 1 + } + self.tree = [Int](repeating: 0, count: 2 * self.n) + build() + } + + private func build() { + for i in 0..> 1 + while j >= 1 { + let a = tree[j << 1] + let b = tree[(j << 1) + 1] + tree[j] = (A[a] <= A[b]) ? a : b + j >>= 1 + } + } + + func query(_ ql: Int, _ qh: Int) -> Int { + return _query(1, 0, n - 1, ql, qh) + } + + private func _query(_ node: Int, _ l: Int, _ h: Int, _ ql: Int, _ qh: Int) -> Int { + if ql > h || qh < l { + return INF + } + if l >= ql && h <= qh { + return tree[node] + } + let a = _query(node << 1, l, (l + h) >> 1, ql, qh) + let b = _query((node << 1) + 1, ((l + h) >> 1) + 1, h, ql, qh) + if a == INF { return b } + if b == INF { return a } + return (A[a] <= A[b]) ? a : b + } +} + +class Solution { + func getMaxArea(_ heights: [Int], _ l: Int, _ r: Int, _ st: MinIdxSegmentTree) -> Int { + if l > r { return 0 } + if l == r { return heights[l] } + + let minIdx = st.query(l, r) + return max( + max(getMaxArea(heights, l, minIdx - 1, st), + getMaxArea(heights, minIdx + 1, r, st)), + (r - l + 1) * heights[minIdx] + ) + } + + func largestRectangleArea(_ heights: [Int]) -> Int { + let n = heights.count + let st = MinIdxSegmentTree(n, heights) + return getMaxArea(heights, 0, n - 1, st) + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -1075,6 +1184,46 @@ class Solution { } ``` +```swift +class Solution { + func largestRectangleArea(_ heights: [Int]) -> Int { + let n = heights.count + var stack = [Int]() + + var leftMost = [Int](repeating: -1, count: n) + for i in 0..= heights[i] { + stack.removeLast() + } + if !stack.isEmpty { + leftMost[i] = stack.last! + } + stack.append(i) + } + + stack.removeAll() + var rightMost = [Int](repeating: n, count: n) + for i in stride(from: n - 1, through: 0, by: -1) { + while !stack.isEmpty && heights[stack.last!] >= heights[i] { + stack.removeLast() + } + if !stack.isEmpty { + rightMost[i] = stack.last! + } + stack.append(i) + } + + var maxArea = 0 + for i in 0.. Int { + var maxArea = 0 + var stack = [(Int, Int)]() // Pair: (index, height) + + for (i, h) in heights.enumerated() { + var start = i + while !stack.isEmpty && stack.last!.1 > h { + let (index, height) = stack.removeLast() + maxArea = max(maxArea, height * (i - index)) + start = index + } + stack.append((start, h)) + } + + for (i, h) in stack { + maxArea = max(maxArea, h * (heights.count - i)) + } + + return maxArea + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -1292,7 +1466,7 @@ class Solution { --- -## 5. Stack (One Pass) +## 5. Stack (Optimal) ::tabs-start @@ -1453,6 +1627,26 @@ class Solution { } ``` +```swift +class Solution { + func largestRectangleArea(_ heights: [Int]) -> Int { + let n = heights.count + var maxArea = 0 + var stack = [Int]() + + for i in 0...n { + while !stack.isEmpty && (i == n || heights[stack.last!] >= heights[i]) { + let height = heights[stack.removeLast()] + let width = stack.isEmpty ? i : i - stack.last! - 1 + maxArea = max(maxArea, height * width) + } + stack.append(i) + } + return maxArea + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/last-stone-weight.md b/articles/last-stone-weight.md index da1ec745e..69ab3403a 100644 --- a/articles/last-stone-weight.md +++ b/articles/last-stone-weight.md @@ -127,6 +127,24 @@ class Solution { } ``` +```swift +class Solution { + func lastStoneWeight(_ stones: [Int]) -> Int { + var stones = stones + + while stones.count > 1 { + stones.sort() + let cur = stones.removeLast() - stones.removeLast() + if cur > 0 { + stones.append(cur) + } + } + + return stones.isEmpty ? 0 : stones[0] + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -368,6 +386,40 @@ class Solution { } ``` +```swift +class Solution { + func lastStoneWeight(_ stones: [Int]) -> Int { + var stones = stones.sorted() + var n = stones.count + + while n > 1 { + let cur = stones.removeLast() - stones.removeLast() + n -= 2 + if cur > 0 { + var l = 0, r = n + while l < r { + let mid = (l + r) / 2 + if stones[mid] < cur { + l = mid + 1 + } else { + r = mid + } + } + let pos = l + stones.append(0) + n += 1 + for i in stride(from: n - 1, to: pos, by: -1) { + stones[i] = stones[i - 1] + } + stones[pos] = cur + } + } + + return n > 0 ? stones[0] : 0 + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -543,6 +595,22 @@ class Solution { } ``` +```swift +class Solution { + func lastStoneWeight(_ stones: [Int]) -> Int { + var heap = Heap(stones) + while heap.count > 1 { + let first = heap.popMax()! + let second = heap.popMax()! + if first > second { + heap.insert(first - second) + } + } + return heap.isEmpty ? 0 : heap.popMax()! + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -843,6 +911,46 @@ class Solution { } ``` +```swift +class Solution { + func lastStoneWeight(_ stones: [Int]) -> Int { + guard let maxStone = stones.max() else { return 0 } + + var bucket = [Int](repeating: 0, count: maxStone + 1) + for stone in stones { + bucket[stone] += 1 + } + + var first = maxStone + var second = maxStone + + while first > 0 { + if bucket[first] % 2 == 0 { + first -= 1 + continue + } + + var j = min(first - 1, second) + while j > 0 && bucket[j] == 0 { + j -= 1 + } + + if j == 0 { + return first + } + + second = j + bucket[first] -= 1 + bucket[second] -= 1 + bucket[first - second] += 1 + first = max(first - second, second) + } + + return first + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/level-order-traversal-of-binary-tree.md b/articles/level-order-traversal-of-binary-tree.md index 3bb08fe9a..28df7fc5f 100644 --- a/articles/level-order-traversal-of-binary-tree.md +++ b/articles/level-order-traversal-of-binary-tree.md @@ -19,11 +19,11 @@ class Solution: return None if len(res) == depth: res.append([]) - + res[depth].append(node.val) dfs(node.left, depth + 1) dfs(node.right, depth + 1) - + dfs(root, 0) return res ``` @@ -251,6 +251,44 @@ class Solution { } ``` +```swift +/** + * Definition for a binary tree node. + * public class TreeNode { + * public var val: Int + * public var left: TreeNode? + * public var right: TreeNode? + * public init() { self.val = 0; self.left = nil; self.right = nil; } + * public init(_ val: Int) { self.val = val; self.left = nil; self.right = nil; } + * public init(_ val: Int, _ left: TreeNode?, _ right: TreeNode?) { + * self.val = val + * self.left = left + * self.right = right + * } + * } + */ +class Solution { + func levelOrder(_ root: TreeNode?) -> [[Int]] { + var res = [[Int]]() + + func dfs(_ node: TreeNode?, _ depth: Int) { + guard let node = node else { return } + + if res.count == depth { + res.append([]) + } + + res[depth].append(node.val) + dfs(node.left, depth + 1) + dfs(node.right, depth + 1) + } + + dfs(root, 0) + return res + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -549,6 +587,48 @@ class Solution { } ``` +```swift +/** + * Definition for a binary tree node. + * public class TreeNode { + * public var val: Int + * public var left: TreeNode? + * public var right: TreeNode? + * public init() { self.val = 0; self.left = nil; self.right = nil; } + * public init(_ val: Int) { self.val = val; self.left = nil; self.right = nil; } + * public init(_ val: Int, _ left: TreeNode?, _ right: TreeNode?) { + * self.val = val + * self.left = left + * self.right = right + * } + * } + */ +class Solution { + func levelOrder(_ root: TreeNode?) -> [[Int]] { + var res = [[Int]]() + var q = Deque() + q.append(root) + + while !q.isEmpty { + let qLen = q.count + var level = [Int]() + + for _ in 0.. Bool { + var seen = Set() + var cur = head + + while cur != nil { + let nodeId = ObjectIdentifier(cur!) + if seen.contains(nodeId) { + return true + } + seen.insert(nodeId) + cur = cur?.next + } + return false + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -387,6 +418,36 @@ class Solution { } ``` +```swift +/** + * Definition for singly-linked list. + * public class ListNode { + * public var val: Int + * public var next: ListNode? + * public init(_ val: Int) { + * self.val = val + * self.next = nil + * } + * } + */ + +class Solution { + func hasCycle(_ head: ListNode?) -> Bool { + var slow = head + var fast = head + + while fast != nil && fast?.next != nil { + slow = slow?.next + fast = fast?.next?.next + if slow === fast { + return true + } + } + return false + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/longest-common-subsequence.md b/articles/longest-common-subsequence.md index f75a48573..95c23cd86 100644 --- a/articles/longest-common-subsequence.md +++ b/articles/longest-common-subsequence.md @@ -136,6 +136,27 @@ class Solution { } ``` +```swift +class Solution { + func longestCommonSubsequence(_ text1: String, _ text2: String) -> Int { + let arr1 = Array(text1) + let arr2 = Array(text2) + + func dfs(_ i: Int, _ j: Int) -> Int { + if i == arr1.count || j == arr2.count { + return 0 + } + if arr1[i] == arr2[j] { + return 1 + dfs(i + 1, j + 1) + } + return max(dfs(i + 1, j), dfs(i, j + 1)) + } + + return dfs(0, 0) + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -363,6 +384,35 @@ class Solution { } ``` +```swift +class Solution { + func longestCommonSubsequence(_ text1: String, _ text2: String) -> Int { + let arr1 = Array(text1) + let arr2 = Array(text2) + var memo = [String: Int]() + + func dfs(_ i: Int, _ j: Int) -> Int { + if i == arr1.count || j == arr2.count { + return 0 + } + let key = "\(i),\(j)" + if let val = memo[key] { + return val + } + + if arr1[i] == arr2[j] { + memo[key] = 1 + dfs(i + 1, j + 1) + } else { + memo[key] = max(dfs(i + 1, j), dfs(i, j + 1)) + } + return memo[key]! + } + + return dfs(0, 0) + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -534,6 +584,31 @@ class Solution { } ``` +```swift +class Solution { + func longestCommonSubsequence(_ text1: String, _ text2: String) -> Int { + let m = text1.count + let n = text2.count + let arr1 = Array(text1) + let arr2 = Array(text2) + + var dp = Array(repeating: Array(repeating: 0, count: n + 1), count: m + 1) + + for i in stride(from: m - 1, through: 0, by: -1) { + for j in stride(from: n - 1, through: 0, by: -1) { + if arr1[i] == arr2[j] { + dp[i][j] = 1 + dp[i + 1][j + 1] + } else { + dp[i][j] = max(dp[i][j + 1], dp[i + 1][j]) + } + } + } + + return dp[0][0] + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -749,6 +824,35 @@ class Solution { } ``` +```swift +class Solution { + func longestCommonSubsequence(_ text1: String, _ text2: String) -> Int { + var t1 = Array(text1) + var t2 = Array(text2) + + if t1.count < t2.count { + swap(&t1, &t2) + } + + var prev = Array(repeating: 0, count: t2.count + 1) + var curr = Array(repeating: 0, count: t2.count + 1) + + for i in stride(from: t1.count - 1, through: 0, by: -1) { + for j in stride(from: t2.count - 1, through: 0, by: -1) { + if t1[i] == t2[j] { + curr[j] = 1 + prev[j + 1] + } else { + curr[j] = max(curr[j + 1], prev[j]) + } + } + swap(&prev, &curr) + } + + return prev[0] + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -966,6 +1070,36 @@ class Solution { } ``` +```swift +class Solution { + func longestCommonSubsequence(_ text1: String, _ text2: String) -> Int { + var t1 = Array(text1) + var t2 = Array(text2) + + if t1.count < t2.count { + swap(&t1, &t2) + } + + var dp = Array(repeating: 0, count: t2.count + 1) + + for i in stride(from: t1.count - 1, through: 0, by: -1) { + var prev = 0 + for j in stride(from: t2.count - 1, through: 0, by: -1) { + let temp = dp[j] + if t1[i] == t2[j] { + dp[j] = 1 + prev + } else { + dp[j] = max(dp[j], dp[j + 1]) + } + prev = temp + } + } + + return dp[0] + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/longest-consecutive-sequence.md b/articles/longest-consecutive-sequence.md index 9f7193486..22cd5db3f 100644 --- a/articles/longest-consecutive-sequence.md +++ b/articles/longest-consecutive-sequence.md @@ -143,6 +143,29 @@ class Solution { } ``` +```swift +class Solution { + func longestConsecutive(_ nums: [Int]) -> Int { + var res = 0 + let store = Set(nums) + + for num in nums { + var streak = 0 + var curr = num + + while store.contains(curr) { + streak += 1 + curr += 1 + } + + res = max(res, streak) + } + + return res + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -343,12 +366,44 @@ class Solution { } ``` +```swift +class Solution { + func longestConsecutive(_ nums: [Int]) -> Int { + if nums.isEmpty { + return 0 + } + + var res = 0 + var nums = nums.sorted() + + var curr = nums[0] + var streak = 0 + var i = 0 + + while i < nums.count { + if curr != nums[i] { + curr = nums[i] + streak = 0 + } + while i < nums.count && nums[i] == curr { + i += 1 + } + streak += 1 + curr += 1 + res = max(res, streak) + } + + return res + } +} +``` + ::tabs-end ### Time & Space Complexity * Time complexity: $O(n \log n)$ -* Space complexity: $O(1)$ +* Space complexity: $O(1)$ or $O(n)$ depending on the sorting algorithm. --- @@ -506,6 +561,27 @@ class Solution { } ``` +```swift +class Solution { + func longestConsecutive(_ nums: [Int]) -> Int { + let numSet = Set(nums) + var longest = 0 + + for num in numSet { + if !numSet.contains(num - 1) { + var length = 1 + while numSet.contains(num + length) { + length += 1 + } + longest = max(length, longest) + } + } + + return longest + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -662,6 +738,31 @@ class Solution { } ``` +```swift +class Solution { + func longestConsecutive(_ nums: [Int]) -> Int { + var mp = [Int: Int]() + var res = 0 + + for num in nums { + if mp[num] == nil { + let left = mp[num - 1] ?? 0 + let right = mp[num + 1] ?? 0 + let length = left + right + 1 + + mp[num] = length + mp[num - left] = length + mp[num + right] = length + + res = max(res, length) + } + } + + return res + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/longest-increasing-path-in-matrix.md b/articles/longest-increasing-path-in-matrix.md index 950ac6e73..e529b1ded 100644 --- a/articles/longest-increasing-path-in-matrix.md +++ b/articles/longest-increasing-path-in-matrix.md @@ -230,6 +230,35 @@ class Solution { } ``` +```swift +class Solution { + func longestIncreasingPath(_ matrix: [[Int]]) -> Int { + let rows = matrix.count, cols = matrix[0].count + let directions = [(-1, 0), (1, 0), (0, -1), (0, 1)] + + func dfs(_ r: Int, _ c: Int, _ prevVal: Int) -> Int { + if r < 0 || c < 0 || r >= rows || c >= cols || matrix[r][c] <= prevVal { + return 0 + } + + var res = 1 + for (dr, dc) in directions { + res = max(res, 1 + dfs(r + dr, c + dc, matrix[r][c])) + } + return res + } + + var lip = 0 + for r in 0.. Int { + let rows = matrix.count, cols = matrix[0].count + var dp = Array(repeating: Array(repeating: -1, count: cols), count: rows) + + func dfs(_ r: Int, _ c: Int, _ prevVal: Int) -> Int { + if r < 0 || r >= rows || c < 0 || c >= cols || matrix[r][c] <= prevVal { + return 0 + } + if dp[r][c] != -1 { + return dp[r][c] + } + + var res = 1 + res = max(res, 1 + dfs(r + 1, c, matrix[r][c])) + res = max(res, 1 + dfs(r - 1, c, matrix[r][c])) + res = max(res, 1 + dfs(r, c + 1, matrix[r][c])) + res = max(res, 1 + dfs(r, c - 1, matrix[r][c])) + + dp[r][c] = res + return res + } + + var maxPath = 0 + for r in 0.. Int { + let rows = matrix.count, cols = matrix[0].count + let directions = [(-1, 0), (1, 0), (0, -1), (0, 1)] + var indegree = Array(repeating: Array(repeating: 0, count: cols), count: rows) + + for r in 0..= 0 && nr < rows && nc >= 0 && nc < cols && + matrix[nr][nc] < matrix[r][c]) { + indegree[r][c] += 1 + } + } + } + } + + var q = Deque<(Int, Int)>() + for r in 0..= 0 && nr < rows && nc >= 0 && nc < cols && + matrix[nr][nc] > matrix[r][c]) { + indegree[nr][nc] -= 1 + if indegree[nr][nc] == 0 { + q.append((nr, nc)) + } + } + } + } + LIS += 1 + } + return LIS + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/longest-increasing-subsequence.md b/articles/longest-increasing-subsequence.md index 1b24412eb..79391921a 100644 --- a/articles/longest-increasing-subsequence.md +++ b/articles/longest-increasing-subsequence.md @@ -170,6 +170,28 @@ class Solution { } ``` +```swift +class Solution { + func lengthOfLIS(_ nums: [Int]) -> Int { + func dfs(_ i: Int, _ j: Int) -> Int { + if i == nums.count { + return 0 + } + + var LIS = dfs(i + 1, j) // not include + + if j == -1 || nums[j] < nums[i] { + LIS = max(LIS, 1 + dfs(i + 1, i)) // include + } + + return LIS + } + + return dfs(0, -1) + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -179,7 +201,7 @@ class Solution { --- -## 2. Dynamic Programming (Top-Down) +## 2. Dynamic Programming (Top-Down) - I ::tabs-start @@ -410,6 +432,508 @@ class Solution { } ``` +```swift +class Solution { + func lengthOfLIS(_ nums: [Int]) -> Int { + let n = nums.count + var memo = Array(repeating: Array(repeating: -1, count: n + 1), count: n) + + func dfs(_ i: Int, _ j: Int) -> Int { + if i == n { + return 0 + } + if memo[i][j + 1] != -1 { + return memo[i][j + 1] + } + + var LIS = dfs(i + 1, j) // not include + + if j == -1 || nums[j] < nums[i] { + LIS = max(LIS, 1 + dfs(i + 1, i)) // include + } + + memo[i][j + 1] = LIS + return LIS + } + + return dfs(0, -1) + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n ^ 2)$ + +--- + +## 3. Dynamic Programming (Top-Down) - II + +::tabs-start + +```python +class Solution: + def lengthOfLIS(self, nums: List[int]) -> int: + n = len(nums) + memo = [-1] * n + + def dfs(i): + if memo[i] != -1: + return memo[i] + + LIS = 1 + for j in range(i + 1, n): + if nums[i] < nums[j]: + LIS = max(LIS, 1 + dfs(j)) + + memo[i] = LIS + return LIS + + return max(dfs(i) for i in range(n)) +``` + +```java +public class Solution { + private int[] memo; + + public int lengthOfLIS(int[] nums) { + int n = nums.length; + memo = new int[n]; + Arrays.fill(memo, -1); + + int maxLIS = 1; + for (int i = 0; i < n; i++) { + maxLIS = Math.max(maxLIS, dfs(nums, i)); + } + return maxLIS; + } + + private int dfs(int[] nums, int i) { + if (memo[i] != -1) { + return memo[i]; + } + + int LIS = 1; + for (int j = i + 1; j < nums.length; j++) { + if (nums[i] < nums[j]) { + LIS = Math.max(LIS, 1 + dfs(nums, j)); + } + } + + memo[i] = LIS; + return LIS; + } +} +``` + +```cpp +class Solution { +private: + vector memo; + + int dfs(vector& nums, int i) { + if (memo[i] != -1) { + return memo[i]; + } + + int LIS = 1; + for (int j = i + 1; j < nums.size(); j++) { + if (nums[i] < nums[j]) { + LIS = max(LIS, 1 + dfs(nums, j)); + } + } + + return memo[i] = LIS; + } + +public: + int lengthOfLIS(vector& nums) { + int n = nums.size(); + memo.assign(n, -1); + + int maxLIS = 1; + for (int i = 0; i < n; i++) { + maxLIS = max(maxLIS, dfs(nums, i)); + } + return maxLIS; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + lengthOfLIS(nums) { + const n = nums.length; + const memo = new Array(n).fill(-1); + + const dfs = (i) => { + if (memo[i] !== -1) { + return memo[i]; + } + + let LIS = 1; + for (let j = i + 1; j < n; j++) { + if (nums[i] < nums[j]) { + LIS = Math.max(LIS, 1 + dfs(j)); + } + } + + memo[i] = LIS; + return LIS; + }; + + return Math.max(...nums.map((_, i) => dfs(i))); + } +} +``` + +```csharp +public class Solution { + private int[] memo; + + public int LengthOfLIS(int[] nums) { + int n = nums.Length; + memo = new int[n]; + Array.Fill(memo, -1); + + int maxLIS = 1; + for (int i = 0; i < n; i++) { + maxLIS = Math.Max(maxLIS, Dfs(nums, i)); + } + return maxLIS; + } + + private int Dfs(int[] nums, int i) { + if (memo[i] != -1) { + return memo[i]; + } + + int LIS = 1; + for (int j = i + 1; j < nums.Length; j++) { + if (nums[i] < nums[j]) { + LIS = Math.Max(LIS, 1 + Dfs(nums, j)); + } + } + + memo[i] = LIS; + return LIS; + } +} +``` + +```go +func lengthOfLIS(nums []int) int { + n := len(nums) + memo := make([]int, n) + for i := range memo { + memo[i] = -1 + } + + var dfs func(int) int + dfs = func(i int) int { + if memo[i] != -1 { + return memo[i] + } + + LIS := 1 + for j := i + 1; j < n; j++ { + if nums[i] < nums[j] { + LIS = max(LIS, 1+dfs(j)) + } + } + + memo[i] = LIS + return LIS + } + + maxLIS := 1 + for i := 0; i < n; i++ { + maxLIS = max(maxLIS, dfs(i)) + } + + return maxLIS +} + +func max(a, b int) int { + if a > b { + return a + } + return b +} +``` + +```kotlin +class Solution { + private lateinit var memo: IntArray + + fun lengthOfLIS(nums: IntArray): Int { + val n = nums.size + memo = IntArray(n) { -1 } + + var maxLIS = 1 + for (i in nums.indices) { + maxLIS = maxOf(maxLIS, dfs(nums, i)) + } + return maxLIS + } + + private fun dfs(nums: IntArray, i: Int): Int { + if (memo[i] != -1) { + return memo[i] + } + + var LIS = 1 + for (j in i + 1 until nums.size) { + if (nums[i] < nums[j]) { + LIS = maxOf(LIS, 1 + dfs(nums, j)) + } + } + + memo[i] = LIS + return LIS + } +} +``` + +```swift +class Solution { + func lengthOfLIS(_ nums: [Int]) -> Int { + let n = nums.count + var memo = Array(repeating: -1, count: n) + + func dfs(_ i: Int) -> Int { + if memo[i] != -1 { + return memo[i] + } + + var LIS = 1 + for j in (i + 1)..= 0; i--) { + for (int j = i - 1; j >= -1; j--) { + int LIS = dp[i + 1][j + 1]; // Not including nums[i] + + if (j == -1 || nums[j] < nums[i]) { + LIS = Math.max(LIS, 1 + dp[i + 1][i + 1]); // Including nums[i] + } + + dp[i][j + 1] = LIS; + } + } + + return dp[0][0]; + } +} +``` + +```cpp +class Solution { +public: + int lengthOfLIS(vector& nums) { + int n = nums.size(); + vector> dp(n + 1, vector(n + 1, 0)); + + for (int i = n - 1; i >= 0; --i) { + for (int j = i - 1; j >= -1; --j) { + int LIS = dp[i + 1][j + 1]; // Not including nums[i] + + if (j == -1 || nums[j] < nums[i]) { + LIS = max(LIS, 1 + dp[i + 1][i + 1]); // Including nums[i] + } + + dp[i][j + 1] = LIS; + } + } + + return dp[0][0]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + lengthOfLIS(nums) { + const n = nums.length; + const dp = Array.from({ length: n + 1 }, () => new Array(n + 1).fill(0)); + + for (let i = n - 1; i >= 0; i--) { + for (let j = i - 1; j >= -1; j--) { + let LIS = dp[i + 1][j + 1]; // Not including nums[i] + + if (j === -1 || nums[j] < nums[i]) { + LIS = Math.max(LIS, 1 + dp[i + 1][i + 1]); // Including nums[i] + } + + dp[i][j + 1] = LIS; + } + } + + return dp[0][0]; + } +} +``` + +```csharp +public class Solution { + public int LengthOfLIS(int[] nums) { + int n = nums.Length; + int[,] dp = new int[n + 1, n + 1]; + + for (int i = n - 1; i >= 0; i--) { + for (int j = i - 1; j >= -1; j--) { + int LIS = dp[i + 1, j + 1]; // Not including nums[i] + + if (j == -1 || nums[j] < nums[i]) { + LIS = Math.Max(LIS, 1 + dp[i + 1, i + 1]); // Including nums[i] + } + + dp[i, j + 1] = LIS; + } + } + + return dp[0, 0]; + } +} +``` + +```go +func lengthOfLIS(nums []int) int { + n := len(nums) + dp := make([][]int, n+1) + for i := range dp { + dp[i] = make([]int, n+1) + } + + for i := n - 1; i >= 0; i-- { + for j := i - 1; j >= -1; j-- { + LIS := dp[i+1][j+1] // Not including nums[i] + + if j == -1 || nums[j] < nums[i] { + LIS = max(LIS, 1+dp[i+1][i+1]) // Including nums[i] + } + + dp[i][j+1] = LIS + } + } + + return dp[0][0] +} + +func max(a, b int) int { + if a > b { + return a + } + return b +} +``` + +```kotlin +class Solution { + fun lengthOfLIS(nums: IntArray): Int { + val n = nums.size + val dp = Array(n + 1) { IntArray(n + 1) } + + for (i in n - 1 downTo 0) { + for (j in i - 1 downTo -1) { + var LIS = dp[i + 1][j + 1] // Not including nums[i] + + if (j == -1 || nums[j] < nums[i]) { + LIS = maxOf(LIS, 1 + dp[i + 1][i + 1]) // Including nums[i] + } + + dp[i][j + 1] = LIS + } + } + + return dp[0][0] + } +} +``` + +```swift +class Solution { + func lengthOfLIS(_ nums: [Int]) -> Int { + let n = nums.count + var dp = Array(repeating: Array(repeating: 0, count: n + 1), count: n + 1) + + for i in stride(from: n - 1, through: 0, by: -1) { + for j in stride(from: i - 1, through: -1, by: -1) { + var LIS = dp[i + 1][j + 1] // Not including nums[i] + + if j == -1 || nums[j] < nums[i] { + LIS = max(LIS, 1 + dp[i + 1][i + 1]) // Including nums[i] + } + + dp[i][j + 1] = LIS + } + } + + return dp[0][0] + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -419,7 +943,7 @@ class Solution { --- -## 3. Dynamic Programming (Bottom-Up) +## 5. Dynamic Programming (Bottom-Up) - II ::tabs-start @@ -556,6 +1080,23 @@ class Solution { } ``` +```swift +class Solution { + func lengthOfLIS(_ nums: [Int]) -> Int { + var LIS = Array(repeating: 1, count: nums.count) + + for i in stride(from: nums.count - 1, through: 0, by: -1) { + for j in (i + 1)..> 1 + while j >= 1 { + tree[j] = max(tree[j << 1], tree[j << 1 | 1]) + j >>= 1 + } + } + + func query(_ l: Int, _ r: Int) -> Int { + if l > r { + return 0 + } + var res = Int.min + var l = l + n + var r = r + n + 1 + while l < r { + if l & 1 == 1 { + res = max(res, tree[l]) + l += 1 + } + if r & 1 == 1 { + r -= 1 + res = max(res, tree[r]) + } + l >>= 1 + r >>= 1 + } + return res + } +} + +class Solution { + func lengthOfLIS(_ nums: [Int]) -> Int { + func compress(_ arr: [Int]) -> [Int] { + let sortedArr = Array(Set(arr)).sorted() + return arr.map { sortedArr.firstIndex(of: $0)! } + } + + let nums = compress(nums) + let n = nums.count + let segTree = SegmentTree(n) + + var LIS = 0 + for num in nums { + let curLIS = segTree.query(0, num - 1) + 1 + segTree.update(num, curLIS) + LIS = max(LIS, curLIS) + } + return LIS + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -1059,7 +1667,7 @@ class Solution { --- -## 5. Binary Search +## 7. Dynamic Programming + Binary Search ::tabs-start @@ -1237,6 +1845,39 @@ class Solution { } ``` +```swift +class Solution { + func lengthOfLIS(_ nums: [Int]) -> Int { + var dp = [nums[0]] + var LIS = 1 + + for i in 1.. Int { + var left = 0, right = dp.count - 1 + while left < right { + let mid = (left + right) / 2 + if dp[mid] < target { + left = mid + 1 + } else { + right = mid + } + } + return left + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/longest-palindromic-substring.md b/articles/longest-palindromic-substring.md index 715d7c512..1d0f82c17 100644 --- a/articles/longest-palindromic-substring.md +++ b/articles/longest-palindromic-substring.md @@ -180,12 +180,38 @@ class Solution { } ``` +```swift +class Solution { + func longestPalindrome(_ s: String) -> String { + let chars = Array(s) + var res = "" + var resLen = 0 + + for i in 0..= r && resLen < (j - i + 1) { + res = String(chars[i...j]) + resLen = j - i + 1 + } + } + } + return res + } +} +``` + ::tabs-end ### Time & Space Complexity * Time complexity: $O(n ^ 3)$ -* Space complexity: $O(1)$ +* Space complexity: $O(n)$ --- @@ -377,6 +403,31 @@ class Solution { } ``` +```swift +class Solution { + func longestPalindrome(_ s: String) -> String { + let chars = Array(s) + let n = chars.count + var resIdx = 0, resLen = 0 + var dp = Array(repeating: Array(repeating: false, count: n), count: n) + + for i in stride(from: n - 1, through: 0, by: -1) { + for j in i.. String { + let chars = Array(s) + var resIdx = 0 + var resLen = 0 + + for i in 0..= 0 && r < chars.count && chars[l] == chars[r] { + if (r - l + 1) > resLen { + resIdx = l + resLen = r - l + 1 + } + l -= 1 + r += 1 + } + + // Even length palindrome + l = i + r = i + 1 + while l >= 0 && r < chars.count && chars[l] == chars[r] { + if (r - l + 1) > resLen { + resIdx = l + resLen = r - l + 1 + } + l -= 1 + r += 1 + } + } + + return String(chars[resIdx.. String { + func manacher(_ s: String) -> [Int] { + let t = "#" + s.map { String($0) }.joined(separator: "#") + "#" + let tArray = Array(t) + let n = tArray.count + var p = [Int](repeating: 0, count: n) + var l = 0, r = 0 + + for i in 0..= 0 && + tArray[i + p[i] + 1] == tArray[i - p[i] - 1]) { + p[i] += 1 + } + if i + p[i] > r { + l = i - p[i] + r = i + p[i] + } + } + return p + } + + let p = manacher(s) + var resLen = 0, centerIndex = 0 + for (i, v) in p.enumerated() { + if v > resLen { + resLen = v + centerIndex = i + } + } + let resIdx = (centerIndex - resLen) / 2 + let start = s.index(s.startIndex, offsetBy: resIdx) + let end = s.index(start, offsetBy: resLen) + return String(s[start.. Int { + var res = 0 + let chars = Array(s) + + for i in 0.. Int { + var res = 0 + let charSet = Set(s) + let chars = Array(s) + + for c in charSet { + var count = 0, l = 0 + + for r in 0.. k { + if chars[l] == c { + count -= 1 + } + l += 1 + } + + res = max(res, r - l + 1) + } + } + return res + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -563,6 +617,30 @@ class Solution { } ``` +```swift +class Solution { + func characterReplacement(_ s: String, _ k: Int) -> Int { + var count = [Character: Int]() + var res = 0 + var l = 0, maxf = 0 + let chars = Array(s) + + for r in 0.. k { + count[chars[l], default: 0] -= 1 + l += 1 + } + res = max(res, r - l + 1) + } + + return res + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/longest-substring-without-duplicates.md b/articles/longest-substring-without-duplicates.md index a37747ed3..3d197dc21 100644 --- a/articles/longest-substring-without-duplicates.md +++ b/articles/longest-substring-without-duplicates.md @@ -137,6 +137,27 @@ class Solution { } ``` +```kotlin +class Solution { + func lengthOfLongestSubstring(_ s: String) -> Int { + var res = 0 + let chars = Array(s) + + for i in 0..() + for j in i.. Int { + var charSet = Set() + var l = 0, res = 0 + let chars = Array(s) + + for r in 0.. Int { + var mp = [Character: Int]() + var l = 0, res = 0 + let chars = Array(s) + + for r in 0.. TreeNode? { + guard let root = root, let p = p, let q = q else { + return nil + } + + if max(p.val, q.val) < root.val { + return lowestCommonAncestor(root.left, p, q) + } else if min(p.val, q.val) > root.val { + return lowestCommonAncestor(root.right, p, q) + } else { + return root + } + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -436,6 +468,41 @@ class Solution { } ``` +```swift +/** + * Definition for a binary tree node. + * public class TreeNode { + * public var val: Int + * public var left: TreeNode? + * public var right: TreeNode? + * public init(_ val: Int) { + * self.val = val + * self.left = nil + * self.right = nil + * } + * } + */ + +class Solution { + func lowestCommonAncestor(_ root: TreeNode?, _ p: TreeNode?, _ q: TreeNode?) -> TreeNode? { + var cur = root + + while let node = cur { + if let p = p, let q = q { + if p.val > node.val && q.val > node.val { + cur = node.right + } else if p.val < node.val && q.val < node.val { + cur = node.left + } else { + return node + } + } + } + return nil + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/lru-cache.md b/articles/lru-cache.md index d93c79886..2bd939f9c 100644 --- a/articles/lru-cache.md +++ b/articles/lru-cache.md @@ -279,6 +279,46 @@ class LRUCache(capacity: Int) { } ``` +```swift +class LRUCache { + private var cache: [(Int, Int)] + private let capacity: Int + + init(_ capacity: Int) { + self.cache = [] + self.capacity = capacity + } + + func get(_ key: Int) -> Int { + for i in 0.. Int { + if let node = cache[key] { + remove(node) + insert(node) + return node.val + } + return -1 + } + + func put(_ key: Int, _ value: Int) { + if let node = cache[key] { + remove(node) + } + let newNode = Node(key, value) + cache[key] = newNode + insert(newNode) + + if cache.count > cap { + if let lru = left.next { + remove(lru) + cache.removeValue(forKey: lru.key) + } + } + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -1002,6 +1112,45 @@ class LRUCache(capacity: Int) { } ``` +```swift +class LRUCache { + private var capacity: Int + private var cache: [Int: Int] = [:] + private var keyOrder: [Int] = [] + + init(_ capacity: Int) { + self.capacity = capacity + } + + func get(_ key: Int) -> Int { + if let value = cache[key] { + if let index = keyOrder.firstIndex(of: key) { + keyOrder.remove(at: index) + } + keyOrder.append(key) + return value + } + return -1 + } + + func put(_ key: Int, _ value: Int) { + if cache[key] != nil { + if let index = keyOrder.firstIndex(of: key) { + keyOrder.remove(at: index) + } + } else if cache.count >= capacity { + if let lruKey = keyOrder.first { + cache.removeValue(forKey: lruKey) + keyOrder.removeFirst() + } + } + + cache[key] = value + keyOrder.append(key) + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/max-area-of-island.md b/articles/max-area-of-island.md index 22f9f8b53..cb69ca56d 100644 --- a/articles/max-area-of-island.md +++ b/articles/max-area-of-island.md @@ -232,6 +232,34 @@ class Solution { } ``` +```swift +class Solution { + func maxAreaOfIsland(_ grid: [[Int]]) -> Int { + let ROWS = grid.count + let COLS = grid[0].count + var visit = Set<[Int]>() + var grid = grid + + func dfs(_ r: Int, _ c: Int) -> Int { + if (r < 0 || r == ROWS || c < 0 || c == COLS || + grid[r][c] == 0 || visit.contains([r, c])) { + return 0 + } + visit.insert([r, c]) + return 1 + dfs(r + 1, c) + dfs(r - 1, c) + dfs(r, c + 1) + dfs(r, c - 1) + } + + var area = 0 + for r in 0.. Int { + let directions = [[1, 0], [-1, 0], [0, 1], [0, -1]] + let ROWS = grid.count + let COLS = grid[0].count + var grid = grid + var area = 0 + + func bfs(_ r: Int, _ c: Int) -> Int { + var queue = Deque<(Int, Int)>() + grid[r][c] = 0 + queue.append((r, c)) + var res = 1 + + while !queue.isEmpty { + let (row, col) = queue.popFirst()! + for dir in directions { + let nr = row + dir[0] + let nc = col + dir[1] + if nr < 0 || nc < 0 || nr >= ROWS || nc >= COLS || grid[nr][nc] == 0 { + continue + } + queue.append((nr, nc)) + grid[nr][nc] = 0 + res += 1 + } + } + return res + } + + for r in 0.. Int { + if parent[node] != node { + parent[node] = find(parent[node]) + } + return parent[node] + } + + func union(_ u: Int, _ v: Int) -> Bool { + let pu = find(u) + let pv = find(v) + if pu == pv { + return false + } + if size[pu] >= size[pv] { + size[pu] += size[pv] + parent[pv] = pu + } else { + size[pv] += size[pu] + parent[pu] = pv + } + return true + } + + func getSize(_ node: Int) -> Int { + let par = find(node) + return size[par] + } +} + +class Solution { + func maxAreaOfIsland(_ grid: [[Int]]) -> Int { + var grid = grid + let ROWS = grid.count + let COLS = grid[0].count + let dsu = DSU(ROWS * COLS) + + func index(_ r: Int, _ c: Int) -> Int { + return r * COLS + c + } + + let directions = [(1, 0), (-1, 0), (0, 1), (0, -1)] + var area = 0 + + for r in 0..= ROWS || nc >= COLS || grid[nr][nc] == 0 { + continue + } + dsu.union(index(r, c), index(nr, nc)) + } + area = max(area, dsu.getSize(index(r, c))) + } + } + } + + return area + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/max-water-container.md b/articles/max-water-container.md index dfd046f07..bb1ea6083 100644 --- a/articles/max-water-container.md +++ b/articles/max-water-container.md @@ -110,6 +110,20 @@ class Solution { } ``` +```swift +class Solution { + func maxArea(_ heights: [Int]) -> Int { + var res = 0 + for i in 0.. Int { + var l = 0, r = heights.count - 1 + var res = 0 + + while l < r { + let area = min(heights[l], heights[r]) * (r - l) + res = max(res, area) + if heights[l] <= heights[r] { + l += 1 + } else { + r -= 1 + } + } + return res + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/maximum-product-subarray.md b/articles/maximum-product-subarray.md index 5f5f0e299..f163b341f 100644 --- a/articles/maximum-product-subarray.md +++ b/articles/maximum-product-subarray.md @@ -137,6 +137,25 @@ class Solution { } ``` +```swift +class Solution { + func maxProduct(_ nums: [Int]) -> Int { + var res = nums[0] + + for i in 0.. Int { + var A = [[Int]]() + var cur = [Int]() + var res = Int.min + + for num in nums { + res = max(res, num) + if num == 0 { + if !cur.isEmpty { + A.append(cur) + } + cur = [] + } else { + cur.append(num) + } + } + + if !cur.isEmpty { + A.append(cur) + } + + for sub in A { + let negsCount = sub.filter { $0 < 0 }.count + var prod = 1 + var need = negsCount % 2 == 0 ? negsCount : negsCount - 1 + var negs = 0 + var j = 0 + + for i in 0.. need { + prod /= sub[j] + if sub[j] < 0 { + negs -= 1 + } + j += 1 + } + } + if j <= i { + res = max(res, prod) + } + } + } + + return res + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -642,6 +714,23 @@ class Solution { } ``` +```swift +class Solution { + func maxProduct(_ nums: [Int]) -> Int { + var res = nums[0] + var curMin = 1, curMax = 1 + + for num in nums { + let tmp = curMax * num + curMax = max(num * curMax, num * curMin, num) + curMin = min(tmp, num * curMin, num) + res = max(res, curMax) + } + return res + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -791,6 +880,23 @@ class Solution { } ``` +```swift +class Solution { + func maxProduct(_ nums: [Int]) -> Int { + let n = nums.count + var res = nums[0] + var prefix = 0, suffix = 0 + + for i in 0.. Int { + let n = nums.count + var res = nums[0] + + for i in 0.. Int { + func dfs(_ i: Int, _ flag: Bool) -> Int { + if i == nums.count { + return flag ? 0 : Int.min + } + + if flag { + return max(0, nums[i] + dfs(i + 1, true)) + } + + return max(dfs(i + 1, false), nums[i] + dfs(i + 1, true)) + } + + return dfs(0, false) + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -449,6 +488,33 @@ class Solution { } ``` +```swift +class Solution { + func maxSubArray(_ nums: [Int]) -> Int { + var memo = Array(repeating: [Int?](repeating: nil, count: 2), count: nums.count + 1) + + func dfs(_ i: Int, _ flag: Bool) -> Int { + if i == nums.count { + return flag ? 0 : Int.min + } + if let value = memo[i][flag ? 1 : 0] { + return value + } + + if flag { + memo[i][flag ? 1 : 0] = max(0, nums[i] + dfs(i + 1, true)) + } else { + memo[i][flag ? 1 : 0] = max(dfs(i + 1, false), nums[i] + dfs(i + 1, true)) + } + + return memo[i][flag ? 1 : 0]! + } + + return dfs(0, false) + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -594,6 +660,24 @@ class Solution { } ``` +```swift +class Solution { + func maxSubArray(_ nums: [Int]) -> Int { + let n = nums.count + var dp = Array(repeating: [0, 0], count: n) + dp[n - 1][1] = nums[n - 1] + dp[n - 1][0] = nums[n - 1] + + for i in (0.. Int { + var dp = nums + + for i in 1.. Int { + var maxSub = nums[0] + var curSum = 0 + + for num in nums { + if curSum < 0 { + curSum = 0 + } + curSum += num + maxSub = max(maxSub, curSum) + } + + return maxSub + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -1092,6 +1209,39 @@ class Solution { } ``` +```swift +class Solution { + func maxSubArray(_ nums: [Int]) -> Int { + + func dfs(_ l: Int, _ r: Int) -> Int { + if l > r { + return Int.min + } + + let m = (l + r) / 2 + var leftSum = 0 + var rightSum = 0 + var curSum = 0 + + for i in stride(from: m - 1, through: l, by: -1) { + curSum += nums[i] + leftSum = max(leftSum, curSum) + } + + curSum = 0 + for i in stride(from: m + 1, to: r + 1, by: 1) { + curSum += nums[i] + rightSum = max(rightSum, curSum) + } + + return max(dfs(l, m - 1), dfs(m + 1, r), leftSum + nums[m] + rightSum) + } + + return dfs(0, nums.count - 1) + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/median-of-two-sorted-arrays.md b/articles/median-of-two-sorted-arrays.md index 6fb01b6af..3ac051111 100644 --- a/articles/median-of-two-sorted-arrays.md +++ b/articles/median-of-two-sorted-arrays.md @@ -127,6 +127,22 @@ class Solution { } ``` +```swift +class Solution { + func findMedianSortedArrays(_ nums1: [Int], _ nums2: [Int]) -> Double { + var merged = nums1 + nums2 + merged.sort() + + let totalLen = merged.count + if totalLen % 2 == 0 { + return (Double(merged[totalLen / 2 - 1]) + Double(merged[totalLen / 2])) / 2.0 + } else { + return Double(merged[totalLen / 2]) + } + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -391,6 +407,41 @@ class Solution { } ``` +```kotlin +class Solution { + func findMedianSortedArrays(_ nums1: [Int], _ nums2: [Int]) -> Double { + let len1 = nums1.count, len2 = nums2.count + var i = 0, j = 0 + var median1 = 0, median2 = 0 + + for _ in 0..<(len1 + len2) / 2 + 1 { + median2 = median1 + if i < len1 && j < len2 { + if nums1[i] > nums2[j] { + median1 = nums2[j] + j += 1 + } else { + median1 = nums1[i] + i += 1 + } + } else if i < len1 { + median1 = nums1[i] + i += 1 + } else { + median1 = nums2[j] + j += 1 + } + } + + if (len1 + len2) % 2 == 1 { + return Double(median1) + } else { + return (Double(median1) + Double(median2)) / 2.0 + } + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -653,12 +704,44 @@ class Solution { } ``` +```swift +class Solution { + func getKth(_ a: [Int], _ m: Int, _ b: [Int], _ n: Int, _ k: Int, _ aStart: Int = 0, _ bStart: Int = 0) -> Int { + if m > n { + return getKth(b, n, a, m, k, bStart, aStart) + } + if m == 0 { + return b[bStart + k - 1] + } + if k == 1 { + return min(a[aStart], b[bStart]) + } + + let i = min(m, k / 2) + let j = min(n, k / 2) + + if a[aStart + i - 1] > b[bStart + j - 1] { + return getKth(a, m, b, n - j, k - j, aStart, bStart + j) + } else { + return getKth(a, m - i, b, n, k - i, aStart + i, bStart) + } + } + + func findMedianSortedArrays(_ nums1: [Int], _ nums2: [Int]) -> Double { + let left = (nums1.count + nums2.count + 1) / 2 + let right = (nums1.count + nums2.count + 2) / 2 + return (Double(getKth(nums1, nums1.count, nums2, nums2.count, left)) + + Double(getKth(nums1, nums1.count, nums2, nums2.count, right))) / 2.0 + } +} +``` + ::tabs-end ### Time & Space Complexity * Time complexity: $O(\log (m + n))$ -* Space complexity: $O(\log (m + n))$ +* Space complexity: $O(\log (m + n))$ for recursion stack. > Where $n$ is the length of $nums1$ and $m$ is the length of $nums2$. @@ -969,6 +1052,44 @@ class Solution { } ``` +```swift +class Solution { + func findMedianSortedArrays(_ nums1: [Int], _ nums2: [Int]) -> Double { + var A = nums1, B = nums2 + if A.count > B.count { + swap(&A, &B) + } + + let total = A.count + B.count + let half = total / 2 + var l = 0 + var r = A.count + + while true { + let i = (l + r) / 2 + let j = half - i + + let Aleft = i > 0 ? Double(A[i - 1]) : -Double.infinity + let Aright = i < A.count ? Double(A[i]) : Double.infinity + let Bleft = j > 0 ? Double(B[j - 1]) : -Double.infinity + let Bright = j < B.count ? Double(B[j]) : Double.infinity + + if Aleft <= Bright && Bleft <= Aright { + if total % 2 == 1 { + return min(Aright, Bright) + } else { + return (max(Aleft, Bleft) + min(Aright, Bright)) / 2.0 + } + } else if Aleft > Bright { + r = i - 1 + } else { + l = i + 1 + } + } + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/meeting-schedule-ii.md b/articles/meeting-schedule-ii.md index ce53b1e92..afd50a48e 100644 --- a/articles/meeting-schedule-ii.md +++ b/articles/meeting-schedule-ii.md @@ -193,6 +193,34 @@ class Solution { } ``` +```swift +/** + * Definition of Interval: + * class Interval { + * var start: Int + * var end: Int + * init(start: Int, end: Int) { + * self.start = start + * self.end = end + * } + * } + */ + +class Solution { + func minMeetingRooms(_ intervals: [Interval]) -> Int { + let sortedIntervals = intervals.sorted { $0.start < $1.start } + var minHeap = Heap() + for interval in sortedIntervals { + if !minHeap.isEmpty, let earliest = minHeap.min!, earliest <= interval.start { + minHeap.removeMin() + } + minHeap.insert(interval.end) + } + return minHeap.count + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -414,6 +442,37 @@ class Solution { } ``` +```swift +/** + * Definition of Interval: + * class Interval { + * var start: Int + * var end: Int + * init(start: Int, end: Int) { + * self.start = start + * self.end = end + * } + * } + */ + +class Solution { + func minMeetingRooms(_ intervals: [Interval]) -> Int { + var mp = [Int: Int]() + for interval in intervals { + mp[interval.start, default: 0] += 1 + mp[interval.end, default: 0] -= 1 + } + var prev = 0 + var res = 0 + for key in mp.keys.sorted() { + prev += mp[key]! + res = max(res, prev) + } + return res + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -689,6 +748,40 @@ class Solution { } ``` +```swift +/** + * Definition of Interval: + * class Interval { + * var start: Int + * var end: Int + * init(start: Int, end: Int) { + * self.start = start + * self.end = end + * } + * } + */ + +class Solution { + func minMeetingRooms(_ intervals: [Interval]) -> Int { + let starts = intervals.map { $0.start }.sorted() + let ends = intervals.map { $0.end }.sorted() + + var res = 0, count = 0, s = 0, e = 0 + while s < intervals.count { + if starts[s] < ends[e] { + count += 1 + s += 1 + } else { + count -= 1 + e += 1 + } + res = max(res, count) + } + return res + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -929,6 +1022,47 @@ class Solution { } ``` +```swift +/** + * Definition of Interval: + * class Interval { + * var start: Int + * var end: Int + * init(start: Int, end: Int) { + * self.start = start + * self.end = end + * } + * } + */ + +class Solution { + func minMeetingRooms(_ intervals: [Interval]) -> Int { + var times = [(Int, Int)]() + for interval in intervals { + times.append((interval.start, 1)) + times.append((interval.end, -1)) + } + + times.sort { + if $0.0 != $1.0 { + return $0.0 < $1.0 + } else { + return $0.1 < $1.1 + } + } + + var count = 0 + var res = 0 + for t in times { + count += t.1 + res = max(res, count) + } + + return res + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/meeting-schedule.md b/articles/meeting-schedule.md index 735df876a..226a6a70f 100644 --- a/articles/meeting-schedule.md +++ b/articles/meeting-schedule.md @@ -205,6 +205,36 @@ class Solution { } ``` +```swift +/** + * Definition of Interval: + * class Interval { + * var start: Int + * var end: Int + * init(start: Int, end: Int) { + * self.start = start + * self.end = end + * } + * } + */ + +class Solution { + func canAttendMeetings(_ intervals: [Interval]) -> Bool { + let n = intervals.count + for i in 0.. max(A.start, B.start) { + return false + } + } + } + return true + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -400,6 +430,32 @@ class Solution { } ``` +```swift +/** + * Definition of Interval: + * class Interval { + * var start: Int + * var end: Int + * init(start: Int, end: Int) { + * self.start = start + * self.end = end + * } + * } + */ + +class Solution { + func canAttendMeetings(_ intervals: [Interval]) -> Bool { + let sortedIntervals = intervals.sorted { $0.start < $1.start } + for i in 1.. sortedIntervals[i].start { + return false + } + } + return true + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/merge-intervals.md b/articles/merge-intervals.md index 4ff97b569..100d894b6 100644 --- a/articles/merge-intervals.md +++ b/articles/merge-intervals.md @@ -164,6 +164,28 @@ class Solution { } ``` +```swift +class Solution { + func merge(_ intervals: [[Int]]) -> [[Int]] { + let intervals = intervals.sorted { $0[0] < $1[0] } + var output: [[Int]] = [intervals[0]] + + for interval in intervals { + let start = interval[0] + let end = interval[1] + var lastEnd = output.last![1] + + if start <= lastEnd { + output[output.count - 1][1] = max(lastEnd, end) + } else { + output.append([start, end]) + } + } + return output + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -383,6 +405,38 @@ class Solution { } ``` +```swift +class Solution { + func merge(_ intervals: [[Int]]) -> [[Int]] { + var mp = [Int: Int]() + + for interval in intervals { + let start = interval[0] + let end = interval[1] + mp[start, default: 0] += 1 + mp[end, default: 0] -= 1 + } + + var res = [[Int]]() + var interval = [Int]() + var have = 0 + + for i in mp.keys.sorted() { + if interval.isEmpty { + interval.append(i) + } + have += mp[i, default: 0] + if have == 0 { + interval.append(i) + res.append(interval) + interval = [] + } + } + return res + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -678,6 +732,44 @@ class Solution { } ``` +```swift +class Solution { + func merge(_ intervals: [[Int]]) -> [[Int]] { + let maxVal = intervals.map { $0[0] }.max() ?? 0 + var mp = [Int](repeating: 0, count: maxVal + 1) + for interval in intervals { + let start = interval[0] + let end = interval[1] + mp[start] = max(end + 1, mp[start]) + } + + var res = [[Int]]() + var have = -1 + var intervalStart = -1 + + for i in 0.. ListNode? { + var nodes: [Int] = [] + + for list in lists { + var lst = list + while lst != nil { + nodes.append(lst!.val) + lst = lst?.next + } + } + + nodes.sort() + + let dummy = ListNode(0) + var cur = dummy + for node in nodes { + cur.next = ListNode(node) + cur = cur.next! + } + + return dummy.next + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -510,6 +547,47 @@ class Solution { } ``` +```swift +/** + * Definition for singly-linked list. + * public class ListNode { + * public var val: Int + * public var next: ListNode? + * public init() { self.val = 0; self.next = nil; } + * public init(_ val: Int) { self.val = val; self.next = nil; } + * public init(_ val: Int, _ next: ListNode?) { self.val = val; self.next = next; } + * } + */ +class Solution { + func mergeKLists(_ lists: [ListNode?]) -> ListNode? { + var lists = lists + let dummy = ListNode(0) + var cur = dummy + + while true { + var minNodeIndex: Int? = nil + for i in 0.. ListNode? { + if lists.isEmpty { + return nil + } + + var lists = lists + for i in 1.. ListNode? { + let dummy = ListNode(0) + var tail = dummy + var l1 = l1, l2 = l2 + + while l1 != nil && l2 != nil { + if l1!.val < l2!.val { + tail.next = l1 + l1 = l1?.next + } else { + tail.next = l2 + l2 = l2?.next + } + tail = tail.next! + } + + if l1 != nil { + tail.next = l1 + } + if l2 != nil { + tail.next = l2 + } + + return dummy.next + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -1157,6 +1288,60 @@ class Solution { } ``` +```swift +/** + * Definition for singly-linked list. + * public class ListNode { + * public var val: Int + * public var next: ListNode? + * public init() { self.val = 0; self.next = nil; } + * public init(_ val: Int) { self.val = val; self.next = nil; } + * public init(_ val: Int, _ next: ListNode?) { self.val = val; self.next = next; } + * } + */ +struct NodeWrapper: Comparable { + let node: ListNode + + init(_ node: ListNode) { + self.node = node + } + + static func < (lhs: NodeWrapper, rhs: NodeWrapper) -> Bool { + return lhs.node.val < rhs.node.val + } + + static func == (lhs: NodeWrapper, rhs: NodeWrapper) -> Bool { + return lhs.node.val == rhs.node.val + } +} + +class Solution { + func mergeKLists(_ lists: [ListNode?]) -> ListNode? { + var heap = Heap() + + for list in lists { + if let node = list { + heap.insert(NodeWrapper(node)) + } + } + + let dummy = ListNode(0) + var tail = dummy + + while let wrapper = heap.popMin() { + tail.next = wrapper.node + tail = tail.next! + + if let next = wrapper.node.next { + heap.insert(NodeWrapper(next)) + } + } + + return dummy.next + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -1190,14 +1375,17 @@ class Solution: return None if l == r: return lists[l] + mid = l + (r - l) // 2 left = self.divide(lists, l, mid) right = self.divide(lists, mid + 1, r) + return self.conquer(left, right) def conquer(self, l1, l2): dummy = ListNode(0) curr = dummy + while l1 and l2: if l1.val <= l2.val: curr.next = l1 @@ -1206,10 +1394,12 @@ class Solution: curr.next = l2 l2 = l2.next curr = curr.next + if l1: curr.next = l1 else: curr.next = l2 + return dummy.next ``` @@ -1240,9 +1430,11 @@ class Solution { if (l == r) { return lists[l]; } + int mid = l + (r - l) / 2; ListNode left = divide(lists, l, mid); ListNode right = divide(lists, mid + 1, r); + return conquer(left, right); } @@ -1302,15 +1494,18 @@ private: if (l == r) { return lists[l]; } + int mid = l + (r - l) / 2; ListNode* left = divide(lists, l, mid); ListNode* right = divide(lists, mid + 1, r); + return conquer(left, right); } ListNode* conquer(ListNode* l1, ListNode* l2) { ListNode dummy(0); ListNode* curr = &dummy; + while (l1 && l2) { if (l1->val <= l2->val) { curr->next = l1; @@ -1321,11 +1516,13 @@ private: } curr = curr->next; } + if (l1) { curr->next = l1; } else { curr->next = l2; } + return dummy.next; } }; @@ -1367,9 +1564,11 @@ class Solution { if (l === r) { return lists[l]; } + const mid = Math.floor(l + (r - l) / 2); const left = this.divide(lists, l, mid); const right = this.divide(lists, mid + 1, r); + return this.conquer(left, right); } @@ -1381,6 +1580,7 @@ class Solution { conquer(l1, l2) { const dummy = new ListNode(0); let curr = dummy; + while (l1 && l2) { if (l1.val <= l2.val) { curr.next = l1; @@ -1391,6 +1591,7 @@ class Solution { } curr = curr.next; } + curr.next = l1 ? l1 : l2; return dummy.next; } @@ -1425,9 +1626,11 @@ public class Solution { if (l == r) { return lists[l]; } + int mid = l + (r - l) / 2; ListNode left = Divide(lists, l, mid); ListNode right = Divide(lists, mid + 1, r); + return Conquer(left, right); } @@ -1479,15 +1682,18 @@ func divide(lists []*ListNode, left, right int) *ListNode { if left == right { return lists[left] } + mid := left + (right-left)/2 l1 := divide(lists, left, mid) l2 := divide(lists, mid+1, right) + return conquer(l1, l2) } func conquer(l1, l2 *ListNode) *ListNode { dummy := &ListNode{} curr := dummy + for l1 != nil && l2 != nil { if l1.Val <= l2.Val { curr.Next = l1 @@ -1498,11 +1704,13 @@ func conquer(l1, l2 *ListNode) *ListNode { } curr = curr.Next } + if l1 != nil { curr.Next = l1 } else { curr.Next = l2 } + return dummy.Next } ``` @@ -1526,9 +1734,11 @@ class Solution { private fun divide(lists: Array, left: Int, right: Int): ListNode? { if (left > right) return null if (left == right) return lists[left] + val mid = left + (right - left) / 2 val l1 = divide(lists, left, mid) val l2 = divide(lists, mid + 1, right) + return conquer(l1, l2) } @@ -1537,6 +1747,7 @@ class Solution { var curr = dummy var list1 = l1 var list2 = l2 + while (list1 != null && list2 != null) { if (list1.`val` <= list2.`val`) { curr.next = list1 @@ -1547,12 +1758,68 @@ class Solution { } curr = curr.next!! } + curr.next = list1 ?: list2 return dummy.next } } ``` +```swift +/** + * Definition for singly-linked list. + * public class ListNode { + * public var val: Int + * public var next: ListNode? + * public init() { self.val = 0; self.next = nil; } + * public init(_ val: Int) { self.val = val; self.next = nil; } + * public init(_ val: Int, _ next: ListNode?) { self.val = val; self.next = next; } + * } + */ +class Solution { + func mergeKLists(_ lists: [ListNode?]) -> ListNode? { + if lists.isEmpty { + return nil + } + return divide(lists, 0, lists.count - 1) + } + + private func divide(_ lists: [ListNode?], _ l: Int, _ r: Int) -> ListNode? { + if l > r { + return nil + } + if l == r { + return lists[l] + } + + let mid = l + (r - l) / 2 + let left = divide(lists, l, mid) + let right = divide(lists, mid + 1, r) + return conquer(left, right) + } + + private func conquer(_ l1: ListNode?, _ l2: ListNode?) -> ListNode? { + let dummy = ListNode(0) + var curr = dummy + var l1 = l1, l2 = l2 + + while let node1 = l1, let node2 = l2 { + if node1.val <= node2.val { + curr.next = node1 + l1 = node1.next + } else { + curr.next = node2 + l2 = node2.next + } + curr = curr.next! + } + + curr.next = l1 ?? l2 + return dummy.next + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -1602,10 +1869,12 @@ class Solution: tail.next = l2 l2 = l2.next tail = tail.next + if l1: tail.next = l1 if l2: tail.next = l2 + return dummy.next ``` @@ -1653,12 +1922,14 @@ public class Solution { } tail = tail.next; } + if (l1 != null) { tail.next = l1; } if (l2 != null) { tail.next = l2; } + return dummy.next; } } @@ -1710,12 +1981,14 @@ private: } tail = tail->next; } + if (l1) { tail->next = l1; } if (l2) { tail->next = l2; } + return dummy.next; } }; @@ -1762,6 +2035,7 @@ class Solution { mergeList(l1, l2) { const dummy = new ListNode(0); let curr = dummy; + while (l1 && l2) { if (l1.val <= l2.val) { curr.next = l1; @@ -1772,6 +2046,7 @@ class Solution { } curr = curr.next; } + curr.next = l1 ? l1 : l2; return dummy.next; } @@ -1823,12 +2098,14 @@ public class Solution { } tail = tail.next; } + if (l1 != null) { tail.next = l1; } if (l2 != null) { tail.next = l2; } + return dummy.next; } } @@ -1876,11 +2153,13 @@ func mergeList(l1, l2 *ListNode) *ListNode { } tail = tail.Next } + if l1 != nil { tail.Next = l1 } else { tail.Next = l2 } + return dummy.Next } ``` @@ -1928,12 +2207,69 @@ class Solution { } tail = tail.next!! } + tail.next = list1 ?: list2 return dummy.next } } ``` +```swift +/** + * Definition for singly-linked list. + * public class ListNode { + * public var val: Int + * public var next: ListNode? + * public init() { self.val = 0; self.next = nil; } + * public init(_ val: Int) { self.val = val; self.next = nil; } + * public init(_ val: Int, _ next: ListNode?) { self.val = val; self.next = next; } + * } + */ +class Solution { + func mergeKLists(_ lists: [ListNode?]) -> ListNode? { + if lists.isEmpty { + return nil + } + + var lists = lists + + while lists.count > 1 { + var mergedLists: [ListNode?] = [] + + for i in stride(from: 0, to: lists.count, by: 2) { + let l1 = lists[i] + let l2 = i + 1 < lists.count ? lists[i + 1] : nil + mergedLists.append(mergeList(l1, l2)) + } + + lists = mergedLists + } + + return lists[0] + } + + private func mergeList(_ l1: ListNode?, _ l2: ListNode?) -> ListNode? { + let dummy = ListNode(0) + var tail = dummy + var l1 = l1, l2 = l2 + + while let node1 = l1, let node2 = l2 { + if node1.val < node2.val { + tail.next = node1 + l1 = node1.next + } else { + tail.next = node2 + l2 = node2.next + } + tail = tail.next! + } + + tail.next = l1 ?? l2 + return dummy.next + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/merge-triplets-to-form-target.md b/articles/merge-triplets-to-form-target.md index 4fe080b4c..268d8ae9d 100644 --- a/articles/merge-triplets-to-form-target.md +++ b/articles/merge-triplets-to-form-target.md @@ -138,6 +138,27 @@ class Solution { } ``` +```swift +class Solution { + func mergeTriplets(_ triplets: [[Int]], _ target: [Int]) -> Bool { + var good = Set() + + for t in triplets { + if t[0] > target[0] || t[1] > target[1] || t[2] > target[2] { + continue + } + for (i, v) in t.enumerated() { + if v == target[i] { + good.insert(i) + } + } + } + + return good.count == 3 + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -268,6 +289,28 @@ class Solution { } ``` +```swift +class Solution { + func mergeTriplets(_ triplets: [[Int]], _ target: [Int]) -> Bool { + var x = false, y = false, z = false + + for t in triplets { + if t[0] <= target[0] && t[1] <= target[1] && t[2] <= target[2] { + if t[0] == target[0] { x = true } + if t[1] == target[1] { y = true } + if t[2] == target[2] { z = true } + } + + if x && y && z { + return true + } + } + + return false + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/merge-two-sorted-linked-lists.md b/articles/merge-two-sorted-linked-lists.md index bffc3e16f..b65407ade 100644 --- a/articles/merge-two-sorted-linked-lists.md +++ b/articles/merge-two-sorted-linked-lists.md @@ -203,6 +203,36 @@ class Solution { } ``` +```swift +/** + * Definition for singly-linked list. + * public class ListNode { + * public var val: Int + * public var next: ListNode? + * public init() { self.val = 0; self.next = nil; } + * public init(_ val: Int) { self.val = val; self.next = nil; } + * public init(_ val: Int, _ next: ListNode?) { self.val = val; self.next = next; } + * } + */ +class Solution { + func mergeTwoLists(_ list1: ListNode?, _ list2: ListNode?) -> ListNode? { + if list1 == nil { + return list2 + } + if list2 == nil { + return list1 + } + if list1!.val <= list2!.val { + list1!.next = mergeTwoLists(list1!.next, list2) + return list1 + } else { + list2!.next = mergeTwoLists(list1, list2!.next) + return list2 + } + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -473,6 +503,42 @@ class Solution { } ``` +```swift +/** + * Definition for singly-linked list. + * public class ListNode { + * public var val: Int + * public var next: ListNode? + * public init() { self.val = 0; self.next = nil; } + * public init(_ val: Int) { self.val = val; self.next = nil; } + * public init(_ val: Int, _ next: ListNode?) { self.val = val; self.next = next; } + * } + */ +class Solution { + func mergeTwoLists(_ list1: ListNode?, _ list2: ListNode?) -> ListNode? { + let dummy = ListNode(0) + var node = dummy + var l1 = list1 + var l2 = list2 + + while l1 != nil && l2 != nil { + if l1!.val < l2!.val { + node.next = l1 + l1 = l1?.next + } else { + node.next = l2 + l2 = l2?.next + } + node = node.next! + } + + node.next = l1 ?? l2 + + return dummy.next + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/min-cost-climbing-stairs.md b/articles/min-cost-climbing-stairs.md index 3c7139726..9433d007b 100644 --- a/articles/min-cost-climbing-stairs.md +++ b/articles/min-cost-climbing-stairs.md @@ -118,6 +118,21 @@ class Solution { } ``` +```swift +class Solution { + func minCostClimbingStairs(_ cost: [Int]) -> Int { + func dfs(_ i: Int) -> Int { + if i >= cost.count { + return 0 + } + return cost[i] + min(dfs(i + 1), dfs(i + 2)) + } + + return min(dfs(0), dfs(1)) + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -291,6 +306,27 @@ class Solution { } ``` +```swift +class Solution { + func minCostClimbingStairs(_ cost: [Int]) -> Int { + var memo = Array(repeating: -1, count: cost.count) + + func dfs(_ i: Int) -> Int { + if i >= cost.count { + return 0 + } + if memo[i] != -1 { + return memo[i] + } + memo[i] = cost[i] + min(dfs(i + 1), dfs(i + 2)) + return memo[i] + } + + return min(dfs(0), dfs(1)) + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -423,6 +459,22 @@ class Solution { } ``` +```swift +class Solution { + func minCostClimbingStairs(_ cost: [Int]) -> Int { + let n = cost.count + var dp = Array(repeating: 0, count: n + 1) + + for i in 2...n { + dp[i] = min(dp[i - 1] + cost[i - 1], + dp[i - 2] + cost[i - 2]) + } + + return dp[n] + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -523,6 +575,17 @@ class Solution { } ``` +```swift +class Solution { + func minCostClimbingStairs(_ cost: inout [Int]) -> Int { + for i in stride(from: cost.count - 3, through: 0, by: -1) { + cost[i] += min(cost[i + 1], cost[i + 2]) + } + return min(cost[0], cost[1]) + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/min-cost-to-connect-points.md b/articles/min-cost-to-connect-points.md index 8e9e5226a..6688ee3a1 100644 --- a/articles/min-cost-to-connect-points.md +++ b/articles/min-cost-to-connect-points.md @@ -45,7 +45,7 @@ class Solution: ``` ```java -public class DSU { +class DSU { int[] Parent, Size; public DSU(int n) { @@ -398,6 +398,68 @@ class Solution { } ``` +```swift +class DSU { + private var parent: [Int] + private var size: [Int] + + init(_ n: Int) { + parent = Array(0...n) + size = Array(repeating: 1, count: n + 1) + } + + func find(_ node: Int) -> Int { + if parent[node] != node { + parent[node] = find(parent[node]) + } + return parent[node] + } + + func union(_ u: Int, _ v: Int) -> Bool { + let pu = find(u) + let pv = find(v) + if pu == pv { + return false + } + if size[pu] < size[pv] { + parent[pu] = pv + size[pv] += size[pu] + } else { + parent[pv] = pu + size[pu] += size[pv] + } + return true + } +} + +class Solution { + func minCostConnectPoints(_ points: [[Int]]) -> Int { + let n = points.count + let dsu = DSU(n) + var edges = [(Int, Int, Int)]() + + for i in 0.. Bool { + return lhs.cost < rhs.cost + } +} + +class Solution { + func minCostConnectPoints(_ points: [[Int]]) -> Int { + let N = points.count + var adj = [Int: [(Int, Int)]]() + + for i in 0..() + var minHeap = Heap() + minHeap.insert(Item(cost: 0, node: 0)) + + while visit.count < N { + guard let item = minHeap.popMin() else { break } + let cost = item.cost + let i = item.node + + if visit.contains(i) { + continue + } + + res += cost + visit.insert(i) + + if let neighbors = adj[i] { + for (neiCost, nei) in neighbors { + if !visit.contains(nei) { + minHeap.insert(Item(cost: neiCost, node: nei)) + } + } + } + } + + return res + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -940,6 +1058,42 @@ class Solution { } ``` +```swift +class Solution { + func minCostConnectPoints(_ points: [[Int]]) -> Int { + let n = points.count + var node = 0 + var dist = Array(repeating: 100000000, count: n) + var visit = Array(repeating: false, count: n) + var edges = 0 + var res = 0 + + while edges < n - 1 { + visit[node] = true + var nextNode = -1 + + for i in 0.. [Int] { + var res = [Int]() + + for q in queries { + var cur = -1 + for interval in intervals { + let l = interval[0] + let r = interval[1] + + if l <= q && q <= r { + if cur == -1 || (r - l + 1) < cur { + cur = r - l + 1 + } + } + } + res.append(cur) + } + + return res + } +} +``` + ::tabs-end ### Time & Space Complexity * Time complexity: $O(m * n)$ -* Space complexity: $O(1)$ +* Space complexity: + * $O(1)$ extra space. + * $O(m)$ space for the output array. > Where $m$ is the length of the array $queries$ and $n$ is the length of the array $intervals$. @@ -508,6 +535,78 @@ class Solution { } ``` +```swift +struct Event { + let time: Int + let type: Int // 0: interval start, 1: query, 2: interval end + let size: Int? // interval size for start/end events + let idx: Int? // index for interval (for start/end) or query index (for query) +} + +struct Item: Comparable { + let size: Int + let idx: Int + + static func < (lhs: Item, rhs: Item) -> Bool { + return lhs.size < rhs.size + } +} + +class Solution { + func minInterval(_ intervals: [[Int]], _ queries: [Int]) -> [Int] { + var events = [Event]() + + // Create events for intervals + for (idx, interval) in intervals.enumerated() { + let start = interval[0] + let end = interval[1] + let intervalSize = end - start + 1 + events.append(Event(time: start, type: 0, size: intervalSize, idx: idx)) + events.append(Event(time: end, type: 2, size: intervalSize, idx: idx)) + } + + // Create events for queries + for (i, q) in queries.enumerated() { + events.append(Event(time: q, type: 1, size: nil, idx: i)) + } + + // Sort by time and type (end before query) + events.sort { (a, b) in + if a.time != b.time { + return a.time < b.time + } + return a.type < b.type + } + + // Min heap storing [size, index] + var sizes = Heap() + var ans = Array(repeating: -1, count: queries.count) + var inactive = Array(repeating: false, count: intervals.count) + + for event in events { + if event.type == 0 { // Interval start + let intervalSize = event.size! + let idx = event.idx! + sizes.insert(Item(size: intervalSize, idx: idx)) + } else if event.type == 2 { // Interval end + let idx = event.idx! + inactive[idx] = true + } else { // Query + let queryIdx = event.idx! + while !sizes.isEmpty, inactive[sizes.min!.idx] { + sizes.removeMin() + } + if !sizes.isEmpty { + ans[queryIdx] = sizes.min!.size + } + } + } + + return ans + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -779,6 +878,44 @@ class Solution { } ``` +```swift +struct HeapItem: Comparable { + let size: Int + let end: Int + static func < (lhs: HeapItem, rhs: HeapItem) -> Bool { + if lhs.size == rhs.size { + return lhs.end < rhs.end + } + return lhs.size < rhs.size + } +} + +class Solution { + func minInterval(_ intervals: [[Int]], _ queries: [Int]) -> [Int] { + let sortedIntervals = intervals.sorted { $0[0] < $1[0] } + var minHeap = Heap() + var res = [Int: Int]() + var i = 0 + + for q in queries.sorted() { + while i < sortedIntervals.count && sortedIntervals[i][0] <= q { + let l = sortedIntervals[i][0] + let r = sortedIntervals[i][1] + minHeap.insert(HeapItem(size: r - l + 1, end: r)) + i += 1 + } + + while !minHeap.isEmpty, minHeap.min!.end < q { + minHeap.removeMin() + } + res[q] = minHeap.isEmpty ? -1 : minHeap.min!.size + } + + return queries.map { res[$0] ?? -1 } + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -871,7 +1008,7 @@ class Solution: ``` ```java -public class SegmentTree { +class SegmentTree { int n; int[] tree; int[] lazy; @@ -1471,11 +1608,120 @@ class Solution { } ``` +```swift +class SegmentTree { + let n: Int + var tree: [Int] + var lazy: [Int] + let INF = Int.max + + init(_ N: Int) { + self.n = N + self.tree = [Int](repeating: INF, count: 4 * N) + self.lazy = [Int](repeating: INF, count: 4 * N) + } + + // Propagate lazy value at tree index over range [lo, hi] + func propagate(_ treeidx: Int, _ lo: Int, _ hi: Int) { + if lazy[treeidx] != INF { + tree[treeidx] = min(tree[treeidx], lazy[treeidx]) + if lo != hi { + lazy[2 * treeidx + 1] = min(lazy[2 * treeidx + 1], lazy[treeidx]) + lazy[2 * treeidx + 2] = min(lazy[2 * treeidx + 2], lazy[treeidx]) + } + lazy[treeidx] = INF + } + } + + // Update the segment tree over range [left, right] with value val + func update(_ treeidx: Int, _ lo: Int, _ hi: Int, _ left: Int, _ right: Int, _ val: Int) { + propagate(treeidx, lo, hi) + if lo > right || hi < left { + return + } + if lo >= left && hi <= right { + lazy[treeidx] = min(lazy[treeidx], val) + propagate(treeidx, lo, hi) + return + } + let mid = (lo + hi) / 2 + update(2 * treeidx + 1, lo, mid, left, right, val) + update(2 * treeidx + 2, mid + 1, hi, left, right, val) + tree[treeidx] = min(tree[2 * treeidx + 1], tree[2 * treeidx + 2]) + } + + // Query the value at index idx in the original array + func query(_ treeidx: Int, _ lo: Int, _ hi: Int, _ idx: Int) -> Int { + propagate(treeidx, lo, hi) + if lo == hi { + return tree[treeidx] + } + let mid = (lo + hi) / 2 + if idx <= mid { + return query(2 * treeidx + 1, lo, mid, idx) + } else { + return query(2 * treeidx + 2, mid + 1, hi, idx) + } + } + + func update_range(_ left: Int, _ right: Int, _ val: Int) { + update(0, 0, n - 1, left, right, val) + } + + func query_point(_ idx: Int) -> Int { + return query(0, 0, n - 1, idx) + } +} + +class Solution { + func minInterval(_ intervals: [[Int]], _ queries: [Int]) -> [Int] { + var points = [Int]() + // Create events for intervals + for interval in intervals { + points.append(interval[0]) + points.append(interval[1]) + } + // Create events for queries + for q in queries { + points.append(q) + } + + // Compress the coordinates + let sortedPoints = Array(Set(points)).sorted() + var compress = [Int: Int]() + for (i, point) in sortedPoints.enumerated() { + compress[point] = i + } + + // Lazy Segment Tree + let segTree = SegmentTree(sortedPoints.count) + + for interval in intervals { + let start = compress[interval[0]]! + let end = compress[interval[1]]! + let length = interval[1] - interval[0] + 1 + segTree.update_range(start, end, length) + } + + var ans = [Int]() + for q in queries { + let idx = compress[q]! + // Query for minSize + let res = segTree.query_point(idx) + ans.append(res == segTree.INF ? -1 : res) + } + return ans + } +} +``` + ::tabs-end ### Time & Space Complexity * Time complexity: $O((n + m)\log k)$ -* Space complexity: $O(k)$ +* Space complexity: + * $O(k)$ extra space. + * $O(m)$ space for the output array. > Where $m$ is the length of the array $queries$, $n$ is the length of the array $intervals$ and $k$ is the number of unique points. \ No newline at end of file diff --git a/articles/minimum-stack.md b/articles/minimum-stack.md index 28527e995..29041a02c 100644 --- a/articles/minimum-stack.md +++ b/articles/minimum-stack.md @@ -278,6 +278,42 @@ class MinStack() { } ``` +```swift +class MinStack { + private var stack: [Int] = [] + + init() {} + + func push(_ val: Int) { + stack.append(val) + } + + func pop() { + stack.popLast() + } + + func top() -> Int { + return stack.last! + } + + func getMin() -> Int { + var tmp = [Int]() + var mini = stack.last! + + while !stack.isEmpty { + mini = min(mini, stack.last!) + tmp.append(stack.removeLast()) + } + + while !tmp.isEmpty { + stack.append(tmp.removeLast()) + } + + return mini + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -524,6 +560,34 @@ class MinStack() { } ``` +```swift +class MinStack { + private var stack: [Int] = [] + private var minStack: [Int] = [] + + init() {} + + func push(_ val: Int) { + stack.append(val) + let minVal = min(val, minStack.last ?? val) + minStack.append(minVal) + } + + func pop() { + stack.popLast() + minStack.popLast() + } + + func top() -> Int { + return stack.last! + } + + func getMin() -> Int { + return minStack.last! + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -543,14 +607,14 @@ class MinStack: self.min = float('inf') self.stack = [] - def push(self, x: int) -> None: + def push(self, val: int) -> None: if not self.stack: self.stack.append(0) - self.min = x + self.min = val else: - self.stack.append(x - self.min) - if x < self.min: - self.min = x + self.stack.append(val - self.min) + if val < self.min: + self.min = val def pop(self) -> None: if not self.stack: @@ -581,13 +645,13 @@ public class MinStack { stack = new Stack<>(); } - public void push(int x) { + public void push(int val) { if (stack.isEmpty()) { stack.push(0L); - min = x; + min = val; } else { - stack.push(x - min); - if (x < min) min = x; + stack.push(val - min); + if (val < min) min = val; } } @@ -621,9 +685,7 @@ private: std::stack stack; public: - MinStack() { - - } + MinStack() {} void push(int val) { if (stack.empty()) { @@ -755,14 +817,14 @@ func Constructor() MinStack { } } -func (this *MinStack) Push(x int) { +func (this *MinStack) Push(val int) { if len(this.stack) == 0 { this.stack = append(this.stack, 0) - this.min = x + this.min = val } else { - this.stack = append(this.stack, x - this.min) - if x < this.min { - this.min = x + this.stack = append(this.stack, val - this.min) + if val < this.min { + this.min = val } } } @@ -796,8 +858,8 @@ class MinStack() { private var min: Long = Long.MAX_VALUE private val stack = ArrayDeque() - fun push(x: Int) { - val valAsLong = x.toLong() + fun push(`val`: Int) { + val valAsLong = `val`.toLong() if (stack.isEmpty()) { stack.addLast(0L) min = valAsLong @@ -828,6 +890,48 @@ class MinStack() { } ``` +```swift +class MinStack { + private var minVal: Int = Int.max + private var stack: [Int] = [] + + init() {} + + func push(_ val: Int) { + if stack.isEmpty { + stack.append(0) + minVal = val + } else { + stack.append(val - minVal) + if val < minVal { + minVal = val + } + } + } + + func pop() { + if stack.isEmpty { + return + } + + let pop = stack.removeLast() + + if pop < 0 { + minVal -= pop + } + } + + func top() -> Int { + let top = stack.last! + return top > 0 ? top + minVal : minVal + } + + func getMin() -> Int { + return minVal + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/minimum-window-with-characters.md b/articles/minimum-window-with-characters.md index 9e0b36005..887cb769c 100644 --- a/articles/minimum-window-with-characters.md +++ b/articles/minimum-window-with-characters.md @@ -279,6 +279,48 @@ class Solution { } ``` +```swift +class Solution { + func minWindow(_ s: String, _ t: String) -> String { + if t.isEmpty { + return "" + } + + var countT = [Character: Int]() + for c in t { + countT[c, default: 0] += 1 + } + + var res = [-1, -1] + var resLen = Int.max + let chars = Array(s) + + for i in 0.. countS[c, default: 0] { + flag = false + break + } + } + + if flag && (j - i + 1) < resLen { + resLen = j - i + 1 + res = [i, j] + } + } + } + + let (l, r) = (res[0], res[1]) + return resLen != Int.max ? String(chars[l...r]) : "" + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -608,6 +650,53 @@ class Solution { } ``` +```swift +class Solution { + func minWindow(_ s: String, _ t: String) -> String { + if t.isEmpty { + return "" + } + + var countT = [Character: Int]() + var window = [Character: Int]() + for c in t { + countT[c, default: 0] += 1 + } + + var have = 0, need = countT.count + var res = [-1, -1], resLen = Int.max + let chars = Array(s) + var l = 0 + + for r in 0.. Int { + var nums = nums.sorted() + let n = nums.count + for i in 0.. Int { + let numSet = Set(nums) + let n = nums.count + for i in 0...n { + if !numSet.contains(i) { + return i + } + } + return -1 + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -331,6 +361,21 @@ class Solution { } ``` +```swift +class Solution { + func missingNumber(_ nums: [Int]) -> Int { + let n = nums.count + var xorr = n + + for i in 0.. Int { + var res = nums.count + + for i in 0.. String { + if num1 == "0" || num2 == "0" { + return "0" + } + if num1.count < num2.count { + return multiply(num2, num1) + } + + var res = "" + var zero = 0 + let num2Arr = Array(num2) + + for i in stride(from: num2Arr.count - 1, through: 0, by: -1) { + let cur = mul(num1, num2Arr[i], zero) + res = add(res, cur) + zero += 1 + } + + return res + } + + func mul(_ s: String, _ d: Character, _ zero: Int) -> String { + var i = s.count - 1 + var carry = 0 + let dInt = Int(String(d))! + let sArr = Array(s) + var cur = [String]() + + while i >= 0 || carry > 0 { + let n = i >= 0 ? Int(String(sArr[i]))! : 0 + let prod = n * dInt + carry + cur.append(String(prod % 10)) + carry = prod / 10 + i -= 1 + } + + let prodStr = cur.reversed().joined() + let zeros = String(repeating: "0", count: zero) + return prodStr + zeros + } + + func add(_ num1: String, _ num2: String) -> String { + let s1 = Array(num1) + let s2 = Array(num2) + var i = s1.count - 1 + var j = s2.count - 1 + var carry = 0 + var res = [String]() + + while i >= 0 || j >= 0 || carry > 0 { + let n1 = i >= 0 ? Int(String(s1[i]))! : 0 + let n2 = j >= 0 ? Int(String(s2[j]))! : 0 + let total = n1 + n2 + carry + res.append(String(total % 10)) + carry = total / 10 + i -= 1 + j -= 1 + } + + return res.reversed().joined() + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -667,6 +733,37 @@ class Solution { } ``` +```swift +class Solution { + func multiply(_ num1: String, _ num2: String) -> String { + if num1 == "0" || num2 == "0" { + return "0" + } + + let n1 = Array(num1.reversed()).map { Int(String($0))! } + let n2 = Array(num2.reversed()).map { Int(String($0))! } + var res = [Int](repeating: 0, count: num1.count + num2.count) + + for i1 in 0.. [[String]] { + var res = [[String]]() + var board = Array(repeating: Array(repeating: ".", count: n), count: n) + + func backtrack(_ r: Int) { + if r == n { + let copy = board.map { $0.joined() } + res.append(copy) + return + } + for c in 0.. Bool { + var row = r - 1 + while row >= 0 { + if board[row][c] == "Q" { return false } + row -= 1 + } + + var row1 = r - 1, col1 = c - 1 + while row1 >= 0, col1 >= 0 { + if board[row1][col1] == "Q" { return false } + row1 -= 1 + col1 -= 1 + } + + var row2 = r - 1, col2 = c + 1 + while row2 >= 0, col2 < board.count { + if board[row2][col2] == "Q" { return false } + row2 -= 1 + col2 += 1 + } + + return true + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -666,6 +717,47 @@ class Solution { } ``` +```swift +class Solution { + func solveNQueens(_ n: Int) -> [[String]] { + var col = Set() + var posDiag = Set() + var negDiag = Set() + var res = [[String]]() + var board = Array(repeating: Array(repeating: ".", count: n), count: n) + + func backtrack(_ r: Int) { + if r == n { + let copy = board.map { $0.joined() } + res.append(copy) + return + } + + for c in 0.. [[String]] { + var col = Array(repeating: false, count: n) + var posDiag = Array(repeating: false, count: 2 * n) + var negDiag = Array(repeating: false, count: 2 * n) + var res = [[String]]() + var board = Array(repeating: Array(repeating: ".", count: n), count: n) + + func backtrack(_ r: Int) { + if r == n { + let copy = board.map { $0.joined() } + res.append(copy) + return + } + + for c in 0.. [[String]] { + var col = 0 + var posDiag = 0 + var negDiag = 0 + var res = [[String]]() + var board = Array(repeating: Array(repeating: ".", count: n), count: n) + + func backtrack(_ r: Int) { + if r == n { + let copy = board.map { $0.joined() } + res.append(copy) + return + } + + for c in 0.. Int { + var adj = [Int: [(Int, Int)]]() + for time in times { + let u = time[0], v = time[1], w = time[2] + adj[u, default: []].append((v, w)) + } + + var dist = [Int: Int]() + for node in 1...n { + dist[node] = Int.max + } + + func dfs(_ node: Int, _ time: Int) { + if time >= dist[node]! { + return + } + + dist[node] = time + if let neighbors = adj[node] { + for (nei, w) in neighbors { + dfs(nei, time + w) + } + } + } + + dfs(k, 0) + let res = dist.values.max()! + return res == Int.max ? -1 : res + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -450,6 +484,34 @@ class Solution { } ``` +```swift +class Solution { + func networkDelayTime(_ times: [[Int]], _ n: Int, _ k: Int) -> Int { + let inf = Int.max / 2 + var dist = Array(repeating: Array(repeating: inf, count: n), count: n) + + for time in times { + let u = time[0] - 1, v = time[1] - 1, w = time[2] + dist[u][v] = w + } + for i in 0..= inf ? -1 : res + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -621,6 +683,32 @@ class Solution { } ``` +```swift +class Solution { + func networkDelayTime(_ times: [[Int]], _ n: Int, _ k: Int) -> Int { + var dist = Array(repeating: Int.max, count: n) + dist[k - 1] = 0 + + for _ in 0.. Int { + var adj = [Int: [(Int, Int)]]() + for time in times { + let u = time[0], v = time[1], w = time[2] + adj[u, default: []].append((v, w)) + } + + var dist = [Int: Int]() + for node in 1...n { + dist[node] = Int.max + } + dist[k] = 0 + + var queue = Deque<(Int, Int)>() + queue.append((k, 0)) + + while !queue.isEmpty { + let (node, time) = queue.popFirst()! + if dist[node]! < time { + continue + } + if let neighbors = adj[node] { + for (nei, w) in neighbors { + if time + w < dist[nei]! { + dist[nei] = time + w + queue.append((nei, time + w)) + } + } + } + } + + let res = dist.values.max()! + return res == Int.max ? -1 : res + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -1175,6 +1302,53 @@ class Solution { } ``` +```swift +struct Item: Comparable { + let weight: Int + let node: Int + + static func < (lhs: Item, rhs: Item) -> Bool { + return lhs.weight < rhs.weight + } +} + +class Solution { + func networkDelayTime(_ times: [[Int]], _ n: Int, _ k: Int) -> Int { + var edges = [Int: [(Int, Int)]]() + for time in times { + let u = time[0], v = time[1], w = time[2] + edges[u, default: []].append((v, w)) + } + + var minHeap = Heap() + minHeap.insert(Item(weight: 0, node: k)) + var visit = Set() + var t = 0 + + while !minHeap.isEmpty { + let item = minHeap.removeMin() + let w1 = item.weight + let n1 = item.node + + if visit.contains(n1) { + continue + } + visit.insert(n1) + t = w1 + + if let neighbors = edges[n1] { + for (n2, w2) in neighbors { + if !visit.contains(n2) { + minHeap.insert(Item(weight: w1 + w2, node: n2)) + } + } + } + } + return visit.count == n ? t : -1 + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/non-cyclical-number.md b/articles/non-cyclical-number.md index 5290a69a7..6b3053011 100644 --- a/articles/non-cyclical-number.md +++ b/articles/non-cyclical-number.md @@ -206,6 +206,36 @@ class Solution { } ``` +```swift +class Solution { + func isHappy(_ n: Int) -> Bool { + var visit = Set() + var num = n + + while !visit.contains(num) { + visit.insert(num) + num = sumOfSquares(num) + if num == 1 { + return true + } + } + return false + } + + private func sumOfSquares(_ n: Int) -> Int { + var num = n + var output = 0 + + while num > 0 { + let digit = num % 10 + output += digit * digit + num /= 10 + } + return output + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -404,6 +434,34 @@ class Solution { } ``` +```swift +class Solution { + func isHappy(_ n: Int) -> Bool { + var slow = n + var fast = sumOfSquares(n) + + while slow != fast { + fast = sumOfSquares(fast) + fast = sumOfSquares(fast) + slow = sumOfSquares(slow) + } + return fast == 1 + } + + private func sumOfSquares(_ n: Int) -> Int { + var num = n + var output = 0 + + while num > 0 { + let digit = num % 10 + output += digit * digit + num /= 10 + } + return output + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -636,6 +694,40 @@ class Solution { } ``` +```swift +class Solution { + func isHappy(_ n: Int) -> Bool { + var slow = n + var fast = sumOfSquares(n) + var power = 1 + var lam = 1 + + while slow != fast { + if power == lam { + slow = fast + power *= 2 + lam = 0 + } + fast = sumOfSquares(fast) + lam += 1 + } + return fast == 1 + } + + private func sumOfSquares(_ n: Int) -> Int { + var num = n + var output = 0 + + while num > 0 { + let digit = num % 10 + output += digit * digit + num /= 10 + } + return output + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/non-overlapping-intervals.md b/articles/non-overlapping-intervals.md index 7970155cb..60f0b89a1 100644 --- a/articles/non-overlapping-intervals.md +++ b/articles/non-overlapping-intervals.md @@ -147,6 +147,28 @@ class Solution { } ``` +```swift +class Solution { + func eraseOverlapIntervals(_ intervals: [[Int]]) -> Int { + var intervals = intervals + intervals.sort { $0[0] < $1[0] } + + func dfs(_ i: Int, _ prev: Int) -> Int { + if i == intervals.count { + return 0 + } + var res = dfs(i + 1, prev) + if prev == -1 || intervals[prev][1] <= intervals[i][0] { + res = max(res, 1 + dfs(i + 1, i)) + } + return res + } + + return intervals.count - dfs(0, -1) + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -365,6 +387,34 @@ class Solution { } ``` +```swift +class Solution { + func eraseOverlapIntervals(_ intervals: [[Int]]) -> Int { + var intervals = intervals + intervals.sort { $0[1] < $1[1] } + let n = intervals.count + var memo = [Int: Int]() + + func dfs(_ i: Int) -> Int { + if let result = memo[i] { + return result + } + + var res = 1 + for j in i + 1.. Int { + var intervals = intervals + intervals.sort { $0[1] < $1[1] } + let n = intervals.count + var dp = [Int](repeating: 0, count: n) + + for i in 0.. Int { + var intervals = intervals + intervals.sort { $0[1] < $1[1] } + let n = intervals.count + var dp = [Int](repeating: 0, count: n) + dp[0] = 1 + + func bs(_ r: Int, _ target: Int) -> Int { + var l = 0 + var r = r + while l < r { + let m = (l + r) >> 1 + if intervals[m][1] <= target { + l = m + 1 + } else { + r = m + } + } + return l + } + + for i in 1.. Int { + var intervals = intervals + intervals.sort { $0[0] < $1[0] } + + var res = 0 + var prevEnd = intervals[0][1] + + for i in 1..= prevEnd { + prevEnd = end + } else { + res += 1 + prevEnd = min(end, prevEnd) + } + } + return res + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -1142,6 +1275,28 @@ class Solution { } ``` +```swift +class Solution { + func eraseOverlapIntervals(_ intervals: [[Int]]) -> Int { + var intervals = intervals + intervals.sort { $0[1] < $1[1] } + + var prevEnd = intervals[0][1] + var res = 0 + + for i in 1.. intervals[i][0] { + res += 1 + } else { + prevEnd = intervals[i][1] + } + } + + return res + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/number-of-one-bits.md b/articles/number-of-one-bits.md index 9468bf89b..4ea545b0d 100644 --- a/articles/number-of-one-bits.md +++ b/articles/number-of-one-bits.md @@ -99,6 +99,20 @@ class Solution { } ``` +```swift +class Solution { + func hammingWeight(_ n: Int) -> Int { + var res = 0 + for i in 0..<32 { + if (1 << i) & n != 0 { + res += 1 + } + } + return res + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -208,6 +222,20 @@ class Solution { } ``` +```swift +class Solution { + func hammingWeight(_ n: Int) -> Int { + var n = n + var res = 0 + while n != 0 { + res += (n & 1) != 0 ? 1 : 0 + n >>= 1 + } + return res + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -313,6 +341,20 @@ class Solution { } ``` +```swift +class Solution { + func hammingWeight(_ n: Int) -> Int { + var n = n + var res = 0 + while n != 0 { + n &= (n - 1) + res += 1 + } + return res + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -383,6 +425,14 @@ class Solution { } ``` +```swift +class Solution { + func hammingWeight(_ n: Int) -> Int { + return n.nonzeroBitCount + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/pacific-atlantic-water-flow.md b/articles/pacific-atlantic-water-flow.md index 1b718571a..1245a570d 100644 --- a/articles/pacific-atlantic-water-flow.md +++ b/articles/pacific-atlantic-water-flow.md @@ -351,6 +351,57 @@ class Solution { } ``` +```swift +class Solution { + func pacificAtlantic(_ heights: [[Int]]) -> [[Int]] { + var heights = heights + let ROWS = heights.count + let COLS = heights[0].count + let directions = [(1, 0), (-1, 0), (0, 1), (0, -1)] + + var pacific = false + var atlantic = false + + func dfs(_ r: Int, _ c: Int, _ prevVal: Int) { + if r < 0 || c < 0 { + pacific = true + return + } + if r >= ROWS || c >= COLS { + atlantic = true + return + } + if heights[r][c] > prevVal { + return + } + + let tmp = heights[r][c] + heights[r][c] = Int.max + for dir in directions { + dfs(r + dir.0, c + dir.1, tmp) + if pacific && atlantic { + break + } + } + heights[r][c] = tmp + } + + var res = [[Int]]() + for r in 0.. [[Int]] { + let ROWS = heights.count + let COLS = heights[0].count + var pac = Set<[Int]>() + var atl = Set<[Int]>() + + func dfs(_ r: Int, _ c: Int, _ visit: inout Set<[Int]>, _ prevHeight: Int) { + if (visit.contains([r, c]) || r < 0 || c < 0 || r == ROWS || + c == COLS || heights[r][c] < prevHeight) { + return + } + visit.insert([r, c]) + dfs(r + 1, c, &visit, heights[r][c]) + dfs(r - 1, c, &visit, heights[r][c]) + dfs(r, c + 1, &visit, heights[r][c]) + dfs(r, c - 1, &visit, heights[r][c]) + } + + for c in 0.. [[Int]] { + let ROWS = heights.count + let COLS = heights[0].count + let directions = [(1, 0), (-1, 0), (0, 1), (0, -1)] + + var pac = Array(repeating: Array(repeating: false, count: COLS), count: ROWS) + var atl = Array(repeating: Array(repeating: false, count: COLS), count: ROWS) + + func bfs(_ source: [(Int, Int)], _ ocean: inout [[Bool]]) { + var queue = Deque(source) + while !queue.isEmpty { + let (r, c) = queue.popFirst()! + ocean[r][c] = true + for dir in directions { + let nr = r + dir.0 + let nc = c + dir.1 + if nr >= 0, nr < ROWS, nc >= 0, nc < COLS, + !ocean[nr][nc], heights[nr][nc] >= heights[r][c] { + queue.append((nr, nc)) + } + } + } + } + + var pacific: [(Int, Int)] = [] + var atlantic: [(Int, Int)] = [] + + for c in 0.. [[String]] { + var res = [[String]]() + var part = [String]() + let sArray = Array(s) + + func dfs(_ j: Int, _ i: Int) { + if i >= sArray.count { + if i == j { + res.append(part) + } + return + } + + if isPali(sArray, j, i) { + part.append(String(sArray[j...i])) + dfs(i + 1, i + 1) + part.removeLast() + } + + dfs(j, i + 1) + } + + func isPali(_ s: [Character], _ l: Int, _ r: Int) -> Bool { + var l = l, r = r + while l < r { + if s[l] != s[r] { + return false + } + l += 1 + r -= 1 + } + return true + } + + dfs(0, 0) + return res + } +} +``` + ::tabs-end ### Time & Space Complexity * Time complexity: $O(n * 2 ^ n)$ -* Space complexity: $O(n)$ +* Space complexity: + * $O(n)$ extra space. + * $O(n * 2 ^ n)$ space for the output list. --- -## 2. Backtracking +## 2. Backtracking - II ::tabs-start @@ -562,12 +606,53 @@ class Solution { } ``` +```swift +class Solution { + func partition(_ s: String) -> [[String]] { + var res = [[String]]() + var part = [String]() + let sArray = Array(s) + + func dfs(_ i: Int) { + if i >= sArray.count { + res.append(part) + return + } + for j in i.. Bool { + var l = l, r = r + while l < r { + if s[l] != s[r] { + return false + } + l += 1 + r -= 1 + } + return true + } + + dfs(0) + return res + } +} +``` + ::tabs-end ### Time & Space Complexity * Time complexity: $O(n * 2 ^ n)$ -* Space complexity: $O(n)$ +* Space complexity: + * $O(n)$ extra space. + * $O(n * 2 ^ n)$ space for the output list. --- @@ -823,12 +908,51 @@ class Solution { } ``` +```swift +class Solution { + func partition(_ s: String) -> [[String]] { + let n = s.count + let sArray = Array(s) + var dp = Array(repeating: Array(repeating: false, count: n), count: n) + + for l in 1...n { + for i in 0...(n - l) { + dp[i][i + l - 1] = (sArray[i] == sArray[i + l - 1] && + (i + 1 > (i + l - 2) || dp[i + 1][i + l - 2])) + } + } + + var res = [[String]]() + var part = [String]() + + func dfs(_ i: Int) { + if i >= sArray.count { + res.append(part) + return + } + for j in i.. [[String]] { + let n = s.count + let sArray = Array(s) + var dp = Array(repeating: Array(repeating: false, count: n), count: n) + + for l in 1...n { + for i in 0...(n - l) { + dp[i][i + l - 1] = (sArray[i] == sArray[i + l - 1] && + (i + 1 > (i + l - 2) || dp[i + 1][i + l - 2])) + } + } + + func dfs(_ i: Int) -> [[String]] { + if i >= n { + return [[]] + } + + var ret = [[String]]() + for j in i.. Int { + let chars = Array(s) + var res = 0 + + for i in 0..= r { + res += 1 + } + } + } + + return res + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -313,6 +337,28 @@ class Solution { } ``` +```swift +class Solution { + func countSubstrings(_ s: String) -> Int { + let n = s.count + var res = 0 + var dp = Array(repeating: Array(repeating: false, count: n), count: n) + let chars = Array(s) + + for i in stride(from: n - 1, through: 0, by: -1) { + for j in i.. Int { + let chars = Array(s) + var res = 0 + + for i in 0..= 0 && r < chars.count && chars[l] == chars[r] { + res += 1 + l -= 1 + r += 1 + } + + // Even length palindromes + l = i + r = i + 1 + while l >= 0 && r < chars.count && chars[l] == chars[r] { + res += 1 + l -= 1 + r += 1 + } + } + + return res + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -722,6 +798,33 @@ class Solution { } ``` +```swift +class Solution { + func countSubstrings(_ s: String) -> Int { + var res = 0 + let chars = Array(s) + + for i in 0.. Int { + var res = 0 + var left = l, right = r + + while left >= 0 && right < s.count && s[left] == s[right] { + res += 1 + left -= 1 + right += 1 + } + return res + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -982,6 +1085,42 @@ class Solution { } ``` +```swift +class Solution { + func countSubstrings(_ s: String) -> Int { + func manacher(_ s: String) -> [Int] { + let t = "#" + s.map { "\($0)#" }.joined() + let chars = Array(t) + let n = chars.count + var p = Array(repeating: 0, count: n) + var l = 0, r = 0 + + for i in 0..= 0, + chars[i + p[i] + 1] == chars[i - p[i] - 1] { + p[i] += 1 + } + if i + p[i] > r { + l = i - p[i] + r = i + p[i] + } + } + return p + } + + let p = manacher(s) + var res = 0 + for i in p { + res += (i + 1) / 2 + } + return res + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/partition-equal-subset-sum.md b/articles/partition-equal-subset-sum.md index d51654692..6d6fd0caa 100644 --- a/articles/partition-equal-subset-sum.md +++ b/articles/partition-equal-subset-sum.md @@ -194,6 +194,33 @@ class Solution { } ``` +```swift +class Solution { + func canPartition(_ nums: [Int]) -> Bool { + let totalSum = nums.reduce(0, +) + if totalSum % 2 != 0 { + return false + } + + let target = totalSum / 2 + let n = nums.count + + func dfs(_ i: Int, _ target: Int) -> Bool { + if i >= n { + return target == 0 + } + if target < 0 { + return false + } + + return dfs(i + 1, target) || dfs(i + 1, target - nums[i]) + } + + return dfs(0, target) + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -454,6 +481,39 @@ class Solution { } ``` +```swift +class Solution { + func canPartition(_ nums: [Int]) -> Bool { + let total = nums.reduce(0, +) + if total % 2 != 0 { + return false + } + + let target = total / 2 + let n = nums.count + var memo = Array(repeating: Array(repeating: -1, count: target + 1), count: n + 1) + + func dfs(_ i: Int, _ target: Int) -> Bool { + if target == 0 { + return true + } + if i >= n || target < 0 { + return false + } + if memo[i][target] != -1 { + return memo[i][target] == 1 + } + + let result = dfs(i + 1, target) || dfs(i + 1, target - nums[i]) + memo[i][target] = result ? 1 : 0 + return result + } + + return dfs(0, target) + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -702,6 +762,37 @@ class Solution { } ``` +```swift +class Solution { + func canPartition(_ nums: [Int]) -> Bool { + let total = nums.reduce(0, +) + if total % 2 != 0 { + return false + } + + let target = total / 2 + let n = nums.count + var dp = Array(repeating: Array(repeating: false, count: target + 1), count: n + 1) + + for i in 0...n { + dp[i][0] = true + } + + for i in 1...n { + for j in 1...target { + if nums[i - 1] <= j { + dp[i][j] = dp[i - 1][j] || dp[i - 1][j - nums[i - 1]] + } else { + dp[i][j] = dp[i - 1][j] + } + } + } + + return dp[n][target] + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -940,6 +1031,34 @@ class Solution { } ``` +```swift +class Solution { + func canPartition(_ nums: [Int]) -> Bool { + if nums.reduce(0, +) % 2 != 0 { + return false + } + + let target = nums.reduce(0, +) / 2 + var dp = Array(repeating: false, count: target + 1) + var nextDp = Array(repeating: false, count: target + 1) + + dp[0] = true + for num in nums { + for j in stride(from: target, through: 1, by: -1) { + if j >= num { + nextDp[j] = dp[j] || dp[j - num] + } else { + nextDp[j] = dp[j] + } + } + swap(&dp, &nextDp) + } + + return dp[target] + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -1147,6 +1266,32 @@ class Solution { } ``` +```swift +class Solution { + func canPartition(_ nums: [Int]) -> Bool { + if nums.reduce(0, +) % 2 != 0 { + return false + } + + var dp: Set = [0] + let target = nums.reduce(0, +) / 2 + + for i in stride(from: nums.count - 1, through: 0, by: -1) { + var nextDP: Set = [] + for t in dp { + if t + nums[i] == target { + return true + } + nextDP.insert(t + nums[i]) + nextDP.insert(t) + } + dp = nextDP + } + return false + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -1342,6 +1487,28 @@ class Solution { } ``` +```swift +class Solution { + func canPartition(_ nums: [Int]) -> Bool { + if nums.reduce(0, +) % 2 != 0 { + return false + } + + let target = nums.reduce(0, +) / 2 + var dp = Array(repeating: false, count: target + 1) + + dp[0] = true + for num in nums { + for j in stride(from: target, through: num, by: -1) { + dp[j] = dp[j] || dp[j - num] + } + } + + return dp[target] + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -1366,10 +1533,10 @@ class Solution: target = total // 2 dp = 1 << 0 - + for num in nums: dp |= dp << num - + return (dp & (1 << target)) != 0 ``` diff --git a/articles/partition-labels.md b/articles/partition-labels.md index 5eb089e85..1e4e2b3a1 100644 --- a/articles/partition-labels.md +++ b/articles/partition-labels.md @@ -169,6 +169,31 @@ class Solution { } ``` +```swift +class Solution { + func partitionLabels(_ s: String) -> [Int] { + var lastIndex = [Character: Int]() + for (i, c) in s.enumerated() { + lastIndex[c] = i + } + + var res = [Int]() + var size = 0 + var end = 0 + for (i, c) in s.enumerated() { + size += 1 + end = max(end, lastIndex[c]!) + + if i == end { + res.append(size) + size = 0 + } + } + return res + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/permutation-string.md b/articles/permutation-string.md index 07e79c7ef..482890ee4 100644 --- a/articles/permutation-string.md +++ b/articles/permutation-string.md @@ -149,6 +149,25 @@ class Solution { } ``` +```swift +class Solution { + func checkInclusion(_ s1: String, _ s2: String) -> Bool { + let s1Sorted = s1.sorted() + let chars = Array(s2) + + for i in 0.. Bool { + var count1 = [Character: Int]() + for c in s1 { + count1[c, default: 0] += 1 + } + + let need = count1.count + let chars = Array(s2) + + for i in 0.. Where $n$ is the length of the string1 and $m$ is the length of string2. @@ -736,6 +788,61 @@ class Solution { } ``` +```swift +class Solution { + func checkInclusion(_ s1: String, _ s2: String) -> Bool { + if s1.count > s2.count { + return false + } + + var s1Count = [Int](repeating: 0, count: 26) + var s2Count = [Int](repeating: 0, count: 26) + let aAscii = Int(Character("a").asciiValue!) + + let s1Array = Array(s1) + let s2Array = Array(s2) + + for i in 0.. [[Int]] { + if nums.isEmpty { + return [[]] + } + + let perms = permute(Array(nums.dropFirst())) + var res = [[Int]]() + + for p in perms { + for i in 0...p.count { + var pCopy = p + pCopy.insert(nums[0], at: i) + res.append(pCopy) + } + } + + return res + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -303,6 +326,28 @@ class Solution { } ``` +```swift +class Solution { + func permute(_ nums: [Int]) -> [[Int]] { + var perms: [[Int]] = [[]] + + for num in nums { + var newPerms = [[Int]]() + for p in perms { + for i in 0...p.count { + var pCopy = p + pCopy.insert(num, at: i) + newPerms.append(pCopy) + } + } + perms = newPerms + } + + return perms + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -501,6 +546,35 @@ class Solution { } ``` +```swift +class Solution { + func permute(_ nums: [Int]) -> [[Int]] { + var res = [[Int]]() + var pick = [Bool](repeating: false, count: nums.count) + + func backtrack(_ perm: inout [Int]) { + if perm.count == nums.count { + res.append(perm) + return + } + for i in 0.. [[Int]] { + var res = [[Int]]() + + func backtrack(_ perm: inout [Int], _ mask: Int) { + if perm.count == nums.count { + res.append(perm) + return + } + for i in 0.. [[Int]] { + var res = [[Int]]() + var nums = nums + + func backtrack(_ idx: Int) { + if idx == nums.count { + res.append(nums) + return + } + for i in idx.. [Int] { + if digits.isEmpty { + return [1] + } + + var digits = digits + if digits[digits.count - 1] < 9 { + digits[digits.count - 1] += 1 + return digits + } else { + return plusOne(Array(digits.dropLast())) + [0] + } + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -341,6 +359,35 @@ class Solution { } ``` +```swift +class Solution { + func plusOne(_ digits: [Int]) -> [Int] { + var digits = digits + var one = 1 + var i = 0 + digits.reverse() + + while one > 0 { + if i < digits.count { + if digits[i] == 9 { + digits[i] = 0 + } else { + digits[i] += 1 + one = 0 + } + } else { + digits.append(one) + one = 0 + } + i += 1 + } + + digits.reverse() + return digits + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -476,6 +523,25 @@ class Solution { } ``` +```swift +class Solution { + func plusOne(_ digits: [Int]) -> [Int] { + var digits = digits + let n = digits.count + + for i in stride(from: n - 1, through: 0, by: -1) { + if digits[i] < 9 { + digits[i] += 1 + return digits + } + digits[i] = 0 + } + + return [1] + digits + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/pow-x-n.md b/articles/pow-x-n.md index 341e3ae9d..50beeb0f4 100644 --- a/articles/pow-x-n.md +++ b/articles/pow-x-n.md @@ -139,6 +139,25 @@ class Solution { } ``` +```swift +class Solution { + func myPow(_ x: Double, _ n: Int) -> Double { + if x == 0 { + return 0 + } + if n == 0 { + return 1 + } + + var res: Double = 1 + for _ in 0..= 0 ? res : 1 / res + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -320,12 +339,33 @@ class Solution { } ``` +```swift +class Solution { + func myPow(_ x: Double, _ n: Int) -> Double { + func helper(_ x: Double, _ n: Int) -> Double { + if x == 0 { + return 0 + } + if n == 0 { + return 1 + } + + let res = helper(x * x, n / 2) + return n % 2 == 0 ? res : x * res + } + + let res = helper(x, abs(n)) + return n >= 0 ? res : 1 / res + } +} +``` + ::tabs-end ### Time & Space Complexity * Time complexity: $O(\log n)$ -* Space complexity: $O(\log n)$ +* Space complexity: $O(\log n)$ for recursion stack. --- @@ -501,6 +541,33 @@ class Solution { } ``` +```swift +class Solution { + func myPow(_ x: Double, _ n: Int) -> Double { + if x == 0 { + return 0 + } + if n == 0 { + return 1 + } + + var res: Double = 1 + var base = x + var power = abs(n) + + while power > 0 { + if power & 1 == 1 { + res *= base + } + base *= base + power >>= 1 + } + + return n >= 0 ? res : 1 / res + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/products-of-array-discluding-self.md b/articles/products-of-array-discluding-self.md index 1a74a5c68..1370fac97 100644 --- a/articles/products-of-array-discluding-self.md +++ b/articles/products-of-array-discluding-self.md @@ -142,12 +142,37 @@ class Solution { } ``` +```swift +class Solution { + func productExceptSelf(_ nums: [Int]) -> [Int] { + let n = nums.count + var res = [Int](repeating: 0, count: n) + + for i in 0.. [Int] { + var prod = 1 + var zeroCount = 0 + + for num in nums { + if num != 0 { + prod *= num + } else { + zeroCount += 1 + } + } + + if zeroCount > 1 { + return [Int](repeating: 0, count: nums.count) + } + + var res = [Int](repeating: 0, count: nums.count) + for (i, num) in nums.enumerated() { + if zeroCount > 0 { + res[i] = num == 0 ? prod : 0 + } else { + res[i] = prod / num + } + } + + return res + } +} +``` + ::tabs-end ### Time & Space Complexity * Time complexity: $O(n)$ -* Space complexity: $O(1)$ since the output array is excluded from space analysis. +* Space complexity: + * $O(1)$ extra space. + * $O(n)$ space for the output array. --- @@ -533,6 +592,34 @@ class Solution { } ``` +```swift +class Solution { + func productExceptSelf(_ nums: [Int]) -> [Int] { + let n = nums.count + var res = [Int](repeating: 0, count: n) + var pref = [Int](repeating: 0, count: n) + var suff = [Int](repeating: 0, count: n) + + pref[0] = 1 + suff[n - 1] = 1 + + for i in 1.. [Int] { + var res = [Int](repeating: 1, count: nums.count) + + var prefix = 1 + for i in 0.. [String] { + var adj = [String: [String]]() + for ticket in tickets { + adj[ticket[0], default: []].append(ticket[1]) + } + + for key in adj.keys { + adj[key]?.sort() + } + + var res = ["JFK"] + + func dfs(_ src: String) -> Bool { + if res.count == tickets.count + 1 { + return true + } + guard let destinations = adj[src] else { + return false + } + + var temp = destinations + for i in 0.. [String] { + var adj = [String: [String]]() + for ticket in tickets.sorted(by: { $0[1] > $1[1] }) { + adj[ticket[0], default: []].append(ticket[1]) + } + + var res = [String]() + + func dfs(_ src: String) { + while let destinations = adj[src], !destinations.isEmpty { + let dst = adj[src]!.removeLast() + dfs(dst) + } + res.append(src) + } + + dfs("JFK") + return res.reversed() + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -718,6 +782,31 @@ class Solution { } ``` +```swift +class Solution { + func findItinerary(_ tickets: [[String]]) -> [String] { + var adj = [String: [String]]() + for ticket in tickets.sorted(by: { $0[1] > $1[1] }) { + adj[ticket[0], default: []].append(ticket[1]) + } + + var stack = ["JFK"] + var res = [String]() + + while !stack.isEmpty { + let curr = stack.last! + if adj[curr] == nil || adj[curr]!.isEmpty { + res.append(stack.removeLast()) + } else { + stack.append(adj[curr]!.removeLast()) + } + } + + return res.reversed() + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/redundant-connection.md b/articles/redundant-connection.md index 81a67ff84..154493da7 100644 --- a/articles/redundant-connection.md +++ b/articles/redundant-connection.md @@ -247,6 +247,45 @@ class Solution { } ``` +```swift +class Solution { + func findRedundantConnection(_ edges: [[Int]]) -> [Int] { + let n = edges.count + var adj = Array(repeating: [Int](), count: n + 1) + + func dfs(_ node: Int, _ par: Int, _ visit: inout [Bool]) -> Bool { + if visit[node] { + return true + } + + visit[node] = true + for nei in adj[node] { + if nei == par { + continue + } + if dfs(nei, node, &visit) { + return true + } + } + return false + } + + for edge in edges { + let u = edge[0] + let v = edge[1] + adj[u].append(v) + adj[v].append(u) + var visit = Array(repeating: false, count: n + 1) + + if dfs(u, -1, &visit) { + return [u, v] + } + } + return [] + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -606,6 +645,62 @@ class Solution { } ``` +```swift +class Solution { + func findRedundantConnection(_ edges: [[Int]]) -> [Int] { + let n = edges.count + var adj = Array(repeating: [Int](), count: n + 1) + + for edge in edges { + let u = edge[0] + let v = edge[1] + adj[u].append(v) + adj[v].append(u) + } + + var visit = Array(repeating: false, count: n + 1) + var cycle = Set() + var cycleStart = -1 + + func dfs(_ node: Int, _ par: Int) -> Bool { + if visit[node] { + cycleStart = node + return true + } + + visit[node] = true + for nei in adj[node] { + if nei == par { + continue + } + if dfs(nei, node) { + if cycleStart != -1 { + cycle.insert(node) + } + if node == cycleStart { + cycleStart = -1 + } + return true + } + } + return false + } + + dfs(1, -1) + + for edge in edges.reversed() { + let u = edge[0] + let v = edge[1] + if cycle.contains(u) && cycle.contains(v) { + return [u, v] + } + } + + return [] + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -895,6 +990,52 @@ class Solution { } ``` +```swift +class Solution { + func findRedundantConnection(_ edges: [[Int]]) -> [Int] { + let n = edges.count + var indegree = Array(repeating: 0, count: n + 1) + var adj = Array(repeating: [Int](), count: n + 1) + + for edge in edges { + let u = edge[0] + let v = edge[1] + adj[u].append(v) + adj[v].append(u) + indegree[u] += 1 + indegree[v] += 1 + } + + var queue = Deque() + for i in 1...n { + if indegree[i] == 1 { + queue.append(i) + } + } + + while !queue.isEmpty { + let node = queue.popFirst()! + indegree[node] -= 1 + for nei in adj[node] { + indegree[nei] -= 1 + if indegree[nei] == 1 { + queue.append(nei) + } + } + } + + for edge in edges.reversed() { + let u = edge[0] + let v = edge[1] + if indegree[u] == 2 && indegree[v] > 0 { + return [u, v] + } + } + return [] + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -1212,6 +1353,50 @@ class Solution { } ``` +```swift +class Solution { + func findRedundantConnection(_ edges: [[Int]]) -> [Int] { + var par = Array(0...edges.count) + var rank = Array(repeating: 1, count: edges.count + 1) + + func find(_ n: Int) -> Int { + var p = par[n] + while p != par[p] { + par[p] = par[par[p]] + p = par[p] + } + return p + } + + func union(_ n1: Int, _ n2: Int) -> Bool { + let p1 = find(n1) + let p2 = find(n2) + + if p1 == p2 { + return false + } + if rank[p1] > rank[p2] { + par[p2] = p1 + rank[p1] += rank[p2] + } else { + par[p1] = p2 + rank[p2] += rank[p1] + } + return true + } + + for edge in edges { + let n1 = edge[0] + let n2 = edge[1] + if !union(n1, n2) { + return [n1, n2] + } + } + return [] + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/regular-expression-matching.md b/articles/regular-expression-matching.md index 31b2a26a4..fd4bfda86 100644 --- a/articles/regular-expression-matching.md +++ b/articles/regular-expression-matching.md @@ -184,6 +184,35 @@ class Solution { } ``` +```swift +class Solution { + func isMatch(_ s: String, _ p: String) -> Bool { + let sArr = Array(s), pArr = Array(p) + let m = sArr.count, n = pArr.count + + func dfs(_ i: Int, _ j: Int) -> Bool { + if j == n { + return i == m + } + + let match = i < m && (sArr[i] == pArr[j] || pArr[j] == ".") + + if j + 1 < n && pArr[j + 1] == "*" { + return dfs(i, j + 2) || (match && dfs(i + 1, j)) + } + + if match { + return dfs(i + 1, j + 1) + } + + return false + } + + return dfs(0, 0) + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -432,6 +461,42 @@ class Solution { } ``` +```swift +class Solution { + func isMatch(_ s: String, _ p: String) -> Bool { + let sArr = Array(s), pArr = Array(p) + let m = sArr.count, n = pArr.count + var cache = [[Bool?]](repeating: [Bool?](repeating: nil, count: n + 1), count: m + 1) + + func dfs(_ i: Int, _ j: Int) -> Bool { + if j == n { + return i == m + } + if let cached = cache[i][j] { + return cached + } + + let match = i < m && (sArr[i] == pArr[j] || pArr[j] == ".") + + if j + 1 < n && pArr[j + 1] == "*" { + cache[i][j] = dfs(i, j + 2) || (match && dfs(i + 1, j)) + return cache[i][j]! + } + + if match { + cache[i][j] = dfs(i + 1, j + 1) + return cache[i][j]! + } + + cache[i][j] = false + return false + } + + return dfs(0, 0) + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -635,6 +700,34 @@ class Solution { } ``` +```swift +class Solution { + func isMatch(_ s: String, _ p: String) -> Bool { + let sArr = Array(s), pArr = Array(p) + let m = sArr.count, n = pArr.count + var dp = Array(repeating: Array(repeating: false, count: n + 1), count: m + 1) + dp[m][n] = true + + for i in stride(from: m, through: 0, by: -1) { + for j in stride(from: n - 1, through: 0, by: -1) { + let match = i < m && (sArr[i] == pArr[j] || pArr[j] == ".") + + if j + 1 < n && pArr[j + 1] == "*" { + dp[i][j] = dp[i][j + 2] + if match { + dp[i][j] = dp[i][j] || dp[i + 1][j] + } + } else if match { + dp[i][j] = dp[i + 1][j + 1] + } + } + } + + return dp[0][0] + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -862,6 +955,38 @@ class Solution { } ``` +```swift +class Solution { + func isMatch(_ s: String, _ p: String) -> Bool { + let sArr = Array(s), pArr = Array(p) + var dp = Array(repeating: false, count: pArr.count + 1) + dp[pArr.count] = true + + for i in stride(from: sArr.count, through: 0, by: -1) { + var nextDp = Array(repeating: false, count: pArr.count + 1) + nextDp[pArr.count] = (i == sArr.count) + + for j in stride(from: pArr.count - 1, through: 0, by: -1) { + let match = i < sArr.count && (sArr[i] == pArr[j] || pArr[j] == ".") + + if j + 1 < pArr.count && pArr[j + 1] == "*" { + nextDp[j] = nextDp[j + 2] + if match { + nextDp[j] = nextDp[j] || dp[j] + } + } else if match { + nextDp[j] = dp[j + 1] + } + } + + dp = nextDp + } + + return dp[0] + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -1096,6 +1221,39 @@ class Solution { } ``` +```swift +class Solution { + func isMatch(_ s: String, _ p: String) -> Bool { + let sArr = Array(s) + let pArr = Array(p) + var dp = [Bool](repeating: false, count: pArr.count + 1) + dp[pArr.count] = true + + for i in stride(from: sArr.count, through: 0, by: -1) { + var dp1 = dp[pArr.count] + dp[pArr.count] = (i == sArr.count) + + for j in stride(from: pArr.count - 1, through: 0, by: -1) { + let match = i < sArr.count && (sArr[i] == pArr[j] || pArr[j] == ".") + var res = false + if j + 1 < pArr.count && pArr[j + 1] == "*" { + res = dp[j + 2] + if match { + res = res || dp[j] + } + } else if match { + res = dp1 + } + dp1 = dp[j] + dp[j] = res + } + } + + return dp[0] + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/remove-node-from-end-of-linked-list.md b/articles/remove-node-from-end-of-linked-list.md index 141817504..ee936f416 100644 --- a/articles/remove-node-from-end-of-linked-list.md +++ b/articles/remove-node-from-end-of-linked-list.md @@ -216,6 +216,38 @@ class Solution { } ``` +```swift +/** + * Definition for singly-linked list. + * public class ListNode { + * public var val: Int + * public var next: ListNode? + * public init() { self.val = 0; self.next = nil; } + * public init(_ val: Int) { self.val = val; self.next = nil; } + * public init(_ val: Int, _ next: ListNode?) { self.val = val; self.next = next; } + * } + */ +class Solution { + func removeNthFromEnd(_ head: ListNode?, _ n: Int) -> ListNode? { + var nodes: [ListNode] = [] + var cur = head + + while cur != nil { + nodes.append(cur!) + cur = cur?.next + } + + let removeIndex = nodes.count - n + if removeIndex == 0 { + return head?.next + } + + nodes[removeIndex - 1].next = nodes[removeIndex].next + return head + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -490,6 +522,44 @@ class Solution { } ``` +```swift +/** + * Definition for singly-linked list. + * public class ListNode { + * public var val: Int + * public var next: ListNode? + * public init() { self.val = 0; self.next = nil; } + * public init(_ val: Int) { self.val = val; self.next = nil; } + * public init(_ val: Int, _ next: ListNode?) { self.val = val; self.next = next; } + * } + */ +class Solution { + func removeNthFromEnd(_ head: ListNode?, _ n: Int) -> ListNode? { + var N = 0 + var cur = head + while cur != nil { + N += 1 + cur = cur?.next + } + + let removeIndex = N - n + if removeIndex == 0 { + return head?.next + } + + cur = head + for i in 0..<(N - 1) { + if (i + 1) == removeIndex { + cur?.next = cur?.next?.next + break + } + cur = cur?.next + } + return head + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -723,12 +793,44 @@ class Solution { } ``` +```swift +/** + * Definition for singly-linked list. + * public class ListNode { + * public var val: Int + * public var next: ListNode? + * public init() { self.val = 0; self.next = nil; } + * public init(_ val: Int) { self.val = val; self.next = nil; } + * public init(_ val: Int, _ next: ListNode?) { self.val = val; self.next = next; } + * } + */ +class Solution { + func rec(_ head: ListNode?, _ n: inout Int) -> ListNode? { + if head == nil { + return nil + } + + head?.next = rec(head?.next, &n) + n -= 1 + if n == 0 { + return head?.next + } + return head + } + + func removeNthFromEnd(_ head: ListNode?, _ n: Int) -> ListNode? { + var n = n + return rec(head, &n) + } +} +``` + ::tabs-end ### Time & Space Complexity * Time complexity: $O(N)$ -* Space complexity: $O(N)$ +* Space complexity: $O(N)$ for recursion stack. --- @@ -964,6 +1066,40 @@ class Solution { } ``` +```swift +/** + * Definition for singly-linked list. + * public class ListNode { + * public var val: Int + * public var next: ListNode? + * public init() { self.val = 0; self.next = nil; } + * public init(_ val: Int) { self.val = val; self.next = nil; } + * public init(_ val: Int, _ next: ListNode?) { self.val = val; self.next = next; } + * } + */ +class Solution { + func removeNthFromEnd(_ head: ListNode?, _ n: Int) -> ListNode? { + let dummy = ListNode(0, head) + var left: ListNode? = dummy + var right: ListNode? = head + var n = n + + while n > 0 { + right = right?.next + n -= 1 + } + + while right != nil { + left = left?.next + right = right?.next + } + + left?.next = left?.next?.next + return dummy.next + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/reorder-linked-list.md b/articles/reorder-linked-list.md index 399860a22..75e0ef82c 100644 --- a/articles/reorder-linked-list.md +++ b/articles/reorder-linked-list.md @@ -260,6 +260,47 @@ class Solution { } ``` +```swift +/** + * Definition for singly-linked list. + * public class ListNode { + * public var val: Int + * public var next: ListNode? + * public init() { self.val = 0; self.next = nil; } + * public init(_ val: Int) { self.val = val; self.next = nil; } + * public init(_ val: Int, _ next: ListNode?) { self.val = val; self.next = next; } + * } + */ +class Solution { + func reorderList(_ head: ListNode?) { + if head == nil { + return + } + + var nodes: [ListNode] = [] + var cur = head + + while cur != nil { + nodes.append(cur!) + cur = cur?.next + } + + var i = 0, j = nodes.count - 1 + while i < j { + nodes[i].next = nodes[j] + i += 1 + if i >= j { + break + } + nodes[j].next = nodes[i] + j -= 1 + } + + nodes[i].next = nil + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -286,10 +327,11 @@ class Solution: def rec(root: ListNode, cur: ListNode) -> ListNode: if not cur: return root - root = rec(root, cur.next) + root = rec(root, cur.next) if not root: return None + tmp = None if root == cur or root.next == cur: cur.next = None @@ -297,8 +339,9 @@ class Solution: tmp = root.next root.next = cur cur.next = tmp + return tmp - + head = rec(head, head.next) ``` @@ -323,10 +366,12 @@ public class Solution { if (cur == null) { return root; } + root = rec(root, cur.next); if (root == null) { return null; } + ListNode tmp = null; if (root == cur || root.next == cur) { cur.next = null; @@ -335,6 +380,7 @@ public class Solution { root.next = cur; cur.next = tmp; } + return tmp; } } @@ -363,10 +409,12 @@ private: if (cur == nullptr) { return root; } + root = rec(root, cur->next); if (root == nullptr) { return nullptr; } + ListNode* tmp = nullptr; if (root == cur || root->next == cur) { cur->next = nullptr; @@ -375,6 +423,7 @@ private: root->next = cur; cur->next = tmp; } + return tmp; } }; @@ -409,10 +458,12 @@ class Solution { if (cur === null) { return root; } + root = this.rec(root, cur.next); if (root === null) { return null; } + let tmp = null; if (root === cur || root.next === cur) { cur.next = null; @@ -421,6 +472,7 @@ class Solution { root.next = cur; cur.next = tmp; } + return tmp; } } @@ -448,10 +500,12 @@ public class Solution { if (cur == null) { return root; } + root = Rec(root, cur.next); if (root == null) { return null; } + ListNode tmp = null; if (root == cur || root.next == cur) { cur.next = null; @@ -460,6 +514,7 @@ public class Solution { root.next = cur; cur.next = tmp; } + return tmp; } } @@ -483,11 +538,12 @@ func reorderList(head *ListNode) { if cur == nil { return root } - root = rec(root, cur.Next) + root = rec(root, cur.Next) if root == nil { return nil } + var tmp *ListNode if root == cur || root.Next == cur { cur.Next = nil @@ -496,6 +552,7 @@ func reorderList(head *ListNode) { root.Next = cur cur.Next = tmp } + return tmp } @@ -521,11 +578,12 @@ class Solution { if (cur == null) { return root } - var updatedRoot = rec(root, cur.next) + var updatedRoot = rec(root, cur.next) if (updatedRoot == null) { return null } + var tmp: ListNode? = null if (updatedRoot == cur || updatedRoot?.next == cur) { cur.next = null @@ -534,6 +592,7 @@ class Solution { updatedRoot.next = cur cur.next = tmp } + return tmp } @@ -542,6 +601,46 @@ class Solution { } ``` +```swift +/** + * Definition for singly-linked list. + * public class ListNode { + * public var val: Int + * public var next: ListNode? + * public init() { self.val = 0; self.next = nil; } + * public init(_ val: Int) { self.val = val; self.next = nil; } + * public init(_ val: Int, _ next: ListNode?) { self.val = val; self.next = next; } + * } + */ +class Solution { + func reorderList(_ head: ListNode?) { + func rec(_ root: ListNode?, _ cur: ListNode?) -> ListNode? { + if cur == nil { + return root + } + + var root = rec(root, cur?.next) + if root == nil { + return nil + } + + var tmp: ListNode? = nil + if root === cur || root?.next === cur { + cur?.next = nil + } else { + tmp = root?.next + root?.next = cur + cur?.next = tmp + } + + return tmp + } + + rec(head, head?.next) + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -852,6 +951,51 @@ class Solution { } ``` +```swift +/** + * Definition for singly-linked list. + * public class ListNode { + * public var val: Int + * public var next: ListNode? + * public init() { self.val = 0; self.next = nil; } + * public init(_ val: Int) { self.val = val; self.next = nil; } + * public init(_ val: Int, _ next: ListNode?) { self.val = val; self.next = next; } + * } + */ +class Solution { + func reorderList(_ head: ListNode?) { + var slow = head, fast = head?.next + while fast != nil && fast?.next != nil { + slow = slow?.next + fast = fast?.next?.next + } + + var second = slow?.next + var prev: ListNode? = nil + slow?.next = nil + + while second != nil { + let tmp = second?.next + second?.next = prev + prev = second + second = tmp + } + + var first = head + second = prev + + while second != nil { + let tmp1 = first?.next + let tmp2 = second?.next + first?.next = second + second?.next = tmp1 + first = tmp1 + second = tmp2 + } + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/reverse-a-linked-list.md b/articles/reverse-a-linked-list.md index 98f5ee98d..3a205a543 100644 --- a/articles/reverse-a-linked-list.md +++ b/articles/reverse-a-linked-list.md @@ -200,6 +200,35 @@ class Solution { } ``` +```swift +/** + * Definition for singly-linked list. + * public class ListNode { + * public var val: Int + * public var next: ListNode? + * public init() { self.val = 0; self.next = nil; } + * public init(_ val: Int) { self.val = val; self.next = nil; } + * public init(_ val: Int, _ next: ListNode?) { self.val = val; self.next = next; } + * } + */ +class Solution { + func reverseList(_ head: ListNode?) -> ListNode? { + if head == nil { + return nil + } + + var newHead = head + if head?.next != nil { + newHead = reverseList(head?.next) + head?.next?.next = head + } + head?.next = nil + + return newHead + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -397,6 +426,33 @@ class Solution { } ``` +```swift +/** + * Definition for singly-linked list. + * public class ListNode { + * public var val: Int + * public var next: ListNode? + * public init() { self.val = 0; self.next = nil; } + * public init(_ val: Int) { self.val = val; self.next = nil; } + * public init(_ val: Int, _ next: ListNode?) { self.val = val; self.next = next; } + * } + */ +class Solution { + func reverseList(_ head: ListNode?) -> ListNode? { + var prev: ListNode? = nil + var curr = head + + while curr != nil { + let temp = curr?.next + curr?.next = prev + prev = curr + curr = temp + } + return prev + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/reverse-bits.md b/articles/reverse-bits.md index d5267bf19..84cbe88ce 100644 --- a/articles/reverse-bits.md +++ b/articles/reverse-bits.md @@ -162,6 +162,30 @@ class Solution { } ``` +```swift +class Solution { + func reverseBits(_ n: Int) -> Int { + var binary = "" + for i in 0..<32 { + if (n & (1 << i)) != 0 { + binary += "1" + } else { + binary += "0" + } + } + + var res = 0 + for (i, bit) in binary.reversed().enumerated() { + if bit == "1" { + res |= (1 << i) + } + } + + return res + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -266,6 +290,20 @@ class Solution { } ``` +```swift +class Solution { + func reverseBits(_ n: Int) -> Int { + var res = 0 + var num = n + for i in 0..<32 { + let bit = (num >> i) & 1 + res |= (bit << (31 - i)) + } + return res + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -378,6 +416,20 @@ class Solution { } ``` +```swift +class Solution { + func reverseBits(_ n: Int) -> Int { + var res = n + res = (res >> 16) | (res << 16) & 0xFFFFFFFF + res = ((res & 0xff00ff00) >> 8) | ((res & 0x00ff00ff) << 8) + res = ((res & 0xf0f0f0f0) >> 4) | ((res & 0x0f0f0f0f) << 4) + res = ((res & 0xcccccccc) >> 2) | ((res & 0x33333333) << 2) + res = ((res & 0xaaaaaaaa) >> 1) | ((res & 0x55555555) << 1) + return res & 0xFFFFFFFF + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/reverse-integer.md b/articles/reverse-integer.md index f6d060a33..c388a821f 100644 --- a/articles/reverse-integer.md +++ b/articles/reverse-integer.md @@ -147,6 +147,25 @@ class Solution { } ``` +```swift +class Solution { + func reverse(_ x: Int) -> Int { + let org = x + var x = abs(x) + var res = Int(String(String(x).reversed()))! + + if org < 0 { + res *= -1 + } + if res < Int32.min || res > Int32.max { + return 0 + } + + return res + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -310,6 +329,28 @@ class Solution { } ``` +```swift +class Solution { + func reverse(_ x: Int) -> Int { + func rec(_ n: Int, _ rev: Int) -> Int { + if n == 0 { + return rev + } + return rec(n / 10, rev * 10 + n % 10) + } + + let sign = x < 0 ? -1 : 1 + let reversedNum = rec(abs(x), 0) * sign + + if reversedNum < Int32.min || reversedNum > Int32.max { + return 0 + } + + return reversedNum + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -489,6 +530,33 @@ class Solution { } ``` +```swift +class Solution { + func reverse(_ x: Int) -> Int { + let MIN = Int32.min + let MAX = Int32.max + + var res = 0 + var num = x + + while num != 0 { + let digit = num % 10 + num /= 10 + + if res > MAX / 10 || (res == MAX / 10 && digit > MAX % 10) { + return 0 + } + if res < MIN / 10 || (res == MIN / 10 && digit < MIN % 10) { + return 0 + } + res = res * 10 + digit + } + + return res + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/reverse-nodes-in-k-group.md b/articles/reverse-nodes-in-k-group.md index ad54de13e..f5836142d 100644 --- a/articles/reverse-nodes-in-k-group.md +++ b/articles/reverse-nodes-in-k-group.md @@ -251,6 +251,47 @@ class Solution { } ``` +```swift +/** + * Definition for singly-linked list. + * public class ListNode { + * public var val: Int + * public var next: ListNode? + * public init() { self.val = 0; self.next = nil; } + * public init(_ val: Int) { self.val = val; self.next = nil; } + * public init(_ val: Int, _ next: ListNode?) { self.val = val; self.next = next; } + * } + */ +class Solution { + func reverseKGroup(_ head: ListNode?, _ k: Int) -> ListNode? { + var cur = head + var group = 0 + + while cur != nil && group < k { + cur = cur!.next + group += 1 + } + + if group == k { + cur = reverseKGroup(cur, k) + + var tempHead = head + while group > 0 { + let tmp = tempHead!.next + tempHead!.next = cur + cur = tempHead + tempHead = tmp + group -= 1 + } + + return cur + } + + return head + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -603,6 +644,57 @@ class Solution { } ``` +```swift +/** + * Definition for singly-linked list. + * public class ListNode { + * public var val: Int + * public var next: ListNode? + * public init() { self.val = 0; self.next = nil; } + * public init(_ val: Int) { self.val = val; self.next = nil; } + * public init(_ val: Int, _ next: ListNode?) { self.val = val; self.next = next; } + * } + */ +class Solution { + func reverseKGroup(_ head: ListNode?, _ k: Int) -> ListNode? { + let dummy = ListNode(0, head) + var groupPrev: ListNode? = dummy + + while true { + guard let kth = getKth(groupPrev, k) else { + break + } + let groupNext = kth.next + + var prev: ListNode? = kth.next + var curr = groupPrev?.next + + while curr !== groupNext { + let tmp = curr?.next + curr?.next = prev + prev = curr + curr = tmp + } + + let tmp = groupPrev?.next + groupPrev?.next = kth + groupPrev = tmp + } + return dummy.next + } + + private func getKth(_ curr: ListNode?, _ k: Int) -> ListNode? { + var curr = curr + var k = k + while curr != nil && k > 0 { + curr = curr?.next + k -= 1 + } + return curr + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/rotate-matrix.md b/articles/rotate-matrix.md index 2f050d984..c7ea787f1 100644 --- a/articles/rotate-matrix.md +++ b/articles/rotate-matrix.md @@ -145,6 +145,27 @@ class Solution { } ``` +```swift +class Solution { + func rotate(_ matrix: inout [[Int]]) { + let n = matrix.count + var rotated = Array(repeating: Array(repeating: 0, count: n), count: n) + + for i in 0.. Int { + var grid = grid + var queue = Deque<(Int, Int)>() + var fresh = 0 + var time = 0 + + let ROWS = grid.count + let COLS = grid[0].count + + for r in 0.. 0 && !queue.isEmpty { + let length = queue.count + for _ in 0..= 0 && row < ROWS && col >= 0 && col < COLS && grid[row][col] == 1 { + grid[row][col] = 2 + queue.append((row, col)) + fresh -= 1 + } + } + } + time += 1 + } + + return fresh == 0 ? time : -1 + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -704,6 +752,64 @@ class Solution { } ``` +```swift +class Solution { + func orangesRotting(_ grid: [[Int]]) -> Int { + var grid = grid + let ROWS = grid.count + let COLS = grid[0].count + var fresh = 0 + var time = 0 + + for r in 0.. 0 { + var flag = false + for r in 0..= 0 && row < ROWS && col >= 0 && + col < COLS && grid[row][col] == 1) { + grid[row][col] = 3 + fresh -= 1 + flag = true + } + } + } + } + } + + if !flag { + return -1 + } + + for r in 0.. Bool { + if p == nil && q == nil { + return true + } + if let p = p, let q = q, p.val == q.val { + return isSameTree(p.left, q.left) && isSameTree(p.right, q.right) + } else { + return false + } + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -444,6 +474,45 @@ class Solution { } ``` +```swift +/** + * Definition for a binary tree node. + * public class TreeNode { + * public var val: Int + * public var left: TreeNode? + * public var right: TreeNode? + * public init() { self.val = 0; self.left = nil; self.right = nil; } + * public init(_ val: Int) { self.val = val; self.left = nil; self.right = nil; } + * public init(_ val: Int, _ left: TreeNode?, _ right: TreeNode?) { + * self.val = val + * self.left = left + * self.right = right + * } + * } + */ +class Solution { + func isSameTree(_ p: TreeNode?, _ q: TreeNode?) -> Bool { + var stack: [(TreeNode?, TreeNode?)] = [(p, q)] + + while !stack.isEmpty { + let (node1, node2) = stack.removeLast() + + if node1 == nil && node2 == nil { + continue + } + if node1 == nil || node2 == nil || node1!.val != node2!.val { + return false + } + + stack.append((node1!.right, node2!.right)) + stack.append((node1!.left, node2!.left)) + } + + return true + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -742,6 +811,51 @@ class Solution { } ``` +```swift +/** + * Definition for a binary tree node. + * public class TreeNode { + * public var val: Int + * public var left: TreeNode? + * public var right: TreeNode? + * public init() { self.val = 0; self.left = nil; self.right = nil; } + * public init(_ val: Int) { self.val = val; self.left = nil; self.right = nil; } + * public init(_ val: Int, _ left: TreeNode?, _ right: TreeNode?) { + * self.val = val + * self.left = left + * self.right = right + * } + * } + */ +class Solution { + func isSameTree(_ p: TreeNode?, _ q: TreeNode?) -> Bool { + var q1 = Deque() + var q2 = Deque() + q1.append(p) + q2.append(q) + + while !q1.isEmpty && !q2.isEmpty { + let nodeP = q1.removeFirst() + let nodeQ = q2.removeFirst() + + if nodeP == nil && nodeQ == nil { + continue + } + if nodeP == nil || nodeQ == nil || nodeP!.val != nodeQ!.val { + return false + } + + q1.append(nodeP!.left) + q1.append(nodeP!.right) + q2.append(nodeQ!.left) + q2.append(nodeQ!.right) + } + + return true + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/search-2d-matrix.md b/articles/search-2d-matrix.md index 722cbe2b6..45181e5df 100644 --- a/articles/search-2d-matrix.md +++ b/articles/search-2d-matrix.md @@ -106,6 +106,21 @@ class Solution { } ``` +```swift +class Solution { + func searchMatrix(_ matrix: [[Int]], _ target: Int) -> Bool { + for r in 0.. Bool { + let m = matrix.count + let n = matrix[0].count + var r = 0, c = n - 1 + + while r < m && c >= 0 { + if matrix[r][c] > target { + c -= 1 + } else if matrix[r][c] < target { + r += 1 + } else { + return true + } + } + return false + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -546,6 +582,44 @@ class Solution { } ``` +```swift +class Solution { + func searchMatrix(_ matrix: [[Int]], _ target: Int) -> Bool { + let ROWS = matrix.count + let COLS = matrix[0].count + + var top = 0, bot = ROWS - 1 + while top <= bot { + let row = (top + bot) / 2 + if target > matrix[row][COLS - 1] { + top = row + 1 + } else if target < matrix[row][0] { + bot = row - 1 + } else { + break + } + } + + if !(top <= bot) { + return false + } + let row = (top + bot) / 2 + var l = 0, r = COLS - 1 + while l <= r { + let m = (l + r) / 2 + if target > matrix[row][m] { + l = m + 1 + } else if target < matrix[row][m] { + r = m - 1 + } else { + return true + } + } + return false + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -718,6 +792,31 @@ class Solution { } ``` +```swift +class Solution { + func searchMatrix(_ matrix: [[Int]], _ target: Int) -> Bool { + let ROWS = matrix.count + let COLS = matrix[0].count + + var l = 0, r = ROWS * COLS - 1 + while l <= r { + let m = l + (r - l) / 2 + let row = m / COLS + let col = m % COLS + + if target > matrix[row][col] { + l = m + 1 + } else if target < matrix[row][col] { + r = m - 1 + } else { + return true + } + } + return false + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/search-for-word-ii.md b/articles/search-for-word-ii.md index d304ea983..519307f04 100644 --- a/articles/search-for-word-ii.md +++ b/articles/search-for-word-ii.md @@ -282,6 +282,53 @@ class Solution { } ``` +```swift +class Solution { + func findWords(_ board: [[Character]], _ words: [String]) -> [String] { + let ROWS = board.count + let COLS = board[0].count + var res: [String] = [] + var board = board + + func backtrack(_ r: Int, _ c: Int, _ i: Int, _ word: [Character]) -> Bool { + if i == word.count { + return true + } + if r < 0 || c < 0 || r >= ROWS || c >= COLS || board[r][c] != word[i] { + return false + } + + board[r][c] = "*" + let ret = backtrack(r + 1, c, i + 1, word) || + backtrack(r - 1, c, i + 1, word) || + backtrack(r, c + 1, i + 1, word) || + backtrack(r, c - 1, i + 1, word) + board[r][c] = word[i] + return ret + } + + for word in words { + var flag = false + let wordArray = Array(word) + for r in 0.. children; boolean isWord; @@ -743,6 +790,68 @@ class Solution { } ``` +```swift +class TrieNode { + var children: [Character: TrieNode] = [:] + var isWord: Bool = false + + func addWord(_ word: String) { + var current = self + for char in word { + if current.children[char] == nil { + current.children[char] = TrieNode() + } + current = current.children[char]! + } + current.isWord = true + } +} + +class Solution { + func findWords(_ board: [[Character]], _ words: [String]) -> [String] { + let root = TrieNode() + for word in words { + root.addWord(word) + } + + let ROWS = board.count + let COLS = board[0].count + var result = Set() + var visited = Set<[Int]>() + + func dfs(_ r: Int, _ c: Int, _ node: TrieNode, _ word: String) { + if r < 0 || c < 0 || r >= ROWS || c >= COLS || + visited.contains([r, c]) || node.children[board[r][c]] == nil { + return + } + + visited.insert([r, c]) + let nextNode = node.children[board[r][c]]! + let newWord = word + String(board[r][c]) + + if nextNode.isWord { + result.insert(newWord) + } + + dfs(r + 1, c, nextNode, newWord) + dfs(r - 1, c, nextNode, newWord) + dfs(r, c + 1, nextNode, newWord) + dfs(r, c - 1, nextNode, newWord) + + visited.remove([r, c]) + } + + for r in 0.. [String] { + let root = TrieNode() + for i in 0.. Int { + return Int(c.asciiValue! - Character("a").asciiValue!) + } + + func dfs(_ r: Int, _ c: Int, _ node: TrieNode) { + if r < 0 || c < 0 || r >= ROWS || c >= COLS || + boardCopy[r][c] == "*" || + node.children[getIndex(boardCopy[r][c])] == nil { + return + } + + let tmp = boardCopy[r][c] + boardCopy[r][c] = "*" + let prev = node + let nextNode = node.children[getIndex(tmp)]! + + if nextNode.idx != -1 { + res.append(words[nextNode.idx]) + nextNode.idx = -1 + nextNode.refs -= 1 + if nextNode.refs == 0 { + prev.children[getIndex(tmp)] = nil + boardCopy[r][c] = tmp + return + } + } + + dfs(r + 1, c, nextNode) + dfs(r - 1, c, nextNode) + dfs(r, c + 1, nextNode) + dfs(r, c - 1, nextNode) + + boardCopy[r][c] = tmp + } + + for r in 0.. Bool { + let ROWS = board.count + let COLS = board[0].count + var path = Set<[Int]>() + let wordArray = Array(word) + + func dfs(_ r: Int, _ c: Int, _ i: Int) -> Bool { + if i == wordArray.count { + return true + } + if (r < 0 || c < 0 || r >= ROWS || c >= COLS || + board[r][c] != wordArray[i] || path.contains([r, c])) { + return false + } + + path.insert([r, c]) + let res = dfs(r + 1, c, i + 1) || + dfs(r - 1, c, i + 1) || + dfs(r, c + 1, i + 1) || + dfs(r, c - 1, i + 1) + path.remove([r, c]) + return res + } + + for r in 0.. Bool { + let ROWS = board.count + let COLS = board[0].count + var visited = Array(repeating: Array(repeating: false, count: COLS), count: ROWS) + let wordArray = Array(word) + + func dfs(_ r: Int, _ c: Int, _ i: Int) -> Bool { + if i == wordArray.count { + return true + } + if (r < 0 || c < 0 || r >= ROWS || c >= COLS || + board[r][c] != wordArray[i] || visited[r][c]) { + return false + } + + visited[r][c] = true + let res = dfs(r + 1, c, i + 1) || + dfs(r - 1, c, i + 1) || + dfs(r, c + 1, i + 1) || + dfs(r, c - 1, i + 1) + visited[r][c] = false + return res + } + + for r in 0.. Bool { + let ROWS = board.count + let COLS = board[0].count + let wordArray = Array(word) + var board = board + + func dfs(_ r: Int, _ c: Int, _ i: Int) -> Bool { + if i == wordArray.count { + return true + } + if (r < 0 || c < 0 || r >= ROWS || c >= COLS || + board[r][c] != wordArray[i] || board[r][c] == "#") { + return false + } + + let temp = board[r][c] + board[r][c] = "#" + let res = dfs(r + 1, c, i + 1) || + dfs(r - 1, c, i + 1) || + dfs(r, c + 1, i + 1) || + dfs(r, c - 1, i + 1) + board[r][c] = temp + return res + } + + for r in 0.. String { + var res = [String]() + + func dfs(_ node: TreeNode?) { + guard let node = node else { + res.append("N") + return + } + res.append("\(node.val)") + dfs(node.left) + dfs(node.right) + } + + dfs(root) + return res.joined(separator: ",") + } + + func deserialize(_ data: String) -> TreeNode? { + var vals = data.split(separator: ",").map { String($0) } + var i = 0 + + func dfs() -> TreeNode? { + if vals[i] == "N" { + i += 1 + return nil + } + let node = TreeNode(Int(vals[i])!) + i += 1 + node.left = dfs() + node.right = dfs() + return node + } + + return dfs() + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -863,6 +918,76 @@ class Codec { } ``` +```swift +/** + * Definition for a binary tree node. + * public class TreeNode { + * public var val: Int + * public var left: TreeNode? + * public var right: TreeNode? + * public init(_ val: Int) { + * self.val = val + * self.left = nil + * self.right = nil + * } + * } + */ + +class Codec { + + func serialize(_ root: TreeNode?) -> String { + guard let root = root else { + return "N" + } + + var res = [String]() + var queue: Deque = [root] + + while !queue.isEmpty { + let node = queue.removeFirst() + if let node = node { + res.append("\(node.val)") + queue.append(node.left) + queue.append(node.right) + } else { + res.append("N") + } + } + + return res.joined(separator: ",") + } + + func deserialize(_ data: String) -> TreeNode? { + let vals = data.split(separator: ",").map { String($0) } + guard vals[0] != "N" else { + return nil + } + + let root = TreeNode(Int(vals[0])!) + var queue: Deque = [root] + var index = 1 + + while !queue.isEmpty { + let node = queue.popFirst()! + + if vals[index] != "N" { + node.left = TreeNode(Int(vals[index])!) + queue.append(node.left!) + } + index += 1 + + if vals[index] != "N" { + node.right = TreeNode(Int(vals[index])!) + queue.append(node.right!) + } + index += 1 + } + + return root + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/set-zeroes-in-matrix.md b/articles/set-zeroes-in-matrix.md index 5a265e1f5..72cef4283 100644 --- a/articles/set-zeroes-in-matrix.md +++ b/articles/set-zeroes-in-matrix.md @@ -208,6 +208,35 @@ class Solution { } ``` +```swift +class Solution { + func setZeroes(_ matrix: inout [[Int]]) { + let rows = matrix.count + let cols = matrix[0].count + var mark = matrix + + for r in 0.. 0 { + matrix[r][0] = 0 + } else { + rowZero = true + } + } + } + } + + for r in 1.. Int { + for i in 0.. Int { + var seen = Set() + + for num in nums { + if seen.contains(num) { + seen.remove(num) + } else { + seen.insert(num) + } + } + + return seen.first! + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -397,6 +435,25 @@ class Solution { } ``` +```swift +class Solution { + func singleNumber(_ nums: [Int]) -> Int { + var nums = nums.sorted() + var i = 0 + + while i < nums.count - 1 { + if nums[i] == nums[i + 1] { + i += 2 + } else { + return nums[i] + } + } + + return nums[i] + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -494,6 +551,18 @@ class Solution { } ``` +```swift +class Solution { + func singleNumber(_ nums: [Int]) -> Int { + var res = 0 + for num in nums { + res ^= num + } + return res + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/sliding-window-maximum.md b/articles/sliding-window-maximum.md index c4accb268..a7af08296 100644 --- a/articles/sliding-window-maximum.md +++ b/articles/sliding-window-maximum.md @@ -133,12 +133,32 @@ class Solution { } ``` +```swift +class Solution { + func maxSlidingWindow(_ nums: [Int], _ k: Int) -> [Int] { + var output = [Int]() + + for i in 0...(nums.count - k) { + var maxi = nums[i] + for j in i..<(i + k) { + maxi = max(maxi, nums[j]) + } + output.append(maxi) + } + + return output + } +} +``` + ::tabs-end ### Time & Space Complexity * Time complexity: $O(n * k)$ -* Space complexity: $O(1)$ +* Space complexity: + * $O(1)$ extra space. + * $O(n - k + 1)$ space for the output array. > Where $n$ is the length of the array and $k$ is the size of the window. @@ -537,6 +557,64 @@ class Solution { } ``` +```swift +class SegmentTree { + private var n: Int + private var tree: [Int] + + init(_ N: Int, _ A: [Int]) { + self.n = N + while (self.n & (self.n - 1)) != 0 { + self.n += 1 + } + self.tree = [Int](repeating: Int.min, count: 2 * self.n) + build(N, A) + } + + private func build(_ N: Int, _ A: [Int]) { + for i in 0.. Int { + var res = Int.min + var l = l + n + var r = r + n + 1 + while l < r { + if l & 1 != 0 { + res = max(res, tree[l]) + l += 1 + } + if r & 1 != 0 { + r -= 1 + res = max(res, tree[r]) + } + l >>= 1 + r >>= 1 + } + return res + } +} + +class Solution { + func maxSlidingWindow(_ nums: [Int], _ k: Int) -> [Int] { + let n = nums.count + let segTree = SegmentTree(n, nums) + var output = [Int]() + + for i in 0...(n - k) { + output.append(segTree.query(i, i + k - 1)) + } + + return output + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -635,8 +713,9 @@ class Solution { ```csharp public class Solution { public int[] MaxSlidingWindow(int[] nums, int k) { - PriorityQueue<(int val, int idx), int> pq = new PriorityQueue<(int val, int idx), int> - (Comparer.Create((a, b) => b.CompareTo(a))); + PriorityQueue<(int val, int idx), int> pq = new PriorityQueue<(int val, int idx), int>( + Comparer.Create((a, b) => b.CompareTo(a)) + ); int[] output = new int[nums.Length - k + 1]; int idx = 0; @@ -700,6 +779,36 @@ class Solution { } ``` +```swift +class Solution { + func maxSlidingWindow(_ nums: [Int], _ k: Int) -> [Int] { + var heap = Heap() + var output = [Int]() + + for i in 0..= k - 1 { + while heap.max!.index <= i - k { + heap.removeMax() + } + output.append(heap.max!.num) + } + } + return output + } +} + +struct Item: Comparable { + let num: Int + let index: Int + + static func < (lhs: Item, rhs: Item) -> Bool { + return lhs.num < rhs.num + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -963,6 +1072,41 @@ class Solution { } ``` +```swift +class Solution { + func maxSlidingWindow(_ nums: [Int], _ k: Int) -> [Int] { + let n = nums.count + var leftMax = [Int](repeating: 0, count: n) + var rightMax = [Int](repeating: 0, count: n) + + leftMax[0] = nums[0] + rightMax[n - 1] = nums[n - 1] + + for i in 1.. [Int] { + var output = [Int]() + var deque = [Int]() // Index + var l = 0, r = 0 + + while r < nums.count { + while !deque.isEmpty && nums[deque.last!] < nums[r] { + deque.removeLast() + } + deque.append(r) + + if l > deque.first! { + deque.removeFirst() + } + + if (r + 1) >= k { + output.append(nums[deque.first!]) + l += 1 + } + r += 1 + } + + return output + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/spiral-matrix.md b/articles/spiral-matrix.md index 7935874fc..2e82fff76 100644 --- a/articles/spiral-matrix.md +++ b/articles/spiral-matrix.md @@ -198,6 +198,39 @@ class Solution { } ``` +```swift +class Solution { + func spiralOrder(_ matrix: [[Int]]) -> [Int] { + var m = matrix.count + var n = matrix[0].count + var res: [Int] = [] + + // append all the elements in the given direction + func dfs(_ row: Int, _ col: Int, _ r: inout Int, _ c: inout Int, _ dr: Int, _ dc: Int) { + if row == 0 || col == 0 { + return + } + + for _ in 0.. [Int] { + var res: [Int] = [] + var left = 0, right = matrix[0].count + var top = 0, bottom = matrix.count + + while left < right && top < bottom { + for i in left.. [Int] { + var res: [Int] = [] + let directions = [(0, 1), (1, 0), (0, -1), (-1, 0)] + var steps = [matrix[0].count, matrix.count - 1] + + var r = 0, c = -1, d = 0 + while steps[d & 1] > 0 { + for _ in 0.. String { + if strs.isEmpty { return "" } + + var sizes: [Int] = [] + var res = "" + for s in strs { + sizes.append(s.count) + } + for sz in sizes { + res += String(sz) + res += "," + } + + res += "#" + for s in strs { + res += s + } + return res + } + + func decode(_ s: String) -> [String] { + if s.isEmpty { return [] } + let sArr = Array(s) + var sizes: [Int] = [] + var res: [String] = [] + var i = 0 + + while sArr[i] != "#" { + var cur = "" + while sArr[i] != "," { + cur.append(sArr[i]) + i += 1 + } + sizes.append(Int(cur)!) + i += 1 + } + + i += 1 + for sz in sizes { + let substring = String(sArr[i.. String { + var res = "" + for s in strs { + res += "\(s.count)#\(s)" + } + return res + } + + func decode(_ s: String) -> [String] { + var res = [String]() + let sArr = Array(s) + var i = 0 + + while i < sArr.count { + var j = i + while sArr[j] != "#" { + j += 1 + } + let lengthStr = String(sArr[i.. [[Int]] { + var res = Set<[Int]>() + var subset = [Int]() + let nums = nums.sorted() + + func backtrack(_ i: Int) { + if i == nums.count { + res.insert(subset) + return + } + + subset.append(nums[i]) + backtrack(i + 1) + subset.removeLast() + backtrack(i + 1) + } + + backtrack(0) + return Array(res) + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -202,7 +227,7 @@ class Solution { --- -## 2. Backtracking (Pick / Not Pick) +## 2. Backtracking - I ::tabs-start @@ -405,16 +430,48 @@ class Solution { } ``` +```swift +class Solution { + func subsetsWithDup(_ nums: [Int]) -> [[Int]] { + var res = [[Int]]() + var subset = [Int]() + let nums = nums.sorted() + + func backtrack(_ i: Int) { + if i == nums.count { + res.append(subset) + return + } + + subset.append(nums[i]) + backtrack(i + 1) + subset.removeLast() + + var j = i + while j + 1 < nums.count && nums[j] == nums[j + 1] { + j += 1 + } + backtrack(j + 1) + } + + backtrack(0) + return res + } +} +``` + ::tabs-end ### Time & Space Complexity * Time complexity: $O(n * 2 ^ n)$ -* Space complexity: $O(n)$ +* Space complexity: + * $O(n)$ extra space. + * $O(2 ^ n)$ space for the output list. --- -## 3. Backtracking +## 3. Backtracking - II ::tabs-start @@ -595,12 +652,40 @@ class Solution { } ``` +```swift +class Solution { + func subsetsWithDup(_ nums: [Int]) -> [[Int]] { + var res = [[Int]]() + var subset = [Int]() + let nums = nums.sorted() + + func backtrack(_ i: Int) { + res.append(subset) + + for j in i.. i && nums[j] == nums[j - 1] { + continue + } + subset.append(nums[j]) + backtrack(j + 1) + subset.removeLast() + } + } + + backtrack(0) + return res + } +} +``` + ::tabs-end ### Time & Space Complexity * Time complexity: $O(n * 2 ^ n)$ -* Space complexity: $O(n)$ +* Space complexity: + * $O(n)$ extra space. + * $O(2 ^ n)$ space for the output list. --- @@ -614,6 +699,7 @@ class Solution: nums.sort() res = [[]] prev_Idx = idx = 0 + for i in range(len(nums)): idx = prev_idx if i >= 1 and nums[i] == nums[i - 1] else 0 prev_idx = len(res) @@ -621,6 +707,7 @@ class Solution: tmp = res[j].copy() tmp.append(nums[i]) res.append(tmp) + return res ``` @@ -632,6 +719,7 @@ public class Solution { res.add(new ArrayList<>()); int prevIdx = 0; int idx = 0; + for (int i = 0; i < nums.length; i++) { idx = (i >= 1 && nums[i] == nums[i - 1]) ? prevIdx : 0; prevIdx = res.size(); @@ -641,6 +729,7 @@ public class Solution { res.add(tmp); } } + return res; } } @@ -654,6 +743,7 @@ public: vector> res = {{}}; int prevIdx = 0; int idx = 0; + for (int i = 0; i < nums.size(); i++) { idx = (i >= 1 && nums[i] == nums[i - 1]) ? prevIdx : 0; prevIdx = res.size(); @@ -663,6 +753,7 @@ public: res.push_back(tmp); } } + return res; } }; @@ -679,6 +770,7 @@ class Solution { const res = [[]]; let prevIdx = 0; let idx = 0; + for (let i = 0; i < nums.length; i++) { idx = (i >= 1 && nums[i] === nums[i - 1]) ? prevIdx : 0; prevIdx = res.length; @@ -688,6 +780,7 @@ class Solution { res.push(tmp); } } + return res; } } @@ -700,6 +793,7 @@ public class Solution { var res = new List> { new List() }; int prevIdx = 0; int idx = 0; + for (int i = 0; i < nums.Length; i++) { idx = (i >= 1 && nums[i] == nums[i - 1]) ? prevIdx : 0; prevIdx = res.Count; @@ -709,6 +803,7 @@ public class Solution { res.Add(tmp); } } + return res; } } @@ -761,9 +856,35 @@ class Solution { } ``` +```swift +class Solution { + func subsetsWithDup(_ nums: [Int]) -> [[Int]] { + let nums = nums.sorted() + var res: [[Int]] = [[]] + var prevIdx = 0 + var idx = 0 + + for i in 0..= 1 && nums[i] == nums[i - 1]) ? prevIdx : 0 + prevIdx = res.count + + for j in idx.. List[List[int]]: res = [] - subset = [] def dfs(i): @@ -172,12 +171,37 @@ class Solution { } ``` +```swift +class Solution { + func subsets(_ nums: [Int]) -> [[Int]] { + var res = [[Int]]() + var subset = [Int]() + + func dfs(_ i: Int) { + if i >= nums.count { + res.append(subset) + return + } + subset.append(nums[i]) + dfs(i + 1) + subset.removeLast() + dfs(i + 1) + } + + dfs(0) + return res + } +} +``` + ::tabs-end ### Time & Space Complexity * Time complexity: $O(n * 2 ^ n)$ -* Space complexity: $O(n)$ +* Space complexity: + * $O(n)$ extra space. + * $O(2 ^ n)$ for the output list. --- @@ -316,12 +340,28 @@ class Solution { } ``` +```swift +class Solution { + func subsets(_ nums: [Int]) -> [[Int]] { + var res: [[Int]] = [[]] + + for num in nums { + res += res.map { $0 + [num] } + } + + return res + } +} +``` + ::tabs-end ### Time & Space Complexity * Time complexity: $O(n * 2 ^ n)$ -* Space complexity: $O(n)$ +* Space complexity: + * $O(n)$ extra space. + * $O(2 ^ n)$ for the output list. --- @@ -461,9 +501,32 @@ class Solution { } ``` +```swift +class Solution { + func subsets(_ nums: [Int]) -> [[Int]] { + let n = nums.count + var res: [[Int]] = [] + + for i in 0..<(1 << n) { + var subset: [Int] = [] + for j in 0.. Bool { + if subRoot == nil { + return true + } + if root == nil { + return false + } + if sameTree(root, subRoot) { + return true + } + return isSubtree(root?.left, subRoot) || isSubtree(root?.right, subRoot) + } + + func sameTree(_ root: TreeNode?, _ subRoot: TreeNode?) -> Bool { + if root == nil && subRoot == nil { + return true + } + if let root = root, let subRoot = subRoot, root.val == subRoot.val { + return sameTree(root.left, subRoot.left) && sameTree(root.right, subRoot.right) + } + return false + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -319,11 +361,10 @@ class Solution { class Solution: def serialize(self, root: Optional[TreeNode]) -> str: - if root == None: - return "$#" - - return ("$" + str(root.val) + - self.serialize(root.left) + self.serialize(root.right)) + if root == None: + return "$#" + + return ("$" + str(root.val) + self.serialize(root.left) + self.serialize(root.right)) def z_function(self, s: str) -> list: z = [0] * len(s) @@ -341,10 +382,10 @@ class Solution: serialized_root = self.serialize(root) serialized_subRoot = self.serialize(subRoot) combined = serialized_subRoot + "|" + serialized_root - + z_values = self.z_function(combined) sub_len = len(serialized_subRoot) - + for i in range(sub_len + 1, len(combined)): if z_values[i] == sub_len: return True @@ -492,8 +533,7 @@ class Solution { if (root === null) { return "$#"; } - return "$" + root.val + - this.serialize(root.left) + this.serialize(root.right); + return "$" + root.val + this.serialize(root.left) + this.serialize(root.right); } /** @@ -714,6 +754,69 @@ class Solution { } ``` +```swift +/** + * Definition for a binary tree node. + * public class TreeNode { + * public var val: Int + * public var left: TreeNode? + * public var right: TreeNode? + * public init() { self.val = 0; self.left = nil; self.right = nil; } + * public init(_ val: Int) { self.val = val; self.left = nil; self.right = nil; } + * public init(_ val: Int, _ left: TreeNode?, _ right: TreeNode?) { + * self.val = val + * self.left = left + * self.right = right + * } + * } + */ +class Solution { + func serialize(_ root: TreeNode?) -> String { + guard let root = root else { + return "$#" + } + return "$\(root.val)" + serialize(root.left) + serialize(root.right) + } + + func zFunction(_ s: String) -> [Int] { + let n = s.count + var z = [Int](repeating: 0, count: n) + var l = 0, r = 0 + let chars = Array(s) + + for i in 1.. r { + l = i + r = i + z[i] - 1 + } + } + return z + } + + func isSubtree(_ root: TreeNode?, _ subRoot: TreeNode?) -> Bool { + let serializedRoot = serialize(root) + let serializedSubRoot = serialize(subRoot) + let combined = serializedSubRoot + "|" + serializedRoot + + let zValues = zFunction(combined) + let subLen = serializedSubRoot.count + + for i in (subLen + 1).. Int { + return a + b + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -250,6 +258,32 @@ class Solution { } ``` +```swift +class Solution { + func getSum(_ a: Int, _ b: Int) -> Int { + var carry = 0 + var res = 0 + let mask = 0xFFFFFFFF + + for i in 0..<32 { + let aBit = (a >> i) & 1 + let bBit = (b >> i) & 1 + let curBit = aBit ^ bBit ^ carry + carry = (aBit + bBit + carry) >= 2 ? 1 : 0 + if curBit == 1 { + res |= (1 << i) + } + } + + if res > 0x7FFFFFFF { + res = ~(res ^ mask) + } + + return res + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -379,6 +413,25 @@ class Solution { } ``` +```swift +class Solution { + func getSum(_ a: Int, _ b: Int) -> Int { + let mask = 0xFFFFFFFF + let maxInt = 0x7FFFFFFF + var a = a + var b = b + + while b != 0 { + let carry = (a & b) << 1 + a = (a ^ b) & mask + b = carry & mask + } + + return a <= maxInt ? a : ~(a ^ mask) + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/surrounded-regions.md b/articles/surrounded-regions.md index f1009a0bc..1c0c5445c 100644 --- a/articles/surrounded-regions.md +++ b/articles/surrounded-regions.md @@ -330,6 +330,54 @@ class Solution { } ``` +```swift +class Solution { + func solve(_ board: inout [[Character]]) { + let ROWS = board.count + let COLS = board[0].count + + func capture(_ r: Int, _ c: Int) { + if r < 0 || c < 0 || r == ROWS || c == COLS || board[r][c] != "O" { + return + } + board[r][c] = "T" + capture(r + 1, c) + capture(r - 1, c) + capture(r, c + 1) + capture(r, c - 1) + } + + for r in 0.. { - let q = []; + let q = new Queue(); for (let r = 0; r < ROWS; r++) { for (let c = 0; c < COLS; c++) { if (r === 0 || r === ROWS - 1 || @@ -506,8 +554,8 @@ class Solution { } } } - while (q.length) { - let [r, c] = q.shift(); + while (!q.isEmpty()) { + let [r, c] = q.pop(); if (board[r][c] === 'O') { board[r][c] = 'T'; for (let [dr, dc] of directions) { @@ -684,6 +732,53 @@ class Solution { } ``` +```swift +class Solution { + func solve(_ board: inout [[Character]]) { + let ROWS = board.count + let COLS = board[0].count + let directions = [(1, 0), (-1, 0), (0, 1), (0, -1)] + + func capture() { + var queue = Deque<(Int, Int)>() + for r in 0..= 0, nr < ROWS, nc >= 0, nc < COLS { + queue.append((nr, nc)) + } + } + } + } + } + + capture() + + for r in 0.. Int { + if parent[node] != node { + parent[node] = find(parent[node]) + } + return parent[node] + } + + func union(_ u: Int, _ v: Int) { + let pu = find(u) + let pv = find(v) + if pu == pv { return } + if size[pu] >= size[pv] { + size[pu] += size[pv] + parent[pv] = pu + } else { + size[pv] += size[pu] + parent[pu] = pv + } + } + + func connected(_ u: Int, _ v: Int) -> Bool { + return find(u) == find(v) + } +} + +class Solution { + func solve(_ board: inout [[Character]]) { + let ROWS = board.count + let COLS = board[0].count + let directions = [(1, 0), (-1, 0), (0, 1), (0, -1)] + let dsu = DSU(ROWS * COLS + 1) + let N = ROWS * COLS + + for r in 0..= 0, nr < ROWS, nc >= 0, nc < COLS, board[nr][nc] == "O" { + dsu.union(r * COLS + c, nr * COLS + nc) + } + } + } + } + } + + for r in 0.. Int { + let n = grid.count + var visit = Array(repeating: Array(repeating: false, count: n), count: n) + + func dfs(_ node: (Int, Int), _ t: Int) -> Int { + let (r, c) = node + if r < 0 || c < 0 || r >= n || c >= n || visit[r][c] { + return 1000000 + } + if r == n - 1 && c == n - 1 { + return max(t, grid[r][c]) + } + visit[r][c] = true + let t = max(t, grid[r][c]) + let res = min(dfs((r + 1, c), t), + dfs((r - 1, c), t), + dfs((r, c + 1), t), + dfs((r, c - 1), t)) + visit[r][c] = false + return res + } + + return dfs((0, 0), 0) + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -544,6 +573,51 @@ class Solution { } ``` +```swift +class Solution { + func swimInWater(_ grid: [[Int]]) -> Int { + let n = grid.count + var visit = Array(repeating: Array(repeating: false, count: n), count: n) + var minH = grid[0][0], maxH = grid[0][0] + + for row in 0.. Bool { + let (r, c) = node + if r < 0 || c < 0 || r >= n || c >= n || visit[r][c] || grid[r][c] > t { + return false + } + if r == n - 1 && c == n - 1 { + return true + } + visit[r][c] = true + return dfs((r + 1, c), t) || + dfs((r - 1, c), t) || + dfs((r, c + 1), t) || + dfs((r, c - 1), t) + } + + for t in minH...maxH { + if dfs((0, 0), t) { + return t + } + for r in 0.. Int { + let n = grid.count + var visit = Array(repeating: Array(repeating: false, count: n), count: n) + var minH = grid[0][0], maxH = grid[0][0] + + for row in 0.. Bool { + let (r, c) = node + if r < 0 || c < 0 || r >= n || c >= n || visit[r][c] || grid[r][c] > t { + return false + } + if r == n - 1 && c == n - 1 { + return true + } + visit[r][c] = true + return dfs((r + 1, c), t) || + dfs((r - 1, c), t) || + dfs((r, c + 1), t) || + dfs((r, c - 1), t) + } + + var l = minH, r = maxH + while l < r { + let m = (l + r) >> 1 + if dfs((0, 0), m) { + r = m + } else { + l = m + 1 + } + for row in 0.. Bool { + return lhs.time < rhs.time + } +} + +class Solution { + func swimInWater(_ grid: [[Int]]) -> Int { + let N = grid.count + var visit = Set<[Int]>() + var minHeap = Heap() + + let directions = [(0, 1), (0, -1), (1, 0), (-1, 0)] + + minHeap.insert(Item(time: grid[0][0], row: 0, col: 0)) + visit.insert([0, 0]) + + while !minHeap.isEmpty { + let item = minHeap.removeMin() + let t = item.time, r = item.row, c = item.col + + if r == N - 1 && c == N - 1 { + return t + } + + for (dr, dc) in directions { + let neiR = r + dr, neiC = c + dc + if neiR < 0 || neiC < 0 || neiR == N || neiC == N || visit.contains([neiR, neiC]) { + continue + } + visit.insert([neiR, neiC]) + minHeap.insert(Item(time: max(t, grid[neiR][neiC]), row: neiR, col: neiC)) + } + } + + return -1 + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -1230,7 +1398,7 @@ class Solution: ``` ```java -public class DSU { +class DSU { private int[] Parent; private int[] Size; @@ -1625,6 +1793,75 @@ class Solution { } ``` +```swift +class DSU { + private var parent: [Int] + private var size: [Int] + + init(_ n: Int) { + parent = Array(0...(n - 1)) + size = Array(repeating: 1, count: n) + } + + func find(_ node: Int) -> Int { + if parent[node] != node { + parent[node] = find(parent[node]) + } + return parent[node] + } + + func union(_ u: Int, _ v: Int) -> Bool { + let pu = find(u) + let pv = find(v) + if pu == pv { + return false + } + if size[pu] < size[pv] { + parent[pu] = pv + size[pv] += size[pu] + } else { + parent[pv] = pu + size[pu] += size[pv] + } + return true + } + + func connected(_ u: Int, _ v: Int) -> Bool { + return find(u) == find(v) + } +} + +class Solution { + func swimInWater(_ grid: [[Int]]) -> Int { + let N = grid.count + let dsu = DSU(N * N) + var positions = [(Int, Int, Int)]() + let directions = [(0, 1), (1, 0), (0, -1), (-1, 0)] + + for r in 0..= 0, nc >= 0, nr < N, nc < N, grid[nr][nc] <= t { + dsu.union(r * N + c, nr * N + nc) + } + } + if dsu.connected(0, N * N - 1) { + return t + } + } + return -1 + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/target-sum.md b/articles/target-sum.md index 819f11b56..71c10c0c2 100644 --- a/articles/target-sum.md +++ b/articles/target-sum.md @@ -120,6 +120,20 @@ class Solution { } ``` +```swift +class Solution { + func findTargetSumWays(_ nums: [Int], _ target: Int) -> Int { + func backtrack(_ i: Int, _ total: Int) -> Int { + if i == nums.count { + return total == target ? 1 : 0 + } + return backtrack(i + 1, total + nums[i]) + backtrack(i + 1, total - nums[i]) + } + return backtrack(0, 0) + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -331,6 +345,29 @@ class Solution { } ``` +```swift +class Solution { + func findTargetSumWays(_ nums: [Int], _ target: Int) -> Int { + let totalSum = nums.reduce(0, +) + var dp = Array(repeating: Array(repeating: Int.min, count: 2 * totalSum + 1), count: nums.count) + + func backtrack(_ i: Int, _ total: Int) -> Int { + if i == nums.count { + return total == target ? 1 : 0 + } + if dp[i][total + totalSum] != Int.min { + return dp[i][total + totalSum] + } + dp[i][total + totalSum] = backtrack(i + 1, total + nums[i]) + + backtrack(i + 1, total - nums[i]) + return dp[i][total + totalSum] + } + + return backtrack(0, 0) + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -502,6 +539,24 @@ class Solution { } ``` +```swift +class Solution { + func findTargetSumWays(_ nums: [Int], _ target: Int) -> Int { + let n = nums.count + var dp = Array(repeating: [Int: Int](), count: n + 1) + dp[0][0] = 1 + + for i in 0.. Int { + var dp = [0: 1] + + for num in nums { + var nextDp = [Int: Int]() + for (total, count) in dp { + nextDp[total + num, default: 0] += count + nextDp[total - num, default: 0] += count + } + dp = nextDp + } + return dp[target, default: 0] + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/task-scheduling.md b/articles/task-scheduling.md index d6718ba3f..03e752212 100644 --- a/articles/task-scheduling.md +++ b/articles/task-scheduling.md @@ -330,6 +330,50 @@ class Solution { } ``` +```swift +class Solution { + func leastInterval(_ tasks: [Character], _ n: Int) -> Int { + var count = [Int](repeating: 0, count: 26) + for task in tasks { + count[Int(task.asciiValue! - Character("A").asciiValue!)] += 1 + } + + var arr = [(Int, Int)]() + for i in 0..<26 { + if count[i] > 0 { + arr.append((count[i], i)) + } + } + + var time = 0 + var processed = [Int]() + + while !arr.isEmpty { + var maxi = -1 + for i in 0.. Int { + var count = [Character: Int]() + for task in tasks { + count[task, default: 0] += 1 + } + + var maxHeap = Heap(Array(count.values)) + var time = 0 + var queue = Deque<(Int, Int)>() + + while !maxHeap.isEmpty || !queue.isEmpty { + time += 1 + if maxHeap.isEmpty { + time = queue.first!.1 + } else { + let cnt = maxHeap.popMax()! - 1 + if cnt > 0 { + queue.append((cnt, time + n)) + } + } + if let front = queue.first, front.1 == time { + maxHeap.insert(front.0) + queue.removeFirst() + } + } + + return time + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -775,6 +852,27 @@ class Solution { } ``` +```swift +class Solution { + func leastInterval(_ tasks: [Character], _ n: Int) -> Int { + var count = [Int](repeating: 0, count: 26) + for task in tasks { + count[Int(task.asciiValue! - Character("A").asciiValue!)] += 1 + } + + count.sort() + let maxf = count[25] + var idle = (maxf - 1) * n + + for i in stride(from: 24, through: 0, by: -1) { + idle -= min(maxf - 1, count[i]) + } + + return max(0, idle) + tasks.count + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -951,6 +1049,28 @@ class Solution { } ``` +```swift +class Solution { + func leastInterval(_ tasks: [Character], _ n: Int) -> Int { + var count = [Int](repeating: 0, count: 26) + for task in tasks { + count[Int(task.asciiValue! - Character("A").asciiValue!)] += 1 + } + + let maxf = count.max()! + var maxCount = 0 + for i in count { + if i == maxf { + maxCount += 1 + } + } + + let time = (maxf - 1) * (n + 1) + maxCount + return max(tasks.count, time) + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/three-integer-sum.md b/articles/three-integer-sum.md index 74afca85c..18b4b76b7 100644 --- a/articles/three-integer-sum.md +++ b/articles/three-integer-sum.md @@ -145,6 +145,27 @@ class Solution { } ``` +```swift +class Solution { + func threeSum(_ nums: [Int]) -> [[Int]] { + var res = Set<[Int]>() + let nums = nums.sorted() + + for i in 0.. [[Int]] { + var nums = nums.sorted() + var count = [Int: Int]() + for num in nums { + count[num, default: 0] += 1 + } + + var res = [[Int]]() + for i in 0.. 0 && nums[i] == nums[i - 1] { + continue + } + + for j in (i + 1).. i + 1 && nums[j] == nums[j - 1] { + continue + } + let target = -(nums[i] + nums[j]) + if let cnt = count[target], cnt > 0 { + res.append([nums[i], nums[j], target]) + } + } + + for j in (i + 1).. [[Int]] { + var res = [[Int]]() + var nums = nums.sorted() + + for i in 0.. 0 { + break + } + if i > 0 && a == nums[i - 1] { + continue + } + + var l = i + 1, r = nums.count - 1 + while l < r { + let threeSum = a + nums[l] + nums[r] + if threeSum > 0 { + r -= 1 + } else if threeSum < 0 { + l += 1 + } else { + res.append([a, nums[l], nums[r]]) + l += 1 + r -= 1 + while l < r && nums[l] == nums[l - 1] { + l += 1 + } + } + } + } + return res + } +} +``` + ::tabs-end ### Time & Space Complexity * Time complexity: $O(n ^ 2)$ -* Space complexity: $O(1)$ or $O(n)$ depending on the sorting algorithm. \ No newline at end of file +* Space complexity: + * $O(1)$ or $O(n)$ extra space depending on the sorting algorithm. + * $O(m)$ space for the output list. + +> Where $m$ is the number of triplets and $n$ is the length of the given array. \ No newline at end of file diff --git a/articles/time-based-key-value-store.md b/articles/time-based-key-value-store.md index bfdc75b3e..8fc87e0e5 100644 --- a/articles/time-based-key-value-store.md +++ b/articles/time-based-key-value-store.md @@ -244,6 +244,40 @@ class TimeMap() { } ``` +```swift +class TimeMap { + private var keyStore: [String: [Int: [String]]] + + init() { + self.keyStore = [:] + } + + func set(_ key: String, _ value: String, _ timestamp: Int) { + if keyStore[key] == nil { + keyStore[key] = [:] + } + if keyStore[key]![timestamp] == nil { + keyStore[key]![timestamp] = [] + } + keyStore[key]![timestamp]!.append(value) + } + + func get(_ key: String, _ timestamp: Int) -> String { + guard let timeMap = keyStore[key] else { + return "" + } + + var seen = 0 + for time in timeMap.keys { + if time <= timestamp { + seen = max(seen, time) + } + } + return seen == 0 ? "" : timeMap[seen]!.last! + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -458,6 +492,44 @@ class TimeMap() { } ``` +```swift +class TimeMap { + private var m: [String: [(Int, String)]] + + init() { + self.m = [:] + } + + func set(_ key: String, _ value: String, _ timestamp: Int) { + if m[key] == nil { + m[key] = [] + } + m[key]!.append((timestamp, value)) + } + + func get(_ key: String, _ timestamp: Int) -> String { + guard let timestamps = m[key] else { + return "" + } + + var l = 0, r = timestamps.count - 1 + var res = "" + + while l <= r { + let mid = (l + r) / 2 + if timestamps[mid].0 <= timestamp { + res = timestamps[mid].1 + l = mid + 1 + } else { + r = mid - 1 + } + } + + return res + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -739,6 +811,44 @@ class TimeMap() { } ``` +```swift +class TimeMap { + private var keyStore: [String: [(String, Int)]] + + init() { + self.keyStore = [:] + } + + func set(_ key: String, _ value: String, _ timestamp: Int) { + if keyStore[key] == nil { + keyStore[key] = [] + } + keyStore[key]!.append((value, timestamp)) + } + + func get(_ key: String, _ timestamp: Int) -> String { + guard let values = keyStore[key] else { + return "" + } + + var res = "" + var l = 0, r = values.count - 1 + + while l <= r { + let m = (l + r) / 2 + if values[m].1 <= timestamp { + res = values[m].0 + l = m + 1 + } else { + r = m - 1 + } + } + + return res + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/top-k-elements-in-list.md b/articles/top-k-elements-in-list.md index ae16871e1..7bca2df74 100644 --- a/articles/top-k-elements-in-list.md +++ b/articles/top-k-elements-in-list.md @@ -156,6 +156,30 @@ class Solution { } ``` +```swift +class Solution { + func topKFrequent(_ nums: [Int], _ k: Int) -> [Int] { + var count = [Int: Int]() + for num in nums { + count[num, default: 0] += 1 + } + + var arr = [(Int, Int)]() + for (num, cnt) in count { + arr.append((cnt, num)) + } + arr.sort { $0.0 < $1.0 } + + var res = [Int]() + while res.count < k { + res.append(arr.removeLast().1) + } + + return res + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -165,7 +189,7 @@ class Solution { --- -## 2. Heap +## 2. Min-Heap ::tabs-start @@ -352,6 +376,41 @@ class Solution { } ``` +```swift +struct NumFreq: Comparable { + let num: Int + let freq: Int + + static func < (lhs: NumFreq, rhs: NumFreq) -> Bool { + return lhs.freq < rhs.freq + } +} + +class Solution { + func topKFrequent(_ nums: [Int], _ k: Int) -> [Int] { + var count = [Int: Int]() + for num in nums { + count[num, default: 0] += 1 + } + + var heap: Heap = [] + for (num, freq) in count { + heap.insert(NumFreq(num: num, freq: freq)) + if heap.count > k { + heap.removeMin() + } + } + + var res = [Int]() + while !heap.isEmpty { + res.append(heap.removeMin().num) + } + + return res + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -564,6 +623,35 @@ class Solution { } ``` +```swift +class Solution { + func topKFrequent(_ nums: [Int], _ k: Int) -> [Int] { + var count = [Int: Int]() + var freq = [[Int]](repeating: [], count: nums.count + 1) + + for num in nums { + count[num, default: 0] += 1 + } + + for (num, cnt) in count { + freq[cnt].append(num) + } + + var res = [Int]() + for i in stride(from: freq.count - 1, through: 1, by: -1) { + for num in freq[i] { + res.append(num) + if res.count == k { + return res + } + } + } + + return res + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/trapping-rain-water.md b/articles/trapping-rain-water.md index 73635a85d..4e8f600b8 100644 --- a/articles/trapping-rain-water.md +++ b/articles/trapping-rain-water.md @@ -197,6 +197,33 @@ class Solution { } ``` +```swift +class Solution { + func trap(_ height: [Int]) -> Int { + if height.isEmpty { + return 0 + } + let n = height.count + var res = 0 + + for i in 0.. Int { + let n = height.count + if n == 0 { + return 0 + } + + var leftMax = [Int](repeating: 0, count: n) + var rightMax = [Int](repeating: 0, count: n) + + leftMax[0] = height[0] + for i in 1.. Int { + if height.isEmpty { + return 0 + } + var stack = [Int]() + var res = 0 + + for i in 0..= height[stack.last!] { + let mid = height[stack.removeLast()] + if !stack.isEmpty { + let right = height[i] + let left = height[stack.last!] + let h = min(right, left) - mid + let w = i - stack.last! - 1 + res += h * w + } + } + stack.append(i) + } + return res + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -852,6 +936,33 @@ class Solution { } ``` +```swift +class Solution { + func trap(_ height: [Int]) -> Int { + if height.isEmpty { + return 0 + } + + var l = 0, r = height.count - 1 + var leftMax = height[l], rightMax = height[r] + var res = 0 + + while l < r { + if leftMax < rightMax { + l += 1 + leftMax = max(leftMax, height[l]) + res += leftMax - height[l] + } else { + r -= 1 + rightMax = max(rightMax, height[r]) + res += rightMax - height[r] + } + } + return res + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/two-integer-sum-ii.md b/articles/two-integer-sum-ii.md index 8aea74e9d..f63e71a60 100644 --- a/articles/two-integer-sum-ii.md +++ b/articles/two-integer-sum-ii.md @@ -106,6 +106,21 @@ class Solution { } ``` +```swift +class Solution { + func twoSum(_ numbers: [Int], _ target: Int) -> [Int] { + for i in 0.. [Int] { + for i in 0.. [Int] { + var mp = [Int: Int]() + + for i in 0.. [Int] { + var l = 0, r = numbers.count - 1 + + while l < r { + let curSum = numbers[l] + numbers[r] + + if curSum > target { + r -= 1 + } else if curSum < target { + l += 1 + } else { + return [l + 1, r + 1] + } + } + return [] + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/two-integer-sum.md b/articles/two-integer-sum.md index 5b08e079d..9fba0afdc 100644 --- a/articles/two-integer-sum.md +++ b/articles/two-integer-sum.md @@ -106,6 +106,21 @@ class Solution { } ``` +```swift +class Solution { + func twoSum(_ nums: [Int], _ target: Int) -> [Int] { + for i in 0.. [Int] { + var A = [(Int, Int)]() + for (i, num) in nums.enumerated() { + A.append((num, i)) + } + + A.sort { $0.0 < $1.0 } + var i = 0 + var j = nums.count - 1 + + while i < j { + let cur = A[i].0 + A[j].0 + if cur == target { + return [min(A[i].1, A[j].1), + max(A[i].1, A[j].1)] + } else if cur < target { + i += 1 + } else { + j -= 1 + } + } + return [] + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -469,6 +512,27 @@ class Solution { } ``` +```swift +class Solution { + func twoSum(_ nums: [Int], _ target: Int) -> [Int] { + var indices = [Int: Int]() // val -> index + + for (i, n) in nums.enumerated() { + indices[n] = i + } + + for (i, n) in nums.enumerated() { + let diff = target - n + if let j = indices[diff], j != i { + return [i, j] + } + } + + return [] + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -607,6 +671,24 @@ class Solution { } ``` +```swift +class Solution { + func twoSum(_ nums: [Int], _ target: Int) -> [Int] { + var prevMap = [Int: Int]() // val -> index + + for (i, n) in nums.enumerated() { + let diff = target - n + if let index = prevMap[diff] { + return [index, i] + } + prevMap[n] = i + } + + return [] + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/valid-binary-search-tree.md b/articles/valid-binary-search-tree.md index fd885ddfc..3b076e3c7 100644 --- a/articles/valid-binary-search-tree.md +++ b/articles/valid-binary-search-tree.md @@ -316,6 +316,44 @@ class Solution { } ``` +```swift +/** + * Definition for a binary tree node. + * public class TreeNode { + * public var val: Int + * public var left: TreeNode? + * public var right: TreeNode? + * public init() { self.val = 0; self.left = nil; self.right = nil; } + * public init(_ val: Int) { self.val = val; self.left = nil; self.right = nil; } + * public init(_ val: Int, _ left: TreeNode?, _ right: TreeNode?) { + * self.val = val + * self.left = left + * self.right = right + * } + * } + */ +class Solution { + func isValidBST(_ root: TreeNode?) -> Bool { + guard let root = root else { return true } + if !isValid(root.left, root.val, { $0 < $1 }) || + !isValid(root.right, root.val, { $0 > $1 }) { + return false + } + + return isValidBST(root.left) && isValidBST(root.right) + } + + private func isValid(_ root: TreeNode?, _ limit: Int, _ check: (Int, Int) -> Bool) -> Bool { + guard let root = root else { return true } + if !check(root.val, limit) { + return false + } + + return isValid(root.left, limit, check) && isValid(root.right, limit, check) + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -550,6 +588,37 @@ class Solution { } ``` +```swift +/** + * Definition for a binary tree node. + * public class TreeNode { + * public var val: Int + * public var left: TreeNode? + * public var right: TreeNode? + * public init() { self.val = 0; self.left = nil; self.right = nil; } + * public init(_ val: Int) { self.val = val; self.left = nil; self.right = nil; } + * public init(_ val: Int, _ left: TreeNode?, _ right: TreeNode?) { + * self.val = val + * self.left = left + * self.right = right + * } + * } + */ +class Solution { + func isValidBST(_ root: TreeNode?) -> Bool { + return valid(root, Int.min, Int.max) + } + + private func valid(_ node: TreeNode?, _ left: Int, _ right: Int) -> Bool { + guard let node = node else { return true } + if !(left < node.val && node.val < right) { + return false + } + return valid(node.left, left, node.val) && valid(node.right, node.val, right) + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -858,6 +927,48 @@ class Solution { } ``` +```swift +/** + * Definition for a binary tree node. + * public class TreeNode { + * public var val: Int + * public var left: TreeNode? + * public var right: TreeNode? + * public init() { self.val = 0; self.left = nil; self.right = nil; } + * public init(_ val: Int) { self.val = val; self.left = nil; self.right = nil; } + * public init(_ val: Int, _ left: TreeNode?, _ right: TreeNode?) { + * self.val = val + * self.left = left + * self.right = right + * } + * } + */ +class Solution { + func isValidBST(_ root: TreeNode?) -> Bool { + guard let root = root else { return true } + + var q = Deque<(TreeNode, Int, Int)>() + q.append((root, Int.min, Int.max)) + + while !q.isEmpty { + let (node, left, right) = q.popFirst()! + + if !(left < node.val && node.val < right) { + return false + } + + if let leftNode = node.left { + q.append((leftNode, left, node.val)) + } + if let rightNode = node.right { + q.append((rightNode, node.val, right)) + } + } + return true + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/valid-parenthesis-string.md b/articles/valid-parenthesis-string.md index 84c0422d8..c412ad605 100644 --- a/articles/valid-parenthesis-string.md +++ b/articles/valid-parenthesis-string.md @@ -148,7 +148,52 @@ func checkValidString(s string) bool { ``` ```kotlin +class Solution { + fun checkValidString(s: String): Boolean { + + fun dfs(i: Int, open: Int): Boolean { + if (open < 0) { + return false + } + if (i == s.length) { + return open == 0 + } + + return when (s[i]) { + '(' -> dfs(i + 1, open + 1) + ')' -> dfs(i + 1, open - 1) + else -> dfs(i + 1, open) || dfs(i + 1, open + 1) || dfs(i + 1, open - 1) + } + } + + return dfs(0, 0) + } +} +``` + +```swift +class Solution { + func checkValidString(_ s: String) -> Bool { + let chars = Array(s) + + func dfs(_ i: Int, _ open: Int) -> Bool { + if open < 0 { return false } + if i == chars.count { return open == 0 } + + if chars[i] == "(" { + return dfs(i + 1, open + 1) + } else if chars[i] == ")" { + return dfs(i + 1, open - 1) + } else { + return dfs(i + 1, open) || + dfs(i + 1, open + 1) || + dfs(i + 1, open - 1) + } + } + return dfs(0, 0) + } +} ``` ::tabs-end @@ -397,6 +442,47 @@ class Solution { } ``` +```swift +class Solution { + func checkValidString(_ s: String) -> Bool { + let n = s.count + let sArr = Array(s) + var memo = Array( + repeating: Array(repeating: nil, count: n + 1), + count: n + 1 + ) + + func dfs(_ i: Int, _ open: Int) -> Bool { + if open < 0 { + return false + } + if i == n { + return open == 0 + } + if let memoized = memo[i][open] { + return memoized + } + + let result: Bool + if sArr[i] == "(" { + result = dfs(i + 1, open + 1) + } else if sArr[i] == ")" { + result = dfs(i + 1, open - 1) + } else { + result = dfs(i + 1, open) || + dfs(i + 1, open + 1) || + dfs(i + 1, open - 1) + } + + memo[i][open] = result + return result + } + + return dfs(0, 0) + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -620,6 +706,40 @@ class Solution { } ``` +```swift +class Solution { + func checkValidString(_ s: String) -> Bool { + let n = s.count + var dp = Array(repeating: Array(repeating: false, count: n + 1), count: n + 1) + dp[n][0] = true + + let chars = Array(s) + + for i in stride(from: n - 1, through: 0, by: -1) { + for open in 0.. 0 { + res = res || dp[i + 1][open - 1] + } + res = res || dp[i + 1][open] + } else { + if chars[i] == "(" { + res = res || dp[i + 1][open + 1] + } else if open > 0 { + res = res || dp[i + 1][open - 1] + } + } + dp[i][open] = res + } + } + + return dp[0][0] + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -815,6 +935,34 @@ class Solution { } ``` +```swift +class Solution { + func checkValidString(_ s: String) -> Bool { + let n = s.count + var dp = Array(repeating: false, count: n + 1) + dp[0] = true + + let chars = Array(s) + + for i in stride(from: n - 1, through: 0, by: -1) { + var new_dp = Array(repeating: false, count: n + 1) + for open in 0.. 0 && dp[open - 1]) || dp[open] + } else if chars[i] == "(" { + new_dp[open] = dp[open + 1] + } else if open > 0 { + new_dp[open] = dp[open - 1] + } + } + dp = new_dp + } + + return dp[0] + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -1034,6 +1182,44 @@ class Solution { } ``` +```swift +class Solution { + func checkValidString(_ s: String) -> Bool { + var left = [Int]() + var star = [Int]() + + let chars = Array(s) + + for (i, ch) in chars.enumerated() { + if ch == "(" { + left.append(i) + } else if ch == "*" { + star.append(i) + } else { + if left.isEmpty && star.isEmpty { + return false + } + if !left.isEmpty { + left.popLast() + } else { + star.popLast() + } + } + } + + while !left.isEmpty && !star.isEmpty { + if left.last! > star.last! { + return false + } + left.popLast() + star.popLast() + } + + return left.isEmpty + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -1236,6 +1422,35 @@ class Solution { } ``` +```swift +class Solution { + func checkValidString(_ s: String) -> Bool { + var leftMin = 0 + var leftMax = 0 + + for c in s { + if c == "(" { + leftMin += 1 + leftMax += 1 + } else if c == ")" { + leftMin -= 1 + leftMax -= 1 + } else { + leftMin -= 1 + leftMax += 1 + } + if leftMax < 0 { + return false + } + if leftMin < 0 { + leftMin = 0 + } + } + return leftMin == 0 + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/valid-sudoku.md b/articles/valid-sudoku.md index 14c8dc735..74fb5deda 100644 --- a/articles/valid-sudoku.md +++ b/articles/valid-sudoku.md @@ -284,6 +284,45 @@ class Solution { } ``` +```swift +class Solution { + func isValidSudoku(_ board: [[Character]]) -> Bool { + for row in 0..<9 { + var seen = Set() + for i in 0..<9 { + if board[row][i] == "." { continue } + if seen.contains(board[row][i]) { return false } + seen.insert(board[row][i]) + } + } + + for col in 0..<9 { + var seen = Set() + for i in 0..<9 { + if board[i][col] == "." { continue } + if seen.contains(board[i][col]) { return false } + seen.insert(board[i][col]) + } + } + + for square in 0..<9 { + var seen = Set() + for i in 0..<3 { + for j in 0..<3 { + let row = (square / 3) * 3 + i + let col = (square % 3) * 3 + j + if board[row][col] == "." { continue } + if seen.contains(board[row][col]) { return false } + seen.insert(board[row][col]) + } + } + } + + return true + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -510,6 +549,36 @@ class Solution { } ``` +```swift +class Solution { + func isValidSudoku(_ board: [[Character]]) -> Bool { + var cols = [Int: Set]() + var rows = [Int: Set]() + var squares = [String: Set]() + + for r in 0..<9 { + for c in 0..<9 { + if board[r][c] == "." { continue } + + let squareKey = "\(r / 3),\(c / 3)" + + if rows[r]?.contains(board[r][c]) == true || + cols[c]?.contains(board[r][c]) == true || + squares[squareKey]?.contains(board[r][c]) == true { + return false + } + + rows[r, default: []].insert(board[r][c]) + cols[c, default: []].insert(board[r][c]) + squares[squareKey, default: []].insert(board[r][c]) + } + } + + return true + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -725,6 +794,35 @@ class Solution { } ``` +```swift +class Solution { + func isValidSudoku(_ board: [[Character]]) -> Bool { + var rows = [Int](repeating: 0, count: 9) + var cols = [Int](repeating: 0, count: 9) + var squares = [Int](repeating: 0, count: 9) + + for r in 0..<9 { + for c in 0..<9 { + if board[r][c] == "." { continue } + + let val = Int(board[r][c].asciiValue! - Character("0").asciiValue!) + let bitmask = 1 << (val - 1) + + if (rows[r] & bitmask) != 0 { return false } + if (cols[c] & bitmask) != 0 { return false } + if (squares[(r / 3) * 3 + (c / 3)] & bitmask) != 0 { return false } + + rows[r] |= bitmask + cols[c] |= bitmask + squares[(r / 3) * 3 + (c / 3)] |= bitmask + } + } + + return true + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/valid-tree.md b/articles/valid-tree.md index a343b676e..7266d9836 100644 --- a/articles/valid-tree.md +++ b/articles/valid-tree.md @@ -266,6 +266,44 @@ class Solution { } ``` +```swift +class Solution { + func validTree(_ n: Int, _ edges: [[Int]]) -> Bool { + if edges.count > (n - 1) { + return false + } + + var adj = Array(repeating: [Int](), count: n) + for edge in edges { + let u = edge[0] + let v = edge[1] + adj[u].append(v) + adj[v].append(u) + } + + var visited = Set() + + func dfs(_ node: Int, _ parent: Int) -> Bool { + if visited.contains(node) { + return false + } + visited.insert(node) + for nei in adj[node] { + if nei == parent { + continue + } + if !dfs(nei, node) { + return false + } + } + return true + } + + return dfs(0, -1) && visited.count == n + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -537,6 +575,45 @@ class Solution { } ``` +```swift +class Solution { + func validTree(_ n: Int, _ edges: [[Int]]) -> Bool { + if edges.count > n - 1 { + return false + } + + var adj = [[Int]](repeating: [], count: n) + for edge in edges { + let u = edge[0] + let v = edge[1] + adj[u].append(v) + adj[v].append(u) + } + + var visit = Set() + var q = Deque<(Int, Int)>() // (current node, parent node) + q.append((0, -1)) + visit.insert(0) + + while !q.isEmpty { + let (node, parent) = q.removeFirst() + for nei in adj[node] { + if nei == parent { + continue + } + if visit.contains(nei) { + return false + } + visit.insert(nei) + q.append((nei, node)) + } + } + + return visit.count == n + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -593,7 +670,7 @@ class Solution: ``` ```java -public class DSU { +class DSU { int[] Parent, Size; int comps; @@ -932,6 +1009,65 @@ class Solution { } ``` +```swift +class DSU { + var comps: Int + var parent: [Int] + var size: [Int] + + init(_ n: Int) { + comps = n + parent = Array(0.. Int { + if parent[node] != node { + parent[node] = find(parent[node]) + } + return parent[node] + } + + func union(_ u: Int, _ v: Int) -> Bool { + let pu = find(u) + let pv = find(v) + if pu == pv { + return false + } + comps -= 1 + if size[pu] < size[pv] { + parent[pu] = pv + size[pv] += size[pu] + } else { + parent[pv] = pu + size[pu] += size[pv] + } + return true + } + + func components() -> Int { + return comps + } +} + +class Solution { + func validTree(_ n: Int, _ edges: [[Int]]) -> Bool { + if edges.count > n - 1 { + return false + } + + let dsu = DSU(n) + for edge in edges { + let u = edge[0], v = edge[1] + if !dsu.union(u, v) { + return false + } + } + return dsu.components() == 1 + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/validate-parentheses.md b/articles/validate-parentheses.md index fc97eac24..a7e649a78 100644 --- a/articles/validate-parentheses.md +++ b/articles/validate-parentheses.md @@ -105,6 +105,20 @@ class Solution { } ``` +```swift +class Solution { + func isValid(_ s: String) -> Bool { + var str = s + while str.contains("()") || str.contains("{}") || str.contains("[]") { + str = str.replacingOccurrences(of: "()", with: "") + str = str.replacingOccurrences(of: "{}", with: "") + str = str.replacingOccurrences(of: "[]", with: "") + } + return str.isEmpty + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -291,6 +305,29 @@ class Solution { } ``` +```swift +class Solution { + func isValid(_ s: String) -> Bool { + var stack = [Character]() + let closeToOpen: [Character: Character] = [")": "(", "]": "[", "}": "{"] + + for c in s { + if let open = closeToOpen[c] { + if !stack.isEmpty && stack.last! == open { + stack.popLast() + } else { + return false + } + } else { + stack.append(c) + } + } + + return stack.isEmpty + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/word-break.md b/articles/word-break.md index 908708670..acc8366d3 100644 --- a/articles/word-break.md +++ b/articles/word-break.md @@ -176,6 +176,32 @@ class Solution { } ``` +```swift +class Solution { + func wordBreak(_ s: String, _ wordDict: [String]) -> Bool { + let chars = Array(s) + + func dfs(_ i: Int) -> Bool { + if i == chars.count { + return true + } + + for w in wordDict { + if i + w.count <= chars.count, + String(chars[i.. Bool { + let wordSet = Set(wordDict) + let chars = Array(s) + + func dfs(_ i: Int) -> Bool { + if i == chars.count { + return true + } + + for j in i.. Bool { + var memo = [Int: Bool]() + memo[s.count] = true + let chars = Array(s) + + func dfs(_ i: Int) -> Bool { + if let cached = memo[i] { + return cached + } + + for w in wordDict { + if i + w.count <= chars.count, + String(chars[i.. Bool { + let wordSet = Set(wordDict) + var maxLen = 0 + for w in wordDict { + maxLen = max(maxLen, w.count) + } + + var memo = [Int: Bool]() + let chars = Array(s) + + func dfs(_ i: Int) -> Bool { + if let cached = memo[i] { + return cached + } + if i == chars.count { + return true + } + for j in i.. Bool { + var dp = Array(repeating: false, count: s.count + 1) + dp[s.count] = true + let chars = Array(s) + + for i in stride(from: s.count - 1, through: 0, by: -1) { + for w in wordDict { + if i + w.count <= chars.count, String(chars[i.. children; boolean isWord; @@ -1096,7 +1237,7 @@ public class TrieNode { } } -public class Trie { +class Trie { TrieNode root; public Trie() { @@ -1497,6 +1638,70 @@ class Solution { } ``` +```swift +class TrieNode { + var children = [Character: TrieNode]() + var isWord = false +} + +class Trie { + private let root = TrieNode() + + func insert(_ word: String) { + var node = root + for char in word { + if node.children[char] == nil { + node.children[char] = TrieNode() + } + node = node.children[char]! + } + node.isWord = true + } + + func search(_ s: [Character], _ i: Int, _ j: Int) -> Bool { + var node = root + for idx in i...j { + if node.children[s[idx]] == nil { + return false + } + node = node.children[s[idx]]! + } + return node.isWord + } +} + +class Solution { + func wordBreak(_ s: String, _ wordDict: [String]) -> Bool { + let trie = Trie() + for word in wordDict { + trie.insert(word) + } + + var dp = Array(repeating: false, count: s.count + 1) + dp[s.count] = true + let chars = Array(s) + + var maxLen = 0 + for word in wordDict { + maxLen = max(maxLen, word.count) + } + + for i in stride(from: s.count, through: 0, by: -1) { + for j in i.. Int { + if !wordList.contains(endWord) || beginWord == endWord { + return 0 + } + + let n = wordList.count + let m = wordList[0].count + var adj = [[Int]](repeating: [], count: n) + var mp = [String: Int]() + + for i in 0..() + var res = 1 + var visit = Set() + let beginChars = Array(beginWord) + + for i in 0.. Int { + if !wordList.contains(endWord) || beginWord == endWord { + return 0 + } + + var words = Set(wordList) + var queue = Deque() + queue.append(beginWord) + var res = 0 + + while !queue.isEmpty { + res += 1 + for _ in 0.. Int { + if !wordList.contains(endWord) { + return 0 + } + + var nei = [String: [String]]() + var wordList = wordList + wordList.append(beginWord) + + for word in wordList { + let wordArray = Array(word) + for j in 0.. = [beginWord] + var queue = Deque() + queue.append(beginWord) + var res = 1 + + while !queue.isEmpty { + for _ in 0.. Int { + if !wordList.contains(endWord) || beginWord == endWord { + return 0 + } + + let m = wordList[0].count + var wordSet = Set(wordList) + var qb = Deque([beginWord]) + var qe = Deque([endWord]) + var fromBegin = [beginWord: 1] + var fromEnd = [endWord: 1] + + while !qb.isEmpty && !qe.isEmpty { + if qb.count > qe.count { + swap(&qb, &qe) + swap(&fromBegin, &fromEnd) + } + + for _ in 0.. Date: Tue, 15 Apr 2025 10:02:05 +0530 Subject: [PATCH 39/45] Sri Hari: Batch-6/Neetcode-All/Added-articles (#4042) * Batch-6/Neetcode-ALL/Added-articles * Batch-6/Neetcode-ALL/Added-articles * Batch-6/Neetcode-ALL/Added-articles * Batch-6/Neetcode-ALL/Added-articles * Batch-6/Neetcode-ALL/Added-articles * Batch-6/Neetcode-ALL/Added-articles * Batch-6/Neetcode-ALL/Added-articles * Batch-6/Neetcode-ALL/Added-articles * Batch-6/Neetcode-ALL/Added-articles --- ...haracters-to-string-to-make-subsequence.md | 210 +++ articles/average-waiting-time.md | 169 +++ .../binary-tree-vertical-order-traversal.md | 837 ++++++++++++ articles/buildings-with-an-ocean-view.md | 339 +++++ .../check-if-array-is-sorted-and-rotated.md | 306 +++++ articles/circular-sentence.md | 154 +++ articles/count-number-of-bad-pairs.md | 157 +++ .../count-the-number-of-consistent-strings.md | 415 ++++++ articles/count-vowel-strings-in-ranges.md | 336 +++++ articles/divide-array-into-equal-pairs.md | 290 +++++ articles/find-common-characters.md | 122 ++ articles/find-missing-and-repeated-values.md | 492 +++++++ articles/height-checker.md | 206 +++ articles/kth-distinct-string-in-an-array.md | 350 +++++ articles/longest-palindrome.md | 419 ++++++ ...reasing-or-strictly-decreasing-subarray.md | 454 +++++++ articles/make-sum-divisible-by-p.md | 235 ++++ articles/maximum-ascending-subarray-sum.md | 173 +++ articles/minimum-index-of-a-valid-split.md | 385 ++++++ ...perations-to-move-all-balls-to-each-box.md | 325 +++++ ...inimum-remove-to-make-valid-parentheses.md | 124 ++ articles/number-of-senior-citizens.md | 149 +++ articles/number-of-sub-arrays-with-odd-sum.md | 521 ++++++++ articles/number-of-ways-to-split-array.md | 320 +++++ articles/relative-sort-array.md | 551 ++++++++ .../remove-duplicates-from-sorted-list.md | 2 +- articles/score-of-a-string.md | 60 + articles/shifting-letters-ii.md | 481 +++++++ .../sort-array-by-increasing-frequency.md | 79 ++ articles/sort-the-jumbled-numbers.md | 244 ++++ articles/sort-the-people.md | 256 ++++ articles/special-array-i.md | 129 ++ articles/string-matching-in-an-array.md | 1157 +++++++++++++++++ articles/subarray-sums-divisible-by-k.md | 280 ++++ articles/uncommon-words-from-two-sentences.md | 183 +++ articles/valid-palindrome-ii.md | 81 ++ articles/valid-word-abbreviation.md | 163 +++ articles/word-subsets.md | 293 +++++ 38 files changed, 11446 insertions(+), 1 deletion(-) create mode 100644 articles/append-characters-to-string-to-make-subsequence.md create mode 100644 articles/average-waiting-time.md create mode 100644 articles/binary-tree-vertical-order-traversal.md create mode 100644 articles/buildings-with-an-ocean-view.md create mode 100644 articles/check-if-array-is-sorted-and-rotated.md create mode 100644 articles/circular-sentence.md create mode 100644 articles/count-number-of-bad-pairs.md create mode 100644 articles/count-the-number-of-consistent-strings.md create mode 100644 articles/count-vowel-strings-in-ranges.md create mode 100644 articles/divide-array-into-equal-pairs.md create mode 100644 articles/find-common-characters.md create mode 100644 articles/find-missing-and-repeated-values.md create mode 100644 articles/height-checker.md create mode 100644 articles/kth-distinct-string-in-an-array.md create mode 100644 articles/longest-palindrome.md create mode 100644 articles/longest-strictly-increasing-or-strictly-decreasing-subarray.md create mode 100644 articles/make-sum-divisible-by-p.md create mode 100644 articles/maximum-ascending-subarray-sum.md create mode 100644 articles/minimum-index-of-a-valid-split.md create mode 100644 articles/minimum-number-of-operations-to-move-all-balls-to-each-box.md create mode 100644 articles/number-of-senior-citizens.md create mode 100644 articles/number-of-sub-arrays-with-odd-sum.md create mode 100644 articles/number-of-ways-to-split-array.md create mode 100644 articles/relative-sort-array.md create mode 100644 articles/score-of-a-string.md create mode 100644 articles/shifting-letters-ii.md create mode 100644 articles/sort-array-by-increasing-frequency.md create mode 100644 articles/sort-the-jumbled-numbers.md create mode 100644 articles/sort-the-people.md create mode 100644 articles/special-array-i.md create mode 100644 articles/string-matching-in-an-array.md create mode 100644 articles/subarray-sums-divisible-by-k.md create mode 100644 articles/uncommon-words-from-two-sentences.md create mode 100644 articles/valid-word-abbreviation.md create mode 100644 articles/word-subsets.md diff --git a/articles/append-characters-to-string-to-make-subsequence.md b/articles/append-characters-to-string-to-make-subsequence.md new file mode 100644 index 000000000..de5f09613 --- /dev/null +++ b/articles/append-characters-to-string-to-make-subsequence.md @@ -0,0 +1,210 @@ +## 1. Two Pointers + +::tabs-start + +```python +class Solution: + def appendCharacters(self, s: str, t: str) -> int: + i, j = 0, 0 + + while i < len(s) and j < len(t): + if s[i] == t[j]: + i += 1 + j += 1 + else: + i += 1 + return len(t) - j +``` + +```java +public class Solution { + public int appendCharacters(String s, String t) { + int i = 0, j = 0; + + while (i < s.length() && j < t.length()) { + if (s.charAt(i) == t.charAt(j)) { + i++; + j++; + } else { + i++; + } + } + return t.length() - j; + } +} +``` + +```cpp +class Solution { +public: + int appendCharacters(string s, string t) { + int i = 0, j = 0; + + while (i < s.length() && j < t.length()) { + if (s[i] == t[j]) { + i++; + j++; + } else { + i++; + } + } + return t.length() - j; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @param {string} t + * @return {number} + */ + appendCharacters(s, t) { + let i = 0, j = 0; + + while (i < s.length && j < t.length) { + if (s[i] === t[j]) { + i++; + j++; + } else { + i++; + } + } + return t.length - j; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n + m)$ +* Space complexity: $O(1)$ + +> Where $n$ and $m$ are the lengths of the strings $s$ and $t$, respectively. + +--- + +## 2. Index Jumping + +::tabs-start + +```python +class Solution: + def appendCharacters(self, s: str, t: str) -> int: + n, m = len(s), len(t) + store = [[n + 1] * 26 for _ in range(n)] + store[n - 1][ord(s[n - 1]) - ord('a')] = n - 1 + + for i in range(n - 2, -1, -1): + store[i] = store[i + 1][:] + store[i][ord(s[i]) - ord('a')] = i + + i, j = 0, 0 + while i < n and j < m: + if store[i][ord(t[j]) - ord('a')] == n + 1: + break + + i = store[i][ord(t[j]) - ord('a')] + 1 + j += 1 + + return m - j +``` + +```java +public class Solution { + public int appendCharacters(String s, String t) { + int n = s.length(), m = t.length(); + int[][] store = new int[n][26]; + for (int[] row : store) { + Arrays.fill(row, n + 1); + } + store[n - 1][s.charAt(n - 1) - 'a'] = n - 1; + + for (int i = n - 2; i >= 0; i--) { + store[i] = store[i + 1].clone(); + store[i][s.charAt(i) - 'a'] = i; + } + + int i = 0, j = 0; + while (i < n && j < m) { + if (store[i][t.charAt(j) - 'a'] == n + 1) { + break; + } + i = store[i][t.charAt(j) - 'a'] + 1; + j++; + } + + return m - j; + } +} +``` + +```cpp +class Solution { +public: + int appendCharacters(string s, string t) { + int n = s.length(), m = t.length(); + vector> store(n, vector(26, n + 1)); + store[n - 1][s[n - 1] - 'a'] = n - 1; + + for (int i = n - 2; i >= 0; i--) { + store[i] = store[i + 1]; + store[i][s[i] - 'a'] = i; + } + + int i = 0, j = 0; + while (i < n && j < m) { + if (store[i][t[j] - 'a'] == n + 1) { + break; + } + i = store[i][t[j] - 'a'] + 1; + j++; + } + + return m - j; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @param {string} t + * @return {number} + */ + appendCharacters(s, t) { + const n = s.length, m = t.length; + const store = Array.from({ length: n }, () => Array(26).fill(n + 1)); + store[n - 1][s.charCodeAt(n - 1) - 97] = n - 1; + + for (let i = n - 2; i >= 0; i--) { + store[i] = store[i + 1].slice(); + store[i][s.charCodeAt(i) - 97] = i; + } + + let i = 0, j = 0; + while (i < n && j < m) { + if (store[i][t.charCodeAt(j) - 97] === n + 1) { + break; + } + i = store[i][t.charCodeAt(j) - 97] + 1; + j++; + } + + return m - j; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n + m)$ +* Space complexity: $O(n)$ + +> Where $n$ and $m$ are the lengths of the strings $s$ and $t$, respectively. \ No newline at end of file diff --git a/articles/average-waiting-time.md b/articles/average-waiting-time.md new file mode 100644 index 000000000..0d6c7c818 --- /dev/null +++ b/articles/average-waiting-time.md @@ -0,0 +1,169 @@ +## 1. Simulation - I + +::tabs-start + +```python +class Solution: + def averageWaitingTime(self, customers: List[List[int]]) -> float: + t = 0 + total = 0 + + for arrival, order in customers: + if t > arrival: + total += t - arrival + else: + t = arrival + total += order + t += order + + return total / len(customers) +``` + +```java +public class Solution { + public double averageWaitingTime(int[][] customers) { + long t = 0, total = 0; + + for (int[] c : customers) { + int arrival = c[0], order = c[1]; + if (t > arrival) { + total += t - arrival; + } else { + t = arrival; + } + total += order; + t += order; + } + + return (double) total / customers.length; + } +} +``` + +```cpp +class Solution { +public: + double averageWaitingTime(vector>& customers) { + long long t = 0, total = 0; + + for (auto& c : customers) { + int arrival = c[0], order = c[1]; + if (t > arrival) { + total += t - arrival; + } else { + t = arrival; + } + total += order; + t += order; + } + + return (double) total / customers.size(); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} customers + * @return {number} + */ + averageWaitingTime(customers) { + let t = 0, total = 0; + + for (let [arrival, order] of customers) { + if (t > arrival) { + total += t - arrival; + } else { + t = arrival; + } + total += order; + t += order; + } + + return total / customers.length; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ + +--- + +## 2. Simulation - II + +::tabs-start + +```python +class Solution: + def averageWaitingTime(self, customers: List[List[int]]) -> float: + t = total = 0 + for arrival, order in customers: + t = max(t, arrival) + order + total += t - arrival + return total / len(customers) +``` + +```java +public class Solution { + public double averageWaitingTime(int[][] customers) { + long t = 0, total = 0; + + for (int[] c : customers) { + int arrival = c[0], order = c[1]; + t = Math.max(t, arrival) + order; + total += t - arrival; + } + + return (double) total / customers.length; + } +} +``` + +```cpp +class Solution { +public: + double averageWaitingTime(vector>& customers) { + long long t = 0, total = 0; + + for (auto& c : customers) { + int arrival = c[0], order = c[1]; + t = max(t, (long long)arrival) + order; + total += t - arrival; + } + + return (double) total / customers.size(); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} customers + * @return {number} + */ + averageWaitingTime(customers) { + let t = 0, total = 0; + + for (let [arrival, order] of customers) { + t = Math.max(t, arrival) + order; + total += t - arrival; + } + + return total / customers.length; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/binary-tree-vertical-order-traversal.md b/articles/binary-tree-vertical-order-traversal.md new file mode 100644 index 000000000..506fdad2c --- /dev/null +++ b/articles/binary-tree-vertical-order-traversal.md @@ -0,0 +1,837 @@ +## 1. Breadth First Search + Sorting + +::tabs-start + +```python +# 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 +class Solution: + def verticalOrder(self, root: Optional[TreeNode]) -> List[List[int]]: + cols = defaultdict(list) + que = deque([(root, 0)]) + + while que: + node, pos = que.popleft() + if node: + cols[pos].append(node.val) + que.append((node.left, pos - 1)) + que.append((node.right, pos + 1)) + + return [cols[x] for x in sorted(cols)] +``` + +```java +/** + * 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; + * } + * } + */ +public class Solution { + public List> verticalOrder(TreeNode root) { + if (root == null) return new ArrayList<>(); + + Map> cols = new TreeMap<>(); + Queue> queue = new LinkedList<>(); + queue.offer(new Pair<>(root, 0)); + + while (!queue.isEmpty()) { + Pair p = queue.poll(); + TreeNode node = p.getKey(); + int pos = p.getValue(); + + cols.computeIfAbsent(pos, k -> new ArrayList<>()).add(node.val); + + if (node.left != null) queue.offer(new Pair<>(node.left, pos - 1)); + if (node.right != null) queue.offer(new Pair<>(node.right, pos + 1)); + } + + return new ArrayList<>(cols.values()); + } +} +``` + +```cpp +/** + * 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: + vector> verticalOrder(TreeNode* root) { + if (!root) return {}; + map> cols; + queue> q; + q.push({root, 0}); + + while (!q.empty()) { + auto [node, pos] = q.front(); q.pop(); + cols[pos].push_back(node->val); + if (node->left) q.push({node->left, pos - 1}); + if (node->right) q.push({node->right, pos + 1}); + } + + vector> res; + for (auto& [_, vec] : cols) + res.push_back(vec); + return res; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {number[][]} + */ + verticalOrder(root) { + if (!root) return []; + + const cols = new Map(); + const queue = new Queue([[root, 0]]); + + while (!queue.isEmpty()) { + const [node, pos] = queue.pop(); + if (!cols.has(pos)) cols.set(pos, []); + cols.get(pos).push(node.val); + + if (node.left) queue.push([node.left, pos - 1]); + if (node.right) queue.push([node.right, pos + 1]); + } + + const sortedKeys = Array.from(cols.keys()).sort((a, b) => a - b); + return sortedKeys.map(k => cols.get(k)); + } +} +``` + +```csharp +/** + * Definition for a binary tree node. + * public class TreeNode { + * public int val; + * public TreeNode left; + * public TreeNode right; + * public TreeNode(int val=0, TreeNode left=null, TreeNode right=null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public IList> VerticalOrder(TreeNode root) { + if (root == null) return new List>(); + + var cols = new SortedDictionary>(); + var queue = new Queue<(TreeNode node, int pos)>(); + queue.Enqueue((root, 0)); + + while (queue.Count > 0) { + var (node, pos) = queue.Dequeue(); + + if (!cols.ContainsKey(pos)) + cols[pos] = new List(); + cols[pos].Add(node.val); + + if (node.left != null) queue.Enqueue((node.left, pos - 1)); + if (node.right != null) queue.Enqueue((node.right, pos + 1)); + } + + return cols.Values.ToList>(); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Depth First Search + Sorting + +::tabs-start + +```python +# 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 +class Solution: + def verticalOrder(self, root: Optional[TreeNode]) -> List[List[int]]: + cols = defaultdict(list) + + def dfs(node, row, col): + if not node: + return + cols[col].append((row, node.val)) + dfs(node.left, row + 1, col - 1) + dfs(node.right, row + 1, col + 1) + + dfs(root, 0, 0) + + res = [] + for col in sorted(cols): + col_vals = sorted(cols[col], key=lambda x: x[0]) + res.append([val for _, val in col_vals]) + return res +``` + +```java +/** + * 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; + * } + * } + */ +public class Solution { + private Map> cols = new TreeMap<>(); + + public List> verticalOrder(TreeNode root) { + dfs(root, 0, 0); + List> res = new ArrayList<>(); + + for (List list : cols.values()) { + list.sort(Comparator.comparingInt(a -> a[0])); + List colVals = new ArrayList<>(); + for (int[] p : list) colVals.add(p[1]); + res.add(colVals); + } + + return res; + } + + private void dfs(TreeNode node, int row, int col) { + if (node == null) return; + cols.computeIfAbsent(col, k -> new ArrayList<>()).add(new int[]{row, node.val}); + dfs(node.left, row + 1, col - 1); + dfs(node.right, row + 1, col + 1); + } +} +``` + +```cpp +/** + * 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 { + map>> cols; + + void dfs(TreeNode* node, int row, int col) { + if (!node) return; + cols[col].push_back({row, node->val}); + dfs(node->left, row + 1, col - 1); + dfs(node->right, row + 1, col + 1); + } + +public: + vector> verticalOrder(TreeNode* root) { + dfs(root, 0, 0); + vector> res; + + for (auto& [col, vec] : cols) { + stable_sort(vec.begin(), vec.end(), + [](const pair& a, const pair& b) { + return a.first < b.first; // sort ONLY by row + }); + + vector colVals; + for (auto& [_, val] : vec) + colVals.push_back(val); + res.push_back(colVals); + } + + return res; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {number[][]} + */ + verticalOrder(root) { + const cols = new Map(); + + const dfs = (node, row, col) => { + if (!node) return; + if (!cols.has(col)) cols.set(col, []); + cols.get(col).push([row, node.val]); + dfs(node.left, row + 1, col - 1); + dfs(node.right, row + 1, col + 1); + }; + + dfs(root, 0, 0); + + const sortedCols = Array.from(cols.entries()).sort((a, b) => a[0] - b[0]); + return sortedCols.map(([_, vec]) => + vec.sort((a, b) => a[0] - b[0]).map(([_, val]) => val) + ); + } +} +``` + +```csharp +/** + * Definition for a binary tree node. + * public class TreeNode { + * public int val; + * public TreeNode left; + * public TreeNode right; + * public TreeNode(int val=0, TreeNode left=null, TreeNode right=null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + private SortedDictionary> cols = new(); + + public IList> VerticalOrder(TreeNode root) { + DFS(root, 0, 0); + + List> res = new(); + foreach (var entry in cols) { + var list = entry.Value.OrderBy(x => x.Item1).Select(x => x.Item2).ToList(); + res.Add(list); + } + + return res; + } + + private void DFS(TreeNode node, int row, int col) { + if (node == null) return; + if (!cols.ContainsKey(col)) cols[col] = new List<(int, int)>(); + cols[col].Add((row, node.val)); + DFS(node.left, row + 1, col - 1); + DFS(node.right, row + 1, col + 1); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n \log n)$ + +--- + +## 3. Breadth First Search (Optimal) + +::tabs-start + +```python +# 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 +class Solution: + def verticalOrder(self, root: Optional[TreeNode]) -> List[List[int]]: + if not root: + return [] + + cols = defaultdict(list) + queue = deque([(root, 0)]) + minCol = maxCol = 0 + + while queue: + node, col = queue.popleft() + cols[col].append(node.val) + minCol = min(minCol, col) + maxCol = max(maxCol, col) + + if node.left: + queue.append((node.left, col - 1)) + if node.right: + queue.append((node.right, col + 1)) + + return [cols[c] for c in range(minCol, maxCol + 1)] +``` + +```java +/** + * 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; + * } + * } + */ +public class Solution { + public List> verticalOrder(TreeNode root) { + if (root == null) return new ArrayList<>(); + + Map> cols = new HashMap<>(); + Queue> queue = new LinkedList<>(); + queue.offer(new Pair<>(root, 0)); + int minCol = 0, maxCol = 0; + + while (!queue.isEmpty()) { + Pair p = queue.poll(); + TreeNode node = p.getKey(); + int col = p.getValue(); + + cols.computeIfAbsent(col, x -> new ArrayList<>()).add(node.val); + minCol = Math.min(minCol, col); + maxCol = Math.max(maxCol, col); + + if (node.left != null) queue.offer(new Pair<>(node.left, col - 1)); + if (node.right != null) queue.offer(new Pair<>(node.right, col + 1)); + } + + List> res = new ArrayList<>(); + for (int c = minCol; c <= maxCol; c++) { + res.add(cols.get(c)); + } + return res; + } +} +``` + +```cpp +/** + * 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: + vector> verticalOrder(TreeNode* root) { + if (!root) return {}; + + unordered_map> cols; + queue> q; + q.push({root, 0}); + int minCol = 0, maxCol = 0; + + while (!q.empty()) { + auto [node, col] = q.front(); q.pop(); + cols[col].push_back(node->val); + minCol = min(minCol, col); + maxCol = max(maxCol, col); + + if (node->left) q.push({node->left, col - 1}); + if (node->right) q.push({node->right, col + 1}); + } + + vector> res; + for (int c = minCol; c <= maxCol; ++c) + res.push_back(cols[c]); + + return res; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {number[][]} + */ + verticalOrder(root) { + if (!root) return []; + + const cols = new Map(); + const queue = new Queue([[root, 0]]); + let minCol = 0, maxCol = 0; + + while (!queue.isEmpty()) { + const [node, col] = queue.pop(); + if (!cols.has(col)) cols.set(col, []); + cols.get(col).push(node.val); + minCol = Math.min(minCol, col); + maxCol = Math.max(maxCol, col); + + if (node.left) queue.push([node.left, col - 1]); + if (node.right) queue.push([node.right, col + 1]); + } + + const res = []; + for (let c = minCol; c <= maxCol; c++) { + res.push(cols.get(c)); + } + return res; + } +} +``` + +```csharp +/** + * Definition for a binary tree node. + * public class TreeNode { + * public int val; + * public TreeNode left; + * public TreeNode right; + * public TreeNode(int val=0, TreeNode left=null, TreeNode right=null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public IList> VerticalOrder(TreeNode root) { + if (root == null) return new List>(); + + Dictionary> cols = new(); + Queue<(TreeNode node, int col)> queue = new(); + queue.Enqueue((root, 0)); + int minCol = 0, maxCol = 0; + + while (queue.Count > 0) { + var (node, col) = queue.Dequeue(); + if (!cols.ContainsKey(col)) + cols[col] = new List(); + cols[col].Add(node.val); + minCol = Math.Min(minCol, col); + maxCol = Math.Max(maxCol, col); + + if (node.left != null) queue.Enqueue((node.left, col - 1)); + if (node.right != null) queue.Enqueue((node.right, col + 1)); + } + + var res = new List>(); + for (int c = minCol; c <= maxCol; c++) { + res.Add(cols[c]); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 4. Depth First Search (Optimal) + +::tabs-start + +```python +# 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 +class Solution: + def verticalOrder(self, root: Optional[TreeNode]) -> List[List[int]]: + if not root: + return [] + + cols = defaultdict(list) + minCol = maxCol = 0 + + def dfs(node, row, col): + nonlocal minCol, maxCol + if not node: + return + cols[col].append((row, node.val)) + minCol = min(minCol, col) + maxCol = max(maxCol, col) + dfs(node.left, row + 1, col - 1) + dfs(node.right, row + 1, col + 1) + + dfs(root, 0, 0) + + res = [] + for c in range(minCol, maxCol + 1): + # sort by row only + col_vals = sorted(cols[c], key=lambda x: x[0]) + res.append([val for _, val in col_vals]) + return res +``` + +```java +/** + * 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; + * } + * } + */ +public class Solution { + private Map> cols = new HashMap<>(); + private int minCol = 0, maxCol = 0; + + public List> verticalOrder(TreeNode root) { + if (root == null) return new ArrayList<>(); + dfs(root, 0, 0); + + List> res = new ArrayList<>(); + for (int c = minCol; c <= maxCol; c++) { + List list = cols.getOrDefault(c, new ArrayList<>()); + list.sort(Comparator.comparingInt(a -> a[0])); // sort by row + List colVals = new ArrayList<>(); + for (int[] p : list) colVals.add(p[1]); + res.add(colVals); + } + return res; + } + + private void dfs(TreeNode node, int row, int col) { + if (node == null) return; + cols.computeIfAbsent(col, k -> new ArrayList<>()).add(new int[]{row, node.val}); + minCol = Math.min(minCol, col); + maxCol = Math.max(maxCol, col); + dfs(node.left, row + 1, col - 1); + dfs(node.right, row + 1, col + 1); + } +} +``` + +```cpp +/** + * 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 { + unordered_map>> cols; + int minCol = 0, maxCol = 0; + + void dfs(TreeNode* node, int row, int col) { + if (!node) return; + cols[col].emplace_back(row, node->val); + minCol = min(minCol, col); + maxCol = max(maxCol, col); + dfs(node->left, row + 1, col - 1); + dfs(node->right, row + 1, col + 1); + } + +public: + vector> verticalOrder(TreeNode* root) { + if (!root) return {}; + dfs(root, 0, 0); + vector> res; + + for (int c = minCol; c <= maxCol; ++c) { + auto& vec = cols[c]; + stable_sort(vec.begin(), vec.end(), + [](const pair& a, const pair& b) { + return a.first < b.first; // sort by row only + }); + vector colVals; + for (auto& [_, val] : vec) + colVals.push_back(val); + res.push_back(colVals); + } + + return res; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {number[][]} + */ + verticalOrder(root) { + if (!root) return []; + + const cols = new Map(); + let minCol = 0, maxCol = 0; + + const dfs = (node, row, col) => { + if (!node) return; + if (!cols.has(col)) cols.set(col, []); + cols.get(col).push([row, node.val]); + minCol = Math.min(minCol, col); + maxCol = Math.max(maxCol, col); + dfs(node.left, row + 1, col - 1); + dfs(node.right, row + 1, col + 1); + }; + + dfs(root, 0, 0); + + const res = []; + for (let c = minCol; c <= maxCol; c++) { + let entries = cols.get(c) || []; + entries.sort((a, b) => a[0] - b[0]); // sort by row only + res.push(entries.map(([_, val]) => val)); + } + + return res; + } +} +``` + +```csharp +/** + * Definition for a binary tree node. + * public class TreeNode { + * public int val; + * public TreeNode left; + * public TreeNode right; + * public TreeNode(int val=0, TreeNode left=null, TreeNode right=null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + private Dictionary> cols = new(); + private int minCol = 0, maxCol = 0; + + public IList> VerticalOrder(TreeNode root) { + if (root == null) return new List>(); + DFS(root, 0, 0); + var res = new List>(); + + for (int c = minCol; c <= maxCol; c++) { + var list = cols.ContainsKey(c) ? cols[c] : new List<(int, int)>(); + list.Sort((a, b) => a.Item1.CompareTo(b.Item1)); // sort by row + res.Add(list.Select(p => p.Item2).ToList()); + } + + return res; + } + + private void DFS(TreeNode node, int row, int col) { + if (node == null) return; + if (!cols.ContainsKey(col)) cols[col] = new List<(int, int)>(); + cols[col].Add((row, node.val)); + minCol = Math.Min(minCol, col); + maxCol = Math.Max(maxCol, col); + DFS(node.left, row + 1, col - 1); + DFS(node.right, row + 1, col + 1); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(w * h \log h)$ +* Space complexity: $O(n)$ + +> Where $n$ is the number of nodes, $h$ is the height of the tree (i.e. maximum number of nodes in any vertical line of the tree), and $w$ is the width of the tree (i.e. maximum number of nodes in any of the levels of the tree). \ No newline at end of file diff --git a/articles/buildings-with-an-ocean-view.md b/articles/buildings-with-an-ocean-view.md new file mode 100644 index 000000000..9c418c11c --- /dev/null +++ b/articles/buildings-with-an-ocean-view.md @@ -0,0 +1,339 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def findBuildings(self, heights: List[int]) -> List[int]: + n = len(heights) + res = [] + + for i in range(n): + flag = True + for j in range(i + 1, n): + if heights[i] <= heights[j]: + flag = False + break + if flag: + res.append(i) + + return res +``` + +```java +public class Solution { + public int[] findBuildings(int[] heights) { + int n = heights.length; + List temp = new ArrayList<>(); + + for (int i = 0; i < n; i++) { + boolean flag = true; + for (int j = i + 1; j < n; j++) { + if (heights[i] <= heights[j]) { + flag = false; + break; + } + } + if (flag) temp.add(i); + } + + int[] res = new int[temp.size()]; + for (int i = 0; i < temp.size(); i++) { + res[i] = temp.get(i); + } + return res; + } +} +``` + +```cpp +class Solution { +public: + vector findBuildings(vector& heights) { + int n = heights.size(); + vector res; + + for (int i = 0; i < n; i++) { + bool flag = true; + for (int j = i + 1; j < n; j++) { + if (heights[i] <= heights[j]) { + flag = false; + break; + } + } + if (flag) res.push_back(i); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} heights + * @return {number[]} + */ + findBuildings(heights) { + const n = heights.length; + const res = []; + + for (let i = 0; i < n; i++) { + let flag = true; + for (let j = i + 1; j < n; j++) { + if (heights[i] <= heights[j]) { + flag = false; + break; + } + } + if (flag) res.push(i); + } + + return res; + } +} +``` + +```csharp +public class Solution { + public int[] FindBuildings(int[] heights) { + int n = heights.Length; + List temp = new List(); + + for (int i = 0; i < n; i++) { + bool flag = true; + for (int j = i + 1; j < n; j++) { + if (heights[i] <= heights[j]) { + flag = false; + break; + } + } + if (flag) temp.Add(i); + } + + return temp.ToArray(); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n)$ for the output array. + +--- + +## 2. Monotonic Stack + +::tabs-start + +```python +class Solution: + def findBuildings(self, heights: List[int]) -> List[int]: + stack = [] + + for i, h in enumerate(heights): + while stack and heights[stack[-1]] <= h: + stack.pop() + stack.append(i) + + return stack +``` + +```java +public class Solution { + public int[] findBuildings(int[] heights) { + int n = heights.length; + Stack stack = new Stack<>(); + + for (int i = 0; i < n; i++) { + while (!stack.isEmpty() && heights[stack.peek()] <= heights[i]) { + stack.pop(); + } + stack.push(i); + } + + int[] res = new int[stack.size()]; + for (int i = stack.size() - 1; i >= 0; i--) { + res[i] = stack.get(i); + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector findBuildings(vector& heights) { + vector stack; + + for (int i = 0; i < heights.size(); i++) { + while (!stack.empty() && heights[stack.back()] <= heights[i]) { + stack.pop_back(); + } + stack.push_back(i); + } + + return stack; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} heights + * @return {number[]} + */ + findBuildings(heights) { + const stack = []; + + for (let i = 0; i < heights.length; i++) { + while (stack.length && heights[stack[stack.length - 1]] <= heights[i]) { + stack.pop(); + } + stack.push(i); + } + + return stack; + } +} +``` + +```csharp +public class Solution { + public int[] FindBuildings(int[] heights) { + List stack = new List(); + + for (int i = 0; i < heights.Length; i++) { + while (stack.Count > 0 && heights[stack[stack.Count - 1]] <= heights[i]) { + stack.RemoveAt(stack.Count - 1); + } + stack.Add(i); + } + + return stack.ToArray(); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Greedy + +::tabs-start + +```python +class Solution: + def findBuildings(self, heights: List[int]) -> List[int]: + res = [len(heights) - 1] + for i in range(len(heights) - 2, -1, -1): + if heights[i] > heights[res[-1]]: + res.append(i) + res.reverse() + return res +``` + +```java +public class Solution { + public int[] findBuildings(int[] heights) { + List res = new ArrayList<>(); + int n = heights.length; + res.add(n - 1); + + for (int i = n - 2; i >= 0; i--) { + if (heights[i] > heights[res.get(res.size() - 1)]) { + res.add(i); + } + } + + Collections.reverse(res); + int[] ans = new int[res.size()]; + for (int i = 0; i < res.size(); i++) { + ans[i] = res.get(i); + } + + return ans; + } +} +``` + +```cpp +class Solution { +public: + vector findBuildings(vector& heights) { + vector res; + int n = heights.size(); + res.push_back(n - 1); + + for (int i = n - 2; i >= 0; i--) { + if (heights[i] > heights[res.back()]) { + res.push_back(i); + } + } + + reverse(res.begin(), res.end()); + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} heights + * @return {number[]} + */ + findBuildings(heights) { + const n = heights.length; + const res = [n - 1]; + + for (let i = n - 2; i >= 0; i--) { + if (heights[i] > heights[res[res.length - 1]]) { + res.push(i); + } + } + + return res.reverse(); + } +} +``` + +```csharp +public class Solution { + public int[] FindBuildings(int[] heights) { + int n = heights.Length; + List res = new List { n - 1 }; + + for (int i = n - 2; i >= 0; i--) { + if (heights[i] > heights[res[res.Count - 1]]) { + res.Add(i); + } + } + + res.Reverse(); + return res.ToArray(); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: + * $O(1)$ extra space. + * $O(n)$ for the output array. \ No newline at end of file diff --git a/articles/check-if-array-is-sorted-and-rotated.md b/articles/check-if-array-is-sorted-and-rotated.md new file mode 100644 index 000000000..061da0d48 --- /dev/null +++ b/articles/check-if-array-is-sorted-and-rotated.md @@ -0,0 +1,306 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def check(self, nums: List[int]) -> bool: + sortedNums = sorted(nums) + arr = [] + + for i in range(len(nums)): + arr.insert(0, sortedNums.pop()) + if nums == arr + sortedNums: + return True + + return False +``` + +```java +public class Solution { + public boolean check(int[] nums) { + int n = nums.length; + int[] sortedNums = nums.clone(); + Arrays.sort(sortedNums); + + for (int i = 0; i < n; i++) { + boolean match = true; + int idx = 0; + for (int j = n - i; j < n && match; j++) { + if (nums[idx] != sortedNums[j]) { + match = false; + } + idx += 1; + } + + for (int j = 0; j < n - i && match; j++) { + if (nums[idx] != sortedNums[j]) { + match = false; + } + idx += 1; + } + + if (match) return true; + } + + return false; + } +} +``` + +```cpp +class Solution { +public: + bool check(vector& nums) { + int n = nums.size(); + vector sortedNums = nums; + sort(sortedNums.begin(), sortedNums.end()); + + for (int i = 0; i < n; i++) { + bool match = true; + int idx = 0; + for (int j = n - i; j < n && match; j++) { + if (nums[idx] != sortedNums[j]) { + match = false; + } + idx++; + } + + for (int j = 0; j < n - i && match; j++) { + if (nums[idx] != sortedNums[j]) { + match = false; + } + idx++; + } + + if (match) return true; + } + + return false; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {boolean} + */ + check(nums) { + const n = nums.length; + const sortedNums = [...nums].sort((a, b) => a - b); + + for (let i = 0; i < n; i++) { + let match = true; + let idx = 0; + + for (let j = n - i; j < n && match; j++) { + if (nums[idx] !== sortedNums[j]) { + match = false; + } + idx++; + } + + for (let j = 0; j < n - i && match; j++) { + if (nums[idx] !== sortedNums[j]) { + match = false; + } + idx++; + } + + if (match) return true; + } + + return false; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n)$ + +--- + +## 2. Sliding Window + +::tabs-start + +```python +class Solution: + def check(self, nums: List[int]) -> bool: + N = len(nums) + count = 1 + + for i in range(1, 2 * N): + if nums[(i - 1) % N] <= nums[i % N]: + count += 1 + else: + count = 1 + if count == N: + return True + + return N == 1 +``` + +```java +public class Solution { + public boolean check(int[] nums) { + int N = nums.length; + int count = 1; + + for (int i = 1; i < 2 * N; i++) { + if (nums[(i - 1) % N] <= nums[i % N]) { + count++; + } else { + count = 1; + } + if (count == N) { + return true; + } + } + + return N == 1; + } +} +``` + +```cpp +class Solution { +public: + bool check(vector& nums) { + int N = nums.size(); + int count = 1; + + for (int i = 1; i < 2 * N; i++) { + if (nums[(i - 1) % N] <= nums[i % N]) { + count++; + } else { + count = 1; + } + if (count == N) { + return true; + } + } + + return N == 1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {boolean} + */ + check(nums) { + const N = nums.length; + let count = 1; + + for (let i = 1; i < 2 * N; i++) { + if (nums[(i - 1) % N] <= nums[i % N]) { + count++; + } else { + count = 1; + } + if (count === N) { + return true; + } + } + + return N === 1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ + +--- + +## 3. Iteration + +::tabs-start + +```python +class Solution: + def check(self, nums: List[int]) -> bool: + count, N = 0, len(nums) + + for i in range(N): + if nums[i] > nums[(i + 1) % N]: + count += 1 + if count > 1: + return False + + return True +``` + +```java +public class Solution { + public boolean check(int[] nums) { + int count = 0, N = nums.length; + + for (int i = 0; i < N; i++) { + if (nums[i] > nums[(i + 1) % N] && ++count > 1) { + return false; + } + } + + return true; + } +} +``` + +```cpp +class Solution { +public: + bool check(vector& nums) { + int count = 0, N = nums.size(); + + for (int i = 0; i < N; i++) { + if (nums[i] > nums[(i + 1) % N] && ++count > 1) { + return false; + } + } + + return true; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {boolean} + */ + check(nums) { + let count = 0, N = nums.length; + + for (let i = 0; i < N; i++) { + if (nums[i] > nums[(i + 1) % N] && ++count > 1) { + return false; + } + } + + return true; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/circular-sentence.md b/articles/circular-sentence.md new file mode 100644 index 000000000..c4e969dc1 --- /dev/null +++ b/articles/circular-sentence.md @@ -0,0 +1,154 @@ +## 1. Splitting the String + +::tabs-start + +```python +class Solution: + def isCircularSentence(self, sentence: str) -> bool: + w = sentence.split(" ") + + for i in range(len(w)): + if w[i][0] != w[i - 1][-1]: + return False + + return True +``` + +```java +public class Solution { + public boolean isCircularSentence(String sentence) { + String[] w = sentence.split(" "); + int n = w.length; + + for (int i = 0; i < n; i++) { + char start = w[i].charAt(0); + char end = w[(i - 1 + n) % n].charAt(w[(i - 1 + n) % n].length() - 1); + if (start != end) { + return false; + } + } + + return true; + } +} +``` + +```cpp +class Solution { +public: + bool isCircularSentence(string sentence) { + vector w; + stringstream ss(sentence); + string word; + + while (ss >> word) { + w.push_back(word); + } + + for (int i = 0; i < w.size(); i++) { + char start = w[i][0]; + char end = w[(i - 1 + w.size()) % w.size()].back(); + if (start != end) { + return false; + } + } + + return true; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} sentence + * @return {boolean} + */ + isCircularSentence(sentence) { + const w = sentence.split(" "); + + for (let i = 0; i < w.length; i++) { + const start = w[i][0]; + const prevEnd = w[(i - 1 + w.length) % w.length].slice(-1); + if (start !== prevEnd) { + return false; + } + } + + return true; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Iteration (Space Optimized) + +::tabs-start + +```python +class Solution: + def isCircularSentence(self, sentence: str) -> bool: + for i in range(len(sentence)): + if sentence[i] == " " and sentence[i - 1] != sentence[i + 1]: + return False + return sentence[0] == sentence[-1] +``` + +```java +public class Solution { + public boolean isCircularSentence(String sentence) { + for (int i = 0; i < sentence.length(); i++) { + if (sentence.charAt(i) == ' ' && sentence.charAt(i - 1) != sentence.charAt(i + 1)) { + return false; + } + } + return sentence.charAt(0) == sentence.charAt(sentence.length() - 1); + } +} +``` + +```cpp +class Solution { +public: + bool isCircularSentence(string sentence) { + for (int i = 0; i < sentence.size(); i++) { + if (sentence[i] == ' ' && sentence[i - 1] != sentence[i + 1]) { + return false; + } + } + return sentence.front() == sentence.back(); + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} sentence + * @return {boolean} + */ + isCircularSentence(sentence) { + for (let i = 0; i < sentence.length; i++) { + if (sentence[i] === ' ' && sentence[i - 1] !== sentence[i + 1]) { + return false; + } + } + return sentence[0] === sentence[sentence.length - 1]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/count-number-of-bad-pairs.md b/articles/count-number-of-bad-pairs.md new file mode 100644 index 000000000..936e3af7f --- /dev/null +++ b/articles/count-number-of-bad-pairs.md @@ -0,0 +1,157 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def countBadPairs(self, nums: List[int]) -> int: + n, res = len(nums), 0 + for i in range(n - 1): + for j in range(i + 1, n): + if j - i != nums[j] - nums[i]: + res += 1 + return res +``` + +```java +public class Solution { + public long countBadPairs(int[] nums) { + int n = nums.length; + long res = 0; + for (int i = 0; i < n - 1; i++) { + for (int j = i + 1; j < n; j++) { + if (j - i != nums[j] - nums[i]) { + res++; + } + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + long long countBadPairs(vector& nums) { + int n = nums.size(); + long long res = 0; + for (int i = 0; i < n - 1; i++) { + for (int j = i + 1; j < n; j++) { + if (j - i != nums[j] - nums[i]) { + res++; + } + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + countBadPairs(nums) { + let n = nums.length, res = 0; + for (let i = 0; i < n - 1; i++) { + for (let j = i + 1; j < n; j++) { + if (j - i !== nums[j] - nums[i]) { + res++; + } + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(1)$ + +--- + +## 2. Hash Map + +::tabs-start + +```python +class Solution: + def countBadPairs(self, nums: List[int]) -> int: + good_pairs = 0 + total_pairs = 0 + count = defaultdict(int) + + for i in range(len(nums)): + total_pairs += i + good_pairs += count[nums[i] - i] + count[nums[i] - i] += 1 + + return total_pairs - good_pairs +``` + +```java +public class Solution { + public long countBadPairs(int[] nums) { + Map count = new HashMap<>(); + long total = 0, good = 0; + for (int i = 0; i < nums.length; i++) { + int key = nums[i] - i; + good += count.getOrDefault(key, 0); + count.put(key, count.getOrDefault(key, 0) + 1); + total += i; + } + return total - good; + } +} +``` + +```cpp +class Solution { +public: + long long countBadPairs(vector& nums) { + unordered_map count; + long long total = 0, good = 0; + for (int i = 0; i < nums.size(); i++) { + int key = nums[i] - i; + good += count[key]; + count[key]++; + total += i; + } + return total - good; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + countBadPairs(nums) { + let count = new Map(); + let total = 0, good = 0; + for (let i = 0; i < nums.length; i++) { + let key = nums[i] - i; + good += count.get(key) || 0; + count.set(key, (count.get(key) || 0) + 1); + total += i; + } + return total - good; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/count-the-number-of-consistent-strings.md b/articles/count-the-number-of-consistent-strings.md new file mode 100644 index 000000000..600698357 --- /dev/null +++ b/articles/count-the-number-of-consistent-strings.md @@ -0,0 +1,415 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def countConsistentStrings(self, allowed: str, words: List[str]) -> int: + res = 0 + + for w in words: + flag = 1 + for c in w: + if c not in allowed: + flag = 0 + break + res += flag + + return res +``` + +```java +public class Solution { + public int countConsistentStrings(String allowed, String[] words) { + int res = 0; + + for (String w : words) { + boolean flag = true; + for (char c : w.toCharArray()) { + if (allowed.indexOf(c) == -1) { + flag = false; + break; + } + } + if (flag) res++; + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int countConsistentStrings(string allowed, vector& words) { + int res = 0; + + for (string& w : words) { + bool flag = true; + for (char c : w) { + if (allowed.find(c) == string::npos) { + flag = false; + break; + } + } + res += flag; + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} allowed + * @param {string[]} words + * @return {number} + */ + countConsistentStrings(allowed, words) { + let res = 0; + + for (let w of words) { + let flag = 1; + for (let c of w) { + if (!allowed.includes(c)) { + flag = 0; + break; + } + } + res += flag; + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * m * l)$ +* Space complexity: $O(1)$ + +> Where $n$ is the number of words, $m$ is the length of the string $allowed$, and $l$ is the length of the longest word. + +--- + +## 2. Hash Set + +::tabs-start + +```python +class Solution: + def countConsistentStrings(self, allowed: str, words: List[str]) -> int: + allowed = set(allowed) + + res = len(words) + for w in words: + for c in w: + if c not in allowed: + res -= 1 + break + + return res +``` + +```java +public class Solution { + public int countConsistentStrings(String allowed, String[] words) { + Set allowedSet = new HashSet<>(); + for (char c : allowed.toCharArray()) { + allowedSet.add(c); + } + + int res = words.length; + for (String w : words) { + for (char c : w.toCharArray()) { + if (!allowedSet.contains(c)) { + res--; + break; + } + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int countConsistentStrings(string allowed, vector& words) { + unordered_set allowedSet(allowed.begin(), allowed.end()); + + int res = words.size(); + for (string& w : words) { + for (char c : w) { + if (allowedSet.find(c) == allowedSet.end()) { + res--; + break; + } + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} allowed + * @param {string[]} words + * @return {number} + */ + countConsistentStrings(allowed, words) { + const allowedSet = new Set(allowed); + let res = words.length; + + for (let w of words) { + for (let c of w) { + if (!allowedSet.has(c)) { + res--; + break; + } + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * l + m)$ +* Space complexity: $O(m)$ + +> Where $n$ is the number of words, $m$ is the length of the string $allowed$, and $l$ is the length of the longest word. + +--- + +## 3. Boolean Array + +::tabs-start + +```python +class Solution: + def countConsistentStrings(self, allowed: str, words: List[str]) -> int: + allowedArr = [False] * 26 + for c in allowed: + allowedArr[ord(c) - ord('a')] = True + + res = len(words) + for w in words: + for c in w: + if not allowedArr[ord(c) - ord('a')]: + res -= 1 + break + + return res +``` + +```java +public class Solution { + public int countConsistentStrings(String allowed, String[] words) { + Set allowedSet = new HashSet<>(); + for (char c : allowed.toCharArray()) { + allowedSet.add(c); + } + + int res = words.length; + for (String w : words) { + for (char c : w.toCharArray()) { + if (!allowedSet.contains(c)) { + res--; + break; + } + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int countConsistentStrings(string allowed, vector& words) { + bool allowedArr[26] = {}; + for (char c : allowed) { + allowedArr[c - 'a'] = true; + } + + int res = words.size(); + for (const string& w : words) { + for (char c : w) { + if (!allowedArr[c - 'a']) { + res--; + break; + } + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} allowed + * @param {string[]} words + * @return {number} + */ + countConsistentStrings(allowed, words) { + const allowedArr = new Array(26).fill(false); + for (let c of allowed) { + allowedArr[c.charCodeAt(0) - 97] = true; + } + + let res = words.length; + for (let w of words) { + for (let c of w) { + if (!allowedArr[c.charCodeAt(0) - 97]) { + res--; + break; + } + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * l + m)$ +* Space complexity: $O(m)$ + +> Where $n$ is the number of words, $m$ is the length of the string $allowed$, and $l$ is the length of the longest word. + +--- + +## 4. Bitmask + +::tabs-start + +```python +class Solution: + def countConsistentStrings(self, allowed: str, words: List[str]) -> int: + bit_mask = 0 + for c in allowed: + bit = 1 << (ord(c) - ord('a')) + bit_mask = bit_mask | bit + + res = len(words) + for w in words: + for c in w: + bit = 1 << (ord(c) - ord('a')) + if bit & bit_mask == 0: + res -= 1 + break + + return res +``` + +```java +public class Solution { + public int countConsistentStrings(String allowed, String[] words) { + int bitMask = 0; + for (char c : allowed.toCharArray()) { + bitMask |= 1 << (c - 'a'); + } + + int res = words.length; + for (String w : words) { + for (char c : w.toCharArray()) { + int bit = 1 << (c - 'a'); + if ((bit & bitMask) == 0) { + res--; + break; + } + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int countConsistentStrings(string allowed, vector& words) { + int bitMask = 0; + for (char c : allowed) { + bitMask |= (1 << (c - 'a')); + } + + int res = words.size(); + for (const string& w : words) { + for (char c : w) { + int bit = 1 << (c - 'a'); + if ((bit & bitMask) == 0) { + res--; + break; + } + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} allowed + * @param {string[]} words + * @return {number} + */ + countConsistentStrings(allowed, words) { + let bitMask = 0; + for (let c of allowed) { + bitMask |= 1 << (c.charCodeAt(0) - 97); + } + + let res = words.length; + for (let w of words) { + for (let c of w) { + const bit = 1 << (c.charCodeAt(0) - 97); + if ((bit & bitMask) === 0) { + res--; + break; + } + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * l + m)$ +* Space complexity: $O(1)$ + +> Where $n$ is the number of words, $m$ is the length of the string $allowed$, and $l$ is the length of the longest word. \ No newline at end of file diff --git a/articles/count-vowel-strings-in-ranges.md b/articles/count-vowel-strings-in-ranges.md new file mode 100644 index 000000000..22c92a839 --- /dev/null +++ b/articles/count-vowel-strings-in-ranges.md @@ -0,0 +1,336 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def vowelStrings(self, words: List[str], queries: List[List[int]]) -> List[int]: + vowels = set("aeiou") + res = [] + + for start, end in queries: + cnt = 0 + for i in range(start, end + 1): + if words[i][0] in vowels and words[i][-1] in vowels: + cnt += 1 + res.append(cnt) + + return res +``` + +```java +public class Solution { + public int[] vowelStrings(String[] words, int[][] queries) { + Set vowels = Set.of('a', 'e', 'i', 'o', 'u'); + int[] res = new int[queries.length]; + + for (int k = 0; k < queries.length; k++) { + int start = queries[k][0], end = queries[k][1], count = 0; + + for (int i = start; i <= end; i++) { + String word = words[i]; + if (vowels.contains(word.charAt(0)) && vowels.contains(word.charAt(word.length() - 1))) { + count++; + } + } + + res[k] = count; + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector vowelStrings(vector& words, vector>& queries) { + unordered_set vowels = {'a', 'e', 'i', 'o', 'u'}; + vector res; + + for (auto& q : queries) { + int start = q[0], end = q[1], count = 0; + + for (int i = start; i <= end; i++) { + if (vowels.count(words[i][0]) && vowels.count(words[i].back())) { + count++; + } + } + + res.push_back(count); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} words + * @param {number[][]} queries + * @return {number[]} + */ + vowelStrings(words, queries) { + const vowels = new Set(['a', 'e', 'i', 'o', 'u']); + const res = []; + + for (let [start, end] of queries) { + let count = 0; + for (let i = start; i <= end; i++) { + const word = words[i]; + if (vowels.has(word[0]) && vowels.has(word[word.length - 1])) { + count++; + } + } + res.push(count); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * m)$ +* Space complexity: + * $O(1)$ extra space. + * $O(m)$ space for the output list. + +> Where $n$ is the number of words, and $m$ is the number of queries. + +--- + +## 2. Prefix Sum + Hash Set + +::tabs-start + +```python +class Solution: + def vowelStrings(self, words: List[str], queries: List[List[int]]) -> List[int]: + vowel_set = set("aeiou") + prefix_cnt = [0] * (len(words) + 1) + prev = 0 + + for i, w in enumerate(words): + if w[0] in vowel_set and w[-1] in vowel_set: + prev += 1 + prefix_cnt[i + 1] = prev + + res = [0] * len(queries) + for i, q in enumerate(queries): + l, r = q + res[i] = prefix_cnt[r + 1] - prefix_cnt[l] + + return res +``` + +```java +public class Solution { + public int[] vowelStrings(String[] words, int[][] queries) { + Set vowels = Set.of('a', 'e', 'i', 'o', 'u'); + int n = words.length; + int[] prefixCnt = new int[n + 1]; + + for (int i = 0; i < n; i++) { + String w = words[i]; + prefixCnt[i + 1] = prefixCnt[i]; + if (vowels.contains(w.charAt(0)) && vowels.contains(w.charAt(w.length() - 1))) { + prefixCnt[i + 1]++; + } + } + + int[] res = new int[queries.length]; + for (int i = 0; i < queries.length; i++) { + int l = queries[i][0], r = queries[i][1]; + res[i] = prefixCnt[r + 1] - prefixCnt[l]; + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector vowelStrings(vector& words, vector>& queries) { + unordered_set vowels = {'a', 'e', 'i', 'o', 'u'}; + int n = words.size(); + vector prefixCnt(n + 1, 0); + + for (int i = 0; i < n; i++) { + prefixCnt[i + 1] = prefixCnt[i]; + if (vowels.count(words[i][0]) && vowels.count(words[i].back())) { + prefixCnt[i + 1]++; + } + } + + vector res; + for (auto& q : queries) { + int l = q[0], r = q[1]; + res.push_back(prefixCnt[r + 1] - prefixCnt[l]); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} words + * @param {number[][]} queries + * @return {number[]} + */ + vowelStrings(words, queries) { + const vowels = new Set(['a', 'e', 'i', 'o', 'u']); + const n = words.length; + const prefixCnt = new Array(n + 1).fill(0); + + for (let i = 0; i < n; i++) { + prefixCnt[i + 1] = prefixCnt[i]; + const w = words[i]; + if (vowels.has(w[0]) && vowels.has(w[w.length - 1])) { + prefixCnt[i + 1]++; + } + } + + const res = new Array(queries.length); + for (let i = 0; i < queries.length; i++) { + const [l, r] = queries[i]; + res[i] = prefixCnt[r + 1] - prefixCnt[l]; + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n + m)$ +* Space complexity: + * $O(n)$ extra space. + * $O(m)$ space for the output list. + +> Where $n$ is the number of words, and $m$ is the number of queries. + +--- + +## 3. Prefix Sum + Bitmask + +::tabs-start + +```python +class Solution: + def vowelStrings(self, words: List[str], queries: List[List[int]]) -> List[int]: + vowels = sum(1 << (ord(c) - ord('a')) for c in "aeiou") + prefix = [0] + for w in words: + prefix.append(prefix[-1]) + if (1 << (ord(w[0]) - ord('a'))) & vowels and (1 << (ord(w[-1]) - ord('a'))) & vowels: + prefix[-1] += 1 + return [prefix[r + 1] - prefix[l] for l, r in queries] +``` + +```java +public class Solution { + public int[] vowelStrings(String[] words, int[][] queries) { + int vowels = 0; + for (char c : "aeiou".toCharArray()) { + vowels |= 1 << (c - 'a'); + } + + int[] prefix = new int[words.length + 1]; + for (int i = 0; i < words.length; i++) { + int f = words[i].charAt(0) - 'a'; + int l = words[i].charAt(words[i].length() - 1) - 'a'; + int isVowel = ((1 << f) & vowels) != 0 && ((1 << l) & vowels) != 0 ? 1 : 0; + prefix[i + 1] = prefix[i] + isVowel; + } + + int[] res = new int[queries.length]; + for (int i = 0; i < queries.length; i++) { + int l = queries[i][0], r = queries[i][1]; + res[i] = prefix[r + 1] - prefix[l]; + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector vowelStrings(vector& words, vector>& queries) { + int vowels = 0; + for (char c : string("aeiou")) { + vowels |= (1 << (c - 'a')); + } + + int n = words.size(); + vector prefix(n + 1); + for (int i = 0; i < n; i++) { + int f = words[i][0] - 'a'; + int l = words[i].back() - 'a'; + int isVowel = ((1 << f) & vowels) && ((1 << l) & vowels); + prefix[i + 1] = prefix[i] + isVowel; + } + + vector res; + for (auto& q : queries) { + int l = q[0], r = q[1]; + res.push_back(prefix[r + 1] - prefix[l]); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} words + * @param {number[][]} queries + * @return {number[]} + */ + vowelStrings(words, queries) { + let vowels = 0; + for (let c of "aeiou") { + vowels |= 1 << (c.charCodeAt(0) - 97); + } + + const prefix = [0]; + for (let w of words) { + const f = w.charCodeAt(0) - 97; + const l = w.charCodeAt(w.length - 1) - 97; + const isVowel = ((1 << f) & vowels) && ((1 << l) & vowels) ? 1 : 0; + prefix.push(prefix[prefix.length - 1] + isVowel); + } + + return queries.map(([l, r]) => prefix[r + 1] - prefix[l]); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n + m)$ +* Space complexity: + * $O(n)$ extra space. + * $O(m)$ space for the output list. + +> Where $n$ is the number of words, and $m$ is the number of queries. \ No newline at end of file diff --git a/articles/divide-array-into-equal-pairs.md b/articles/divide-array-into-equal-pairs.md new file mode 100644 index 000000000..bc8a01fb1 --- /dev/null +++ b/articles/divide-array-into-equal-pairs.md @@ -0,0 +1,290 @@ +## 1. Sorting + +::tabs-start + +```python +class Solution: + def divideArray(self, nums: List[int]) -> bool: + N = len(nums) + nums.sort() + + i = 0 + while i < N: + j = i + while j < N and nums[i] == nums[j]: + j += 1 + + if (j - i) % 2 != 0: + return False + + i = j + + return True +``` + +```java +public class Solution { + public boolean divideArray(int[] nums) { + int N = nums.length; + Arrays.sort(nums); + + int i = 0; + while (i < N) { + int j = i; + while (j < N && nums[i] == nums[j]) { + j++; + } + + if ((j - i) % 2 != 0) { + return false; + } + + i = j; + } + + return true; + } +} +``` + +```cpp +class Solution { +public: + bool divideArray(vector& nums) { + int N = nums.size(); + sort(nums.begin(), nums.end()); + + int i = 0; + while (i < N) { + int j = i; + while (j < N && nums[i] == nums[j]) { + j++; + } + + if ((j - i) % 2 != 0) { + return false; + } + + i = j; + } + + return true; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {boolean} + */ + divideArray(nums) { + const N = nums.length; + nums.sort((a, b) => a - b); + + let i = 0; + while (i < N) { + let j = i; + while (j < N && nums[i] === nums[j]) { + j++; + } + + if ((j - i) % 2 !== 0) { + return false; + } + + i = j; + } + + return true; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(1)$ or $O(n)$ depending on the sorting algorithm. + +--- + +## 2. Hash Map + +::tabs-start + +```python +class Solution: + def divideArray(self, nums: List[int]) -> bool: + count = {} + for num in nums: + if num not in count: + count[num] = 0 + count[num] += 1 + + for cnt in count.values(): + if cnt % 2 == 1: + return False + + return True +``` + +```java +public class Solution { + public boolean divideArray(int[] nums) { + Map count = new HashMap<>(); + for (int num : nums) { + count.put(num, count.getOrDefault(num, 0) + 1); + } + + for (int cnt : count.values()) { + if (cnt % 2 == 1) { + return false; + } + } + + return true; + } +} +``` + +```cpp +class Solution { +public: + bool divideArray(vector& nums) { + unordered_map count; + for (int num : nums) { + count[num]++; + } + + for (auto& [key, cnt] : count) { + if (cnt % 2 == 1) { + return false; + } + } + + return true; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {boolean} + */ + divideArray(nums) { + const count = {}; + for (let num of nums) { + if (!(num in count)) { + count[num] = 0; + } + count[num]++; + } + + for (let key in count) { + if (count[key] % 2 === 1) { + return false; + } + } + + return true; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Hash Set + +::tabs-start + +```python +class Solution: + def divideArray(self, nums: List[int]) -> bool: + odd_set = set() + + for num in nums: + if num not in odd_set: + odd_set.add(num) + else: + odd_set.remove(num) + + return not len(odd_set) +``` + +```java +public class Solution { + public boolean divideArray(int[] nums) { + Set oddSet = new HashSet<>(); + + for (int num : nums) { + if (!oddSet.contains(num)) { + oddSet.add(num); + } else { + oddSet.remove(num); + } + } + + return oddSet.isEmpty(); + } +} +``` + +```cpp +class Solution { +public: + bool divideArray(vector& nums) { + unordered_set oddSet; + + for (int num : nums) { + if (oddSet.count(num)) { + oddSet.erase(num); + } else { + oddSet.insert(num); + } + } + + return oddSet.empty(); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {boolean} + */ + divideArray(nums) { + const oddSet = new Set(); + + for (let num of nums) { + if (oddSet.has(num)) { + oddSet.delete(num); + } else { + oddSet.add(num); + } + } + + return oddSet.size === 0; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/find-common-characters.md b/articles/find-common-characters.md new file mode 100644 index 000000000..4a6cfe1e8 --- /dev/null +++ b/articles/find-common-characters.md @@ -0,0 +1,122 @@ +## 1. Frequency Count + +::tabs-start + +```python +class Solution: + def commonChars(self, words: List[str]) -> List[str]: + cnt = Counter(words[0]) + + for w in words: + cur_cnt = Counter(w) + for c in cnt: + cnt[c] = min(cnt[c], cur_cnt[c]) + + res = [] + for c in cnt: + for i in range(cnt[c]): + res.append(c) + + return res +``` + +```java +public class Solution { + public List commonChars(String[] words) { + int[] cnt = new int[26]; + Arrays.fill(cnt, Integer.MAX_VALUE); + + for (String word : words) { + int[] curCnt = new int[26]; + for (char c : word.toCharArray()) { + curCnt[c - 'a']++; + } + + for (int i = 0; i < 26; i++) { + cnt[i] = Math.min(cnt[i], curCnt[i]); + } + } + + List res = new ArrayList<>(); + for (int i = 0; i < 26; i++) { + for (int j = 0; j < cnt[i]; j++) { + res.add(String.valueOf((char) (i + 'a'))); + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector commonChars(vector& words) { + vector cnt(26, INT_MAX); + + for (string& word : words) { + vector curCnt(26, 0); + for (char c : word) { + curCnt[c - 'a']++; + } + + for (int i = 0; i < 26; i++) { + cnt[i] = min(cnt[i], curCnt[i]); + } + } + + vector res; + for (int i = 0; i < 26; i++) { + for (int j = 0; j < cnt[i]; j++) { + res.push_back(string(1, i + 'a')); + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} words + * @return {string[]} + */ + commonChars(words) { + const cnt = new Array(26).fill(Infinity); + + for (let word of words) { + const curCnt = new Array(26).fill(0); + for (let c of word) { + curCnt[c.charCodeAt(0) - 97]++; + } + + for (let i = 0; i < 26; i++) { + cnt[i] = Math.min(cnt[i], curCnt[i]); + } + } + + const res = []; + for (let i = 0; i < 26; i++) { + for (let j = 0; j < cnt[i]; j++) { + res.push(String.fromCharCode(i + 97)); + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * m)$ +* Space complexity: + * $O(1)$ extra space, since we have at most $26$ different characters. + * $O(n * m)$ space for the output list. + +> Where $n$ is the number of words and $m$ is the length of the longest word. \ No newline at end of file diff --git a/articles/find-missing-and-repeated-values.md b/articles/find-missing-and-repeated-values.md new file mode 100644 index 000000000..58753bb0f --- /dev/null +++ b/articles/find-missing-and-repeated-values.md @@ -0,0 +1,492 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def findMissingAndRepeatedValues(self, grid: List[List[int]]) -> List[int]: + n = len(grid) + double = missing = 0 + + for num in range(1, n * n + 1): + cnt = 0 + for i in range(n): + for j in range(n): + if num == grid[i][j]: + cnt += 1 + + if cnt == 2: + double = num + elif cnt == 0: + missing = num + + return [double, missing] +``` + +```java +public class Solution { + public int[] findMissingAndRepeatedValues(int[][] grid) { + int n = grid.length; + int doubleVal = 0, missing = 0; + + for (int num = 1; num <= n * n; num++) { + int cnt = 0; + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { + if (grid[i][j] == num) { + cnt++; + } + } + } + + if (cnt == 2) { + doubleVal = num; + } else if (cnt == 0) { + missing = num; + } + } + + return new int[]{doubleVal, missing}; + } +} +``` + +```cpp +class Solution { +public: + vector findMissingAndRepeatedValues(vector>& grid) { + int n = grid.size(); + int doubleVal = 0, missing = 0; + + for (int num = 1; num <= n * n; num++) { + int cnt = 0; + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { + if (grid[i][j] == num) { + cnt++; + } + } + } + + if (cnt == 2) { + doubleVal = num; + } else if (cnt == 0) { + missing = num; + } + } + + return {doubleVal, missing}; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} grid + * @return {number[]} + */ + findMissingAndRepeatedValues(grid) { + const n = grid.length; + let doubleVal = 0, missing = 0; + + for (let num = 1; num <= n * n; num++) { + let cnt = 0; + for (let i = 0; i < n; i++) { + for (let j = 0; j < n; j++) { + if (grid[i][j] === num) { + cnt++; + } + } + } + + if (cnt === 2) { + doubleVal = num; + } else if (cnt === 0) { + missing = num; + } + } + + return [doubleVal, missing]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 4)$ +* Space complexity: $O(1)$ + +--- + +## 2. Hash Map + +::tabs-start + +```python +class Solution: + def findMissingAndRepeatedValues(self, grid: List[List[int]]) -> List[int]: + N = len(grid) + count = defaultdict(int) + + for i in range(N): + for j in range(N): + count[grid[i][j]] += 1 + + double = missing = 0 + + for num in range(1, N * N + 1): + if count[num] == 0: + missing = num + if count[num] == 2: + double = num + + return [double, missing] +``` + +```java +public class Solution { + public int[] findMissingAndRepeatedValues(int[][] grid) { + int N = grid.length; + Map count = new HashMap<>(); + + for (int i = 0; i < N; i++) { + for (int j = 0; j < N; j++) { + count.put(grid[i][j], count.getOrDefault(grid[i][j], 0) + 1); + } + } + + int doubleVal = 0, missing = 0; + + for (int num = 1; num <= N * N; num++) { + int freq = count.getOrDefault(num, 0); + if (freq == 0) missing = num; + if (freq == 2) doubleVal = num; + } + + return new int[]{doubleVal, missing}; + } +} +``` + +```cpp +class Solution { +public: + vector findMissingAndRepeatedValues(vector>& grid) { + int N = grid.size(); + unordered_map count; + + for (int i = 0; i < N; i++) { + for (int j = 0; j < N; j++) { + count[grid[i][j]]++; + } + } + + int doubleVal = 0, missing = 0; + + for (int num = 1; num <= N * N; num++) { + int freq = count[num]; + if (freq == 0) missing = num; + if (freq == 2) doubleVal = num; + } + + return {doubleVal, missing}; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} grid + * @return {number[]} + */ + findMissingAndRepeatedValues(grid) { + const N = grid.length; + const count = {}; + + for (let i = 0; i < N; i++) { + for (let j = 0; j < N; j++) { + let val = grid[i][j]; + count[val] = (count[val] || 0) + 1; + } + } + + let doubleVal = 0, missing = 0; + + for (let num = 1; num <= N * N; num++) { + let freq = count[num] || 0; + if (freq === 0) missing = num; + if (freq === 2) doubleVal = num; + } + + return [doubleVal, missing]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n ^ 2)$ + +--- + +## 3. Hash Set + +::tabs-start + +```python +class Solution: + def findMissingAndRepeatedValues(self, grid: List[List[int]]) -> List[int]: + N = len(grid) + seen = set() + double = missing = 0 + + for i in range(N): + for j in range(N): + if grid[i][j] in seen: + double = grid[i][j] + seen.add(grid[i][j]) + + for num in range(1, N * N + 1): + if num not in seen: + missing = num + break + + return [double, missing] +``` + +```java +public class Solution { + public int[] findMissingAndRepeatedValues(int[][] grid) { + int N = grid.length; + Set seen = new HashSet<>(); + int doubleVal = 0, missing = 0; + + for (int i = 0; i < N; i++) { + for (int j = 0; j < N; j++) { + if (seen.contains(grid[i][j])) { + doubleVal = grid[i][j]; + } + seen.add(grid[i][j]); + } + } + + for (int num = 1; num <= N * N; num++) { + if (!seen.contains(num)) { + missing = num; + break; + } + } + + return new int[]{doubleVal, missing}; + } +} +``` + +```cpp +class Solution { +public: + vector findMissingAndRepeatedValues(vector>& grid) { + int N = grid.size(); + unordered_set seen; + int doubleVal = 0, missing = 0; + + for (int i = 0; i < N; i++) { + for (int j = 0; j < N; j++) { + if (seen.count(grid[i][j])) { + doubleVal = grid[i][j]; + } + seen.insert(grid[i][j]); + } + } + + for (int num = 1; num <= N * N; num++) { + if (!seen.count(num)) { + missing = num; + break; + } + } + + return {doubleVal, missing}; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} grid + * @return {number[]} + */ + findMissingAndRepeatedValues(grid) { + const N = grid.length; + const seen = new Set(); + let doubleVal = 0, missing = 0; + + for (let i = 0; i < N; i++) { + for (let j = 0; j < N; j++) { + const val = grid[i][j]; + if (seen.has(val)) { + doubleVal = val; + } + seen.add(val); + } + } + + for (let num = 1; num <= N * N; num++) { + if (!seen.has(num)) { + missing = num; + break; + } + } + + return [doubleVal, missing]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n ^ 2)$ + +--- + +## 4. Math + +::tabs-start + +```python +class Solution: + def findMissingAndRepeatedValues(self, grid: List[List[int]]) -> List[int]: + N = len(grid) + gridSum = 0 + gridSqSum = 0 + + for i in range(N): + for j in range(N): + gridSum += grid[i][j] + gridSqSum += grid[i][j] * grid[i][j] + + totSum = (N * N * (N * N + 1)) // 2 + diff = gridSum - totSum # a - b + + totSqSum = (N * N * (N * N + 1) * (2 * N * N + 1)) // 6 + sqDiff = gridSqSum - totSqSum # (a^2) - (b^2) + + sum = sqDiff // diff # a + b + + a = (sum + diff) // 2 + b = sum - a + return [a, b] +``` + +```java +public class Solution { + public int[] findMissingAndRepeatedValues(int[][] grid) { + int N = grid.length; + long gridSum = 0; + long gridSqSum = 0; + + for (int i = 0; i < N; i++) { + for (int j = 0; j < N; j++) { + gridSum += grid[i][j]; + gridSqSum += 1L * grid[i][j] * grid[i][j]; + } + } + + long totSum = (long) N * N * (N * N + 1) / 2; + long diff = gridSum - totSum; // a - b + + long totSqSum = (long) N * N * (N * N + 1) * (2L * N * N + 1) / 6; + long sqDiff = gridSqSum - totSqSum; // (a^2) - (b^2) + + long sum = sqDiff / diff; // a + b + + long a = (sum + diff) / 2; + long b = sum - a; + + return new int[]{(int) a, (int) b}; + } +} +``` + +```cpp +class Solution { +public: + vector findMissingAndRepeatedValues(vector>& grid) { + int N = grid.size(); + long long gridSum = 0; + long long gridSqSum = 0; + + for (int i = 0; i < N; i++) { + for (int j = 0; j < N; j++) { + gridSum += grid[i][j]; + gridSqSum += 1LL * grid[i][j] * grid[i][j]; + } + } + + long long totSum = 1LL * N * N * (N * N + 1) / 2; + long long diff = gridSum - totSum; // a - b + + long long totSqSum = 1LL * N * N * (N * N + 1) * (2 * N * N + 1) / 6; + long long sqDiff = gridSqSum - totSqSum; // (a^2) - (b^2) + + long long sum = sqDiff / diff; // a + b + + int a = (sum + diff) / 2; + int b = sum - a; + + return {a, b}; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} grid + * @return {number[]} + */ + findMissingAndRepeatedValues(grid) { + const N = grid.length; + let gridSum = 0; + let gridSqSum = 0; + + for (let i = 0; i < N; i++) { + for (let j = 0; j < N; j++) { + gridSum += grid[i][j]; + gridSqSum += grid[i][j] * grid[i][j]; + } + } + + let totSum = N * N * (N * N + 1) / 2; + let diff = gridSum - totSum; // a - b + + let totSqSum = N * N * (N * N + 1) * (2 * N * N + 1) / 6; + let sqDiff = gridSqSum - totSqSum; // (a^2) - (b^2) + + let sum = sqDiff / diff; // a + b + + let a = (sum + diff) / 2; + let b = sum - a; + + return [a, b]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/height-checker.md b/articles/height-checker.md new file mode 100644 index 000000000..1e0919676 --- /dev/null +++ b/articles/height-checker.md @@ -0,0 +1,206 @@ +## 1. Sorting + +::tabs-start + +```python +class Solution: + def heightChecker(self, heights: List[int]) -> int: + expected = sorted(heights) + + res = 0 + for i in range(len(heights)): + if heights[i] != expected[i]: + res += 1 + + return res +``` + +```java +public class Solution { + public int heightChecker(int[] heights) { + int[] expected = Arrays.copyOf(heights, heights.length); + Arrays.sort(expected); + + int res = 0; + for (int i = 0; i < heights.length; i++) { + if (heights[i] != expected[i]) { + res++; + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int heightChecker(vector& heights) { + vector expected = heights; + sort(expected.begin(), expected.end()); + + int res = 0; + for (int i = 0; i < heights.size(); i++) { + if (heights[i] != expected[i]) { + res++; + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} heights + * @return {number} + */ + heightChecker(heights) { + const expected = [...heights].sort((a, b) => a - b); + + let res = 0; + for (let i = 0; i < heights.length; i++) { + if (heights[i] !== expected[i]) { + res++; + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Counting Sort + +::tabs-start + +```python +class Solution: + def heightChecker(self, heights: List[int]) -> int: + count = [0] * 101 + for h in heights: + count[h] += 1 + + expected = [] + for h in range(1, 101): + c = count[h] + for _ in range(c): + expected.append(h) + + res = 0 + for i in range(len(heights)): + if heights[i] != expected[i]: + res += 1 + + return res +``` + +```java +public class Solution { + public int heightChecker(int[] heights) { + int[] count = new int[101]; + for (int h : heights) { + count[h]++; + } + + List expected = new ArrayList<>(); + for (int h = 1; h <= 100; h++) { + int c = count[h]; + for (int i = 0; i < c; i++) { + expected.add(h); + } + } + + int res = 0; + for (int i = 0; i < heights.length; i++) { + if (heights[i] != expected.get(i)) { + res++; + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int heightChecker(vector& heights) { + int count[101] = {}; + for (int h : heights) { + count[h]++; + } + + vector expected; + for (int h = 1; h <= 100; h++) { + int c = count[h]; + for (int i = 0; i < c; i++) { + expected.push_back(h); + } + } + + int res = 0; + for (int i = 0; i < heights.size(); i++) { + if (heights[i] != expected[i]) { + res++; + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} heights + * @return {number} + */ + heightChecker(heights) { + const count = new Array(101).fill(0); + for (let h of heights) { + count[h]++; + } + + const expected = []; + for (let h = 1; h <= 100; h++) { + let c = count[h]; + for (let i = 0; i < c; i++) { + expected.push(h); + } + } + + let res = 0; + for (let i = 0; i < heights.length; i++) { + if (heights[i] !== expected[i]) { + res++; + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n + k)$ +* Space complexity: $O(n + k)$ + +> Where $n$ is the size of the input array, and $k$ is the range of numbers. \ No newline at end of file diff --git a/articles/kth-distinct-string-in-an-array.md b/articles/kth-distinct-string-in-an-array.md new file mode 100644 index 000000000..244281490 --- /dev/null +++ b/articles/kth-distinct-string-in-an-array.md @@ -0,0 +1,350 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def kthDistinct(self, arr: List[str], k: int) -> str: + for i in range(len(arr)): + flag = True + for j in range(len(arr)): + if i == j: + continue + + if arr[i] == arr[j]: + flag = False + break + + if flag: + k -= 1 + if k == 0: + return arr[i] + return "" +``` + +```java +public class Solution { + public String kthDistinct(String[] arr, int k) { + for (int i = 0; i < arr.length; i++) { + boolean flag = true; + for (int j = 0; j < arr.length; j++) { + if (i == j) continue; + + if (arr[i].equals(arr[j])) { + flag = false; + break; + } + } + + if (flag) { + k--; + if (k == 0) { + return arr[i]; + } + } + } + return ""; + } +} +``` + +```cpp +class Solution { +public: + string kthDistinct(vector& arr, int k) { + for (int i = 0; i < arr.size(); i++) { + bool flag = true; + for (int j = 0; j < arr.size(); j++) { + if (i == j) continue; + + if (arr[i] == arr[j]) { + flag = false; + break; + } + } + + if (flag) { + k--; + if (k == 0) { + return arr[i]; + } + } + } + return ""; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} arr + * @param {number} k + * @return {string} + */ + kthDistinct(arr, k) { + for (let i = 0; i < arr.length; i++) { + let flag = true; + for (let j = 0; j < arr.length; j++) { + if (i === j) continue; + + if (arr[i] === arr[j]) { + flag = false; + break; + } + } + + if (flag) { + k--; + if (k === 0) { + return arr[i]; + } + } + } + return ""; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(1)$ + +--- + +## 2. Hash Map + +::tabs-start + +```python +class Solution: + def kthDistinct(self, arr: List[str], k: int) -> str: + count = {} + + for s in arr: + if s not in count: + count[s] = 0 + count[s] += 1 + + for s in arr: + if count[s] == 1: + k -= 1 + if k == 0: + return s + + return "" +``` + +```java +public class Solution { + public String kthDistinct(String[] arr, int k) { + Map count = new HashMap<>(); + + for (String s : arr) { + count.put(s, count.getOrDefault(s, 0) + 1); + } + + for (String s : arr) { + if (count.get(s) == 1) { + k--; + if (k == 0) { + return s; + } + } + } + + return ""; + } +} +``` + +```cpp +class Solution { +public: + string kthDistinct(vector& arr, int k) { + unordered_map count; + + for (const string& s : arr) { + count[s]++; + } + + for (const string& s : arr) { + if (count[s] == 1) { + k--; + if (k == 0) { + return s; + } + } + } + + return ""; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} arr + * @param {number} k + * @return {string} + */ + kthDistinct(arr, k) { + const count = {}; + + for (let s of arr) { + if (!(s in count)) { + count[s] = 0; + } + count[s]++; + } + + for (let s of arr) { + if (count[s] === 1) { + k--; + if (k === 0) { + return s; + } + } + } + + return ""; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Hash Set + +::tabs-start + +```python +class Solution: + def kthDistinct(self, arr: List[str], k: int) -> str: + distinct, seen = set(), set() + + for s in arr: + if s in distinct: + distinct.remove(s) + seen.add(s) + elif s not in seen: + distinct.add(s) + + for s in arr: + if s in distinct: + k -= 1 + if k == 0: + return s + + return "" +``` + +```java +public class Solution { + public String kthDistinct(String[] arr, int k) { + Set distinct = new HashSet<>(); + Set seen = new HashSet<>(); + + for (String s : arr) { + if (distinct.contains(s)) { + distinct.remove(s); + seen.add(s); + } else if (!seen.contains(s)) { + distinct.add(s); + } + } + + for (String s : arr) { + if (distinct.contains(s)) { + k--; + if (k == 0) { + return s; + } + } + } + + return ""; + } +} +``` + +```cpp +class Solution { +public: + string kthDistinct(vector& arr, int k) { + unordered_set distinct, seen; + + for (const string& s : arr) { + if (distinct.count(s)) { + distinct.erase(s); + seen.insert(s); + } else if (!seen.count(s)) { + distinct.insert(s); + } + } + + for (const string& s : arr) { + if (distinct.count(s)) { + k--; + if (k == 0) { + return s; + } + } + } + + return ""; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} arr + * @param {number} k + * @return {string} + */ + kthDistinct(arr, k) { + const distinct = new Set(); + const seen = new Set(); + + for (let s of arr) { + if (distinct.has(s)) { + distinct.delete(s); + seen.add(s); + } else if (!seen.has(s)) { + distinct.add(s); + } + } + + for (let s of arr) { + if (distinct.has(s)) { + k--; + if (k === 0) { + return s; + } + } + } + + return ""; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/longest-palindrome.md b/articles/longest-palindrome.md new file mode 100644 index 000000000..635b7a163 --- /dev/null +++ b/articles/longest-palindrome.md @@ -0,0 +1,419 @@ +## 1. Hash Map + +::tabs-start + +```python +class Solution: + def longestPalindrome(self, s: str) -> int: + count = defaultdict(int) + res = 0 + + for c in s: + count[c] += 1 + if count[c] % 2 == 0: + res += 2 + + for cnt in count.values(): + if cnt % 2: + res += 1 + break + + return res +``` + +```java +public class Solution { + public int longestPalindrome(String s) { + Map count = new HashMap<>(); + int res = 0; + + for (char c : s.toCharArray()) { + count.put(c, count.getOrDefault(c, 0) + 1); + if (count.get(c) % 2 == 0) { + res += 2; + } + } + + for (int cnt : count.values()) { + if (cnt % 2 == 1) { + res += 1; + break; + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int longestPalindrome(string s) { + unordered_map count; + int res = 0; + + for (char c : s) { + count[c]++; + if (count[c] % 2 == 0) { + res += 2; + } + } + + for (auto& [ch, cnt] : count) { + if (cnt % 2 == 1) { + res += 1; + break; + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {number} + */ + longestPalindrome(s) { + const count = {}; + let res = 0; + + for (let c of s) { + count[c] = (count[c] || 0) + 1; + if (count[c] % 2 === 0) { + res += 2; + } + } + + for (let key in count) { + if (count[key] % 2 === 1) { + res += 1; + break; + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(m)$ + +> Where $n$ is the length of the given string, and $m$ is the number of distinct characters in the string. + +--- + +## 2. Hash Map (Optimal) + +::tabs-start + +```python +class Solution: + def longestPalindrome(self, s: str) -> int: + count = defaultdict(int) + res = 0 + + for c in s: + count[c] += 1 + if count[c] % 2 == 0: + res += 2 + + return res + (res < len(s)) +``` + +```java +public class Solution { + public int longestPalindrome(String s) { + Map count = new HashMap<>(); + int res = 0; + + for (char c : s.toCharArray()) { + count.put(c, count.getOrDefault(c, 0) + 1); + if (count.get(c) % 2 == 0) { + res += 2; + } + } + + return res + (res < s.length() ? 1 : 0); + } +} +``` + +```cpp +class Solution { +public: + int longestPalindrome(string s) { + unordered_map count; + int res = 0; + + for (char c : s) { + count[c]++; + if (count[c] % 2 == 0) { + res += 2; + } + } + + return res + (res < s.size()); + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {number} + */ + longestPalindrome(s) { + const count = {}; + let res = 0; + + for (let c of s) { + count[c] = (count[c] || 0) + 1; + if (count[c] % 2 === 0) { + res += 2; + } + } + + return res + (res < s.length ? 1 : 0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(m)$ + +> Where $n$ is the length of the given string, and $m$ is the number of distinct characters in the string. + +--- + +## 3. Hash Set + +::tabs-start + +```python +class Solution: + def longestPalindrome(self, s: str) -> int: + seen = set() + res = 0 + + for c in s: + if c in seen: + seen.remove(c) + res += 2 + else: + seen.add(c) + + return res + 1 if seen else res +``` + +```java +public class Solution { + public int longestPalindrome(String s) { + Set seen = new HashSet<>(); + int res = 0; + + for (char c : s.toCharArray()) { + if (seen.contains(c)) { + seen.remove(c); + res += 2; + } else { + seen.add(c); + } + } + + return seen.isEmpty() ? res : res + 1; + } +} +``` + +```cpp +class Solution { +public: + int longestPalindrome(string s) { + unordered_set seen; + int res = 0; + + for (char c : s) { + if (seen.count(c)) { + seen.erase(c); + res += 2; + } else { + seen.insert(c); + } + } + + return seen.empty() ? res : res + 1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {number} + */ + longestPalindrome(s) { + const seen = new Set(); + let res = 0; + + for (let c of s) { + if (seen.has(c)) { + seen.delete(c); + res += 2; + } else { + seen.add(c); + } + } + + return seen.size === 0 ? res : res + 1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(m)$ + +> Where $n$ is the length of the given string, and $m$ is the number of distinct characters in the string. + +--- + +## 4. Bitmask + +::tabs-start + +```python +class Solution: + def longestPalindrome(self, s: str) -> int: + mask1 = 0 # [a - z] + mask2 = 0 # [A - Z] + res = 0 + + for c in s: + if 'a' <= c <= 'z': + bit = 1 << (ord(c) - ord('a')) + if mask1 & bit: + res += 2 + mask1 ^= bit + else: + bit = 1 << (ord(c) - ord('A')) + if mask2 & bit: + res += 2 + mask2 ^= bit + + return res + 1 if mask1 or mask2 else res +``` + +```java +public class Solution { + public int longestPalindrome(String s) { + int mask1 = 0; // [a - z] + int mask2 = 0; // [A - Z] + int res = 0; + + for (char c : s.toCharArray()) { + if (c >= 'a' && c <= 'z') { + int bit = 1 << (c - 'a'); + if ((mask1 & bit) != 0) { + res += 2; + } + mask1 ^= bit; + } else { + int bit = 1 << (c - 'A'); + if ((mask2 & bit) != 0) { + res += 2; + } + mask2 ^= bit; + } + } + + return (mask1 != 0 || mask2 != 0) ? res + 1 : res; + } +} +``` + +```cpp +class Solution { +public: + int longestPalindrome(string s) { + int mask1 = 0; // [a - z] + int mask2 = 0; // [A - Z] + int res = 0; + + for (char c : s) { + if ('a' <= c && c <= 'z') { + int bit = 1 << (c - 'a'); + if (mask1 & bit) { + res += 2; + } + mask1 ^= bit; + } else { + int bit = 1 << (c - 'A'); + if (mask2 & bit) { + res += 2; + } + mask2 ^= bit; + } + } + + return (mask1 || mask2) ? res + 1 : res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {number} + */ + longestPalindrome(s) { + let mask1 = 0; // [a - z] + let mask2 = 0; // [A - Z] + let res = 0; + + for (let c of s) { + if (c >= 'a' && c <= 'z') { + let bit = 1 << (c.charCodeAt(0) - 97); + if (mask1 & bit) { + res += 2; + } + mask1 ^= bit; + } else { + let bit = 1 << (c.charCodeAt(0) - 65); + if (mask2 & bit) { + res += 2; + } + mask2 ^= bit; + } + } + + return (mask1 || mask2) ? res + 1 : res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/longest-strictly-increasing-or-strictly-decreasing-subarray.md b/articles/longest-strictly-increasing-or-strictly-decreasing-subarray.md new file mode 100644 index 000000000..4c55b7a6b --- /dev/null +++ b/articles/longest-strictly-increasing-or-strictly-decreasing-subarray.md @@ -0,0 +1,454 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def longestMonotonicSubarray(self, nums: List[int]) -> int: + n = len(nums) + res = 1 + + for i in range(n - 1): + curLen = 1 + for j in range(i + 1, n): + if nums[j] == nums[j - 1] or ((nums[i] < nums[i + 1]) != (nums[j - 1] < nums[j])): + break + curLen += 1 + + res = max(res, curLen) + + return res +``` + +```java +public class Solution { + public int longestMonotonicSubarray(int[] nums) { + int n = nums.length; + int res = 1; + + for (int i = 0; i < n - 1; i++) { + int curLen = 1; + for (int j = i + 1; j < n; j++) { + if (nums[j] == nums[j - 1] || ((nums[i] < nums[i + 1]) != (nums[j - 1] < nums[j]))) { + break; + } + curLen++; + } + res = Math.max(res, curLen); + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int longestMonotonicSubarray(vector& nums) { + int n = nums.size(); + int res = 1; + + for (int i = 0; i < n - 1; i++) { + int curLen = 1; + for (int j = i + 1; j < n; j++) { + if (nums[j] == nums[j - 1] || ((nums[i] < nums[i + 1]) != (nums[j - 1] < nums[j]))) { + break; + } + curLen++; + } + res = max(res, curLen); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + longestMonotonicSubarray(nums) { + let n = nums.length; + let res = 1; + + for (let i = 0; i < n - 1; i++) { + let curLen = 1; + for (let j = i + 1; j < n; j++) { + if (nums[j] === nums[j - 1] || ((nums[i] < nums[i + 1]) !== (nums[j - 1] < nums[j]))) { + break; + } + curLen++; + } + res = Math.max(res, curLen); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(1)$ + +--- + +## 2. Iteration - I + +::tabs-start + +```python +class Solution: + def longestMonotonicSubarray(self, nums: List[int]) -> int: + cur = 1 + res = 1 + increasing = 0 + + for i in range(1, len(nums)): + if nums[i - 1] < nums[i]: + if increasing > 0: + cur += 1 + else: + cur = 2 + increasing = 1 + elif nums[i - 1] > nums[i]: + if increasing < 0: + cur += 1 + else: + cur = 2 + increasing = -1 + else: + cur = 1 + increasing = 0 + res = max(res, cur) + + return res +``` + +```java +public class Solution { + public int longestMonotonicSubarray(int[] nums) { + int cur = 1; + int res = 1; + int increasing = 0; + + for (int i = 1; i < nums.length; i++) { + if (nums[i - 1] < nums[i]) { + if (increasing > 0) { + cur++; + } else { + cur = 2; + increasing = 1; + } + } else if (nums[i - 1] > nums[i]) { + if (increasing < 0) { + cur++; + } else { + cur = 2; + increasing = -1; + } + } else { + cur = 1; + increasing = 0; + } + res = Math.max(res, cur); + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int longestMonotonicSubarray(vector& nums) { + int cur = 1; + int res = 1; + int increasing = 0; + + for (int i = 1; i < nums.size(); i++) { + if (nums[i - 1] < nums[i]) { + if (increasing > 0) { + cur++; + } else { + cur = 2; + increasing = 1; + } + } else if (nums[i - 1] > nums[i]) { + if (increasing < 0) { + cur++; + } else { + cur = 2; + increasing = -1; + } + } else { + cur = 1; + increasing = 0; + } + res = max(res, cur); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + longestMonotonicSubarray(nums) { + let cur = 1; + let res = 1; + let increasing = 0; + + for (let i = 1; i < nums.length; i++) { + if (nums[i - 1] < nums[i]) { + if (increasing > 0) { + cur++; + } else { + cur = 2; + increasing = 1; + } + } else if (nums[i - 1] > nums[i]) { + if (increasing < 0) { + cur++; + } else { + cur = 2; + increasing = -1; + } + } else { + cur = 1; + increasing = 0; + } + res = Math.max(res, cur); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ + +--- + +## 3. Iteration - II + +::tabs-start + +```python +class Solution: + def longestMonotonicSubarray(self, nums: List[int]) -> int: + inc = dec = 1 + res = 1 + + for i in range(1, len(nums)): + if nums[i] == nums[i - 1]: + inc = dec = 1 + elif nums[i] > nums[i - 1]: + inc, dec = inc + 1, 1 + else: + inc, dec = 1, dec + 1 + + res = max(res, inc, dec) + + return res +``` + +```java +public class Solution { + public int longestMonotonicSubarray(int[] nums) { + int inc = 1, dec = 1, res = 1; + + for (int i = 1; i < nums.length; i++) { + if (nums[i] == nums[i - 1]) { + inc = dec = 1; + } else if (nums[i] > nums[i - 1]) { + inc = inc + 1; + dec = 1; + } else { + inc = 1; + dec = dec + 1; + } + res = Math.max(res, Math.max(inc, dec)); + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int longestMonotonicSubarray(vector& nums) { + int inc = 1, dec = 1, res = 1; + + for (int i = 1; i < nums.size(); i++) { + if (nums[i] == nums[i - 1]) { + inc = dec = 1; + } else if (nums[i] > nums[i - 1]) { + inc = inc + 1; + dec = 1; + } else { + inc = 1; + dec = dec + 1; + } + res = max(res, max(inc, dec)); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + longestMonotonicSubarray(nums) { + let inc = 1, dec = 1, res = 1; + + for (let i = 1; i < nums.length; i++) { + if (nums[i] === nums[i - 1]) { + inc = dec = 1; + } else if (nums[i] > nums[i - 1]) { + inc = inc + 1; + dec = 1; + } else { + inc = 1; + dec = dec + 1; + } + res = Math.max(res, inc, dec); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ + +--- + +## 4. Iteration - III + +::tabs-start + +```python +class Solution: + def longestMonotonicSubarray(self, nums: List[int]) -> int: + curLen = res = 1 + + for i in range(1, len(nums)): + if (nums[i] == nums[i - 1] or + ((nums[i - curLen] < nums[i - curLen + 1]) != (nums[i - 1] < nums[i])) + ): + curLen = 1 if (nums[i] == nums[i - 1]) else 2 + continue + + curLen += 1 + res = max(res, curLen) + + return res +``` + +```java +public class Solution { + public int longestMonotonicSubarray(int[] nums) { + int curLen = 1, res = 1; + + for (int i = 1; i < nums.length; i++) { + if (nums[i] == nums[i - 1] || + ((nums[i - curLen] < nums[i - curLen + 1]) != (nums[i - 1] < nums[i]))) { + curLen = (nums[i] == nums[i - 1]) ? 1 : 2; + continue; + } + + curLen++; + res = Math.max(res, curLen); + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int longestMonotonicSubarray(vector& nums) { + int curLen = 1, res = 1; + + for (int i = 1; i < nums.size(); i++) { + if (nums[i] == nums[i - 1] || + ((nums[i - curLen] < nums[i - curLen + 1]) != (nums[i - 1] < nums[i]))) { + curLen = (nums[i] == nums[i - 1]) ? 1 : 2; + continue; + } + + curLen++; + res = max(res, curLen); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + longestMonotonicSubarray(nums) { + let curLen = 1, res = 1; + + for (let i = 1; i < nums.length; i++) { + if ( + nums[i] === nums[i - 1] || + ((nums[i - curLen] < nums[i - curLen + 1]) !== (nums[i - 1] < nums[i])) + ) { + curLen = nums[i] === nums[i - 1] ? 1 : 2; + continue; + } + + curLen++; + res = Math.max(res, curLen); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/make-sum-divisible-by-p.md b/articles/make-sum-divisible-by-p.md new file mode 100644 index 000000000..493f5732c --- /dev/null +++ b/articles/make-sum-divisible-by-p.md @@ -0,0 +1,235 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def minSubarray(self, nums: List[int], p: int) -> int: + n = len(nums) + totSum = sum(nums) + + if totSum % p == 0: + return 0 + + for l in range(1, n): + curSum = 0 + for i in range(n): + curSum += nums[i] + if i >= l: + curSum -= nums[i - l] + + remainSum = totSum - curSum + if remainSum % p == 0: + return l + + return -1 +``` + +```java +public class Solution { + public int minSubarray(int[] nums, int p) { + int n = nums.length; + long totSum = 0; + for (int num : nums) totSum += num; + + if (totSum % p == 0) return 0; + + for (int l = 1; l < n; l++) { + long curSum = 0; + for (int i = 0; i < n; i++) { + curSum += nums[i]; + if (i >= l) curSum -= nums[i - l]; + + long remainSum = totSum - curSum; + if (remainSum % p == 0) return l; + } + } + + return -1; + } +} +``` + +```cpp +class Solution { +public: + int minSubarray(vector& nums, int p) { + int n = nums.size(); + long long totSum = 0; + for (int num : nums) totSum += num; + + if (totSum % p == 0) return 0; + + for (int l = 1; l < n; l++) { + long long curSum = 0; + for (int i = 0; i < n; i++) { + curSum += nums[i]; + if (i >= l) curSum -= nums[i - l]; + + long long remainSum = totSum - curSum; + if (remainSum % p == 0) return l; + } + } + + return -1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} p + * @return {number} + */ + minSubarray(nums, p) { + const n = nums.length; + let totSum = nums.reduce((a, b) => a + b, 0); + + if (totSum % p === 0) return 0; + + for (let l = 1; l < n; l++) { + let curSum = 0; + for (let i = 0; i < n; i++) { + curSum += nums[i]; + if (i >= l) curSum -= nums[i - l]; + + const remainSum = totSum - curSum; + if (remainSum % p === 0) return l; + } + } + + return -1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(1)$ + +--- + +## 2. Prefix Sum + +::tabs-start + +```python +class Solution: + def minSubarray(self, nums: List[int], p: int) -> int: + total = sum(nums) + remain = total % p + if remain == 0: + return 0 + + res = len(nums) + cur_sum = 0 + remain_to_idx = {0: -1} + + for i, n in enumerate(nums): + cur_sum = (cur_sum + n) % p + prefix = (cur_sum - remain + p) % p + if prefix in remain_to_idx: + length = i - remain_to_idx[prefix] + res = min(res, length) + remain_to_idx[cur_sum] = i + + return -1 if res == len(nums) else res +``` + +```java +public class Solution { + public int minSubarray(int[] nums, int p) { + long total = 0; + for (int num : nums) total += num; + int remain = (int)(total % p); + if (remain == 0) return 0; + + int res = nums.length; + long curSum = 0; + Map map = new HashMap<>(); + map.put(0, -1); + + for (int i = 0; i < nums.length; i++) { + curSum = (curSum + nums[i]) % p; + int prefix = (int)((curSum - remain + p) % p); + if (map.containsKey(prefix)) { + res = Math.min(res, i - map.get(prefix)); + } + map.put((int)curSum, i); + } + + return res == nums.length ? -1 : res; + } +} +``` + +```cpp +class Solution { +public: + int minSubarray(vector& nums, int p) { + long total = 0; + for (int num : nums) total += num; + int remain = total % p; + if (remain == 0) return 0; + + int res = nums.size(); + long curSum = 0; + unordered_map map; + map[0] = -1; + + for (int i = 0; i < nums.size(); i++) { + curSum = (curSum + nums[i]) % p; + int prefix = (curSum - remain + p) % p; + if (map.count(prefix)) { + res = min(res, i - map[prefix]); + } + map[curSum] = i; + } + + return res == nums.size() ? -1 : res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} p + * @return {number} + */ + minSubarray(nums, p) { + let total = nums.reduce((a, b) => a + b, 0); + let remain = total % p; + if (remain === 0) return 0; + + let res = nums.length; + let curSum = 0; + const map = new Map(); + map.set(0, -1); + + for (let i = 0; i < nums.length; i++) { + curSum = (curSum + nums[i]) % p; + let prefix = (curSum - remain + p) % p; + if (map.has(prefix)) { + res = Math.min(res, i - map.get(prefix)); + } + map.set(curSum, i); + } + + return res === nums.length ? -1 : res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/maximum-ascending-subarray-sum.md b/articles/maximum-ascending-subarray-sum.md new file mode 100644 index 000000000..5d4e47a60 --- /dev/null +++ b/articles/maximum-ascending-subarray-sum.md @@ -0,0 +1,173 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def maxAscendingSum(self, nums: List[int]) -> int: + res = 0 + for i in range(len(nums)): + curSum = nums[i] + for j in range(i + 1, len(nums)): + if nums[j] <= nums[j - 1]: + break + curSum += nums[j] + res = max(res, curSum) + return res +``` + +```java +public class Solution { + public int maxAscendingSum(int[] nums) { + int res = 0; + for (int i = 0; i < nums.length; i++) { + int curSum = nums[i]; + for (int j = i + 1; j < nums.length; j++) { + if (nums[j] <= nums[j - 1]) { + break; + } + curSum += nums[j]; + } + res = Math.max(res, curSum); + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int maxAscendingSum(vector& nums) { + int res = 0; + for (int i = 0; i < nums.size(); i++) { + int curSum = nums[i]; + for (int j = i + 1; j < nums.size(); j++) { + if (nums[j] <= nums[j - 1]) { + break; + } + curSum += nums[j]; + } + res = max(res, curSum); + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + maxAscendingSum(nums) { + let res = 0; + for (let i = 0; i < nums.length; i++) { + let curSum = nums[i]; + for (let j = i + 1; j < nums.length; j++) { + if (nums[j] <= nums[j - 1]) { + break; + } + curSum += nums[j]; + } + res = Math.max(res, curSum); + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(1)$ + +--- + +## 2. Iteration + +::tabs-start + +```python +class Solution: + def maxAscendingSum(self, nums: List[int]) -> int: + res = curSum= nums[0] + + for i in range(1, len(nums)): + if nums[i] <= nums[i - 1]: + curSum = 0 + + curSum += nums[i] + res = max(res, curSum) + + return res +``` + +```java +public class Solution { + public int maxAscendingSum(int[] nums) { + int res = nums[0], curSum = nums[0]; + + for (int i = 1; i < nums.length; i++) { + if (nums[i] <= nums[i - 1]) { + curSum = 0; + } + curSum += nums[i]; + res = Math.max(res, curSum); + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int maxAscendingSum(vector& nums) { + int res = nums[0], curSum = nums[0]; + + for (int i = 1; i < nums.size(); i++) { + if (nums[i] <= nums[i - 1]) { + curSum = 0; + } + curSum += nums[i]; + res = max(res, curSum); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + maxAscendingSum(nums) { + let res = nums[0], curSum = nums[0]; + + for (let i = 1; i < nums.length; i++) { + if (nums[i] <= nums[i - 1]) { + curSum = 0; + } + curSum += nums[i]; + res = Math.max(res, curSum); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/minimum-index-of-a-valid-split.md b/articles/minimum-index-of-a-valid-split.md new file mode 100644 index 000000000..17548cd54 --- /dev/null +++ b/articles/minimum-index-of-a-valid-split.md @@ -0,0 +1,385 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def minimumIndex(self, nums: List[int]) -> int: + n = len(nums) + + for i in range(n - 1): + left_cnt = defaultdict(int) + for l in range(i + 1): + left_cnt[nums[l]] += 1 + + right_cnt = defaultdict(int) + for r in range(i + 1, n): + right_cnt[nums[r]] += 1 + + for num in left_cnt: + if left_cnt[num] > (i + 1) // 2 and right_cnt[num] > (n - i - 1) // 2: + return i + + return -1 +``` + +```java +public class Solution { + public int minimumIndex(List nums) { + int n = nums.size(); + + for (int i = 0; i < n - 1; i++) { + Map leftCnt = new HashMap<>(); + for (int l = 0; l <= i; l++) { + int val = nums.get(l); + leftCnt.put(val, leftCnt.getOrDefault(val, 0) + 1); + } + + Map rightCnt = new HashMap<>(); + for (int r = i + 1; r < n; r++) { + int val = nums.get(r); + rightCnt.put(val, rightCnt.getOrDefault(val, 0) + 1); + } + + for (int num : leftCnt.keySet()) { + if (leftCnt.get(num) > (i + 1) / 2 && rightCnt.getOrDefault(num, 0) > (n - i - 1) / 2) { + return i; + } + } + } + + return -1; + } +} +``` + +```cpp +class Solution { +public: + int minimumIndex(vector& nums) { + int n = nums.size(); + + for (int i = 0; i < n - 1; i++) { + unordered_map leftCnt, rightCnt; + for (int l = 0; l <= i; l++) { + leftCnt[nums[l]]++; + } + for (int r = i + 1; r < n; r++) { + rightCnt[nums[r]]++; + } + + for (auto& [num, cnt] : leftCnt) { + if (cnt > (i + 1) / 2 && rightCnt[num] > (n - i - 1) / 2) { + return i; + } + } + } + + return -1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + minimumIndex(nums) { + const n = nums.length; + + for (let i = 0; i < n - 1; i++) { + const leftCnt = {}; + for (let l = 0; l <= i; l++) { + leftCnt[nums[l]] = (leftCnt[nums[l]] || 0) + 1; + } + + const rightCnt = {}; + for (let r = i + 1; r < n; r++) { + rightCnt[nums[r]] = (rightCnt[nums[r]] || 0) + 1; + } + + for (const num in leftCnt) { + if (leftCnt[num] > Math.floor((i + 1) / 2) && + (rightCnt[num] || 0) > Math.floor((n - i - 1) / 2)) { + return i; + } + } + } + + return -1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n)$ + +--- + +## 2. Hash Map + +::tabs-start + +```python +class Solution: + def minimumIndex(self, nums: List[int]) -> int: + left = defaultdict(int) + right = Counter(nums) + + for i in range(len(nums)): + left[nums[i]] += 1 + right[nums[i]] -= 1 + + left_len = i + 1 + right_len = len(nums) - i - 1 + + if 2 * left[nums[i]] > left_len and 2 * right[nums[i]] > right_len: + return i + + return -1 +``` + +```java +public class Solution { + public int minimumIndex(List nums) { + Map left = new HashMap<>(); + Map right = new HashMap<>(); + int n = nums.size(); + + for (int num : nums) { + right.put(num, right.getOrDefault(num, 0) + 1); + } + + for (int i = 0; i < n; i++) { + int num = nums.get(i); + left.put(num, left.getOrDefault(num, 0) + 1); + right.put(num, right.get(num) - 1); + + int leftLen = i + 1; + int rightLen = n - i - 1; + + if (2 * left.get(num) > leftLen && 2 * right.get(num) > rightLen) { + return i; + } + } + + return -1; + } +} +``` + +```cpp +class Solution { +public: + int minimumIndex(vector& nums) { + unordered_map left, right; + int n = nums.size(); + + for (int num : nums) { + right[num]++; + } + + for (int i = 0; i < n; i++) { + int num = nums[i]; + left[num]++; + right[num]--; + + int leftLen = i + 1; + int rightLen = n - i - 1; + + if (2 * left[num] > leftLen && 2 * right[num] > rightLen) { + return i; + } + } + + return -1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + minimumIndex(nums) { + const left = {}; + const right = {}; + const n = nums.length; + + for (const num of nums) { + right[num] = (right[num] || 0) + 1; + } + + for (let i = 0; i < n; i++) { + const num = nums[i]; + left[num] = (left[num] || 0) + 1; + right[num] -= 1; + + const leftLen = i + 1; + const rightLen = n - i - 1; + + if (2 * left[num] > leftLen && 2 * right[num] > rightLen) { + return i; + } + } + + return -1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Boyer-Moore Voting Algorithm + +::tabs-start + +```python +class Solution: + def minimumIndex(self, nums: List[int]) -> int: + majority = count = 0 + for num in nums: + if count == 0: + majority = num + count += (1 if majority == num else -1) + + left_cnt, right_cnt = 0, nums.count(majority) + + for i in range(len(nums)): + if nums[i] == majority: + left_cnt += 1 + right_cnt -= 1 + + left_len = i + 1 + right_len = len(nums) - i - 1 + + if 2 * left_cnt > left_len and 2 * right_cnt > right_len: + return i + + return -1 +``` + +```java +public class Solution { + public int minimumIndex(List nums) { + int majority = 0, count = 0; + for (int num : nums) { + if (count == 0) majority = num; + count += (majority == num) ? 1 : -1; + } + + int leftCnt = 0, rightCnt = 0; + for (int num : nums) { + if (num == majority) rightCnt++; + } + + int n = nums.size(); + for (int i = 0; i < n; i++) { + if (nums.get(i) == majority) { + leftCnt++; + rightCnt--; + } + + int leftLen = i + 1; + int rightLen = n - i - 1; + + if (2 * leftCnt > leftLen && 2 * rightCnt > rightLen) { + return i; + } + } + + return -1; + } +} +``` + +```cpp +class Solution { +public: + int minimumIndex(vector& nums) { + int majority = 0, count = 0; + for (int num : nums) { + if (count == 0) majority = num; + count += (num == majority ? 1 : -1); + } + + int leftCnt = 0, rightCnt = count_if(nums.begin(), nums.end(), + [&](int x) { return x == majority; }); + + int n = nums.size(); + for (int i = 0; i < n; i++) { + if (nums[i] == majority) { + leftCnt++; + rightCnt--; + } + + int leftLen = i + 1; + int rightLen = n - i - 1; + + if (2 * leftCnt > leftLen && 2 * rightCnt > rightLen) { + return i; + } + } + + return -1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + minimumIndex(nums) { + let majority = 0, count = 0; + for (let num of nums) { + if (count === 0) majority = num; + count += (num === majority ? 1 : -1); + } + + let leftCnt = 0; + let rightCnt = nums.filter(x => x === majority).length; + const n = nums.length; + + for (let i = 0; i < n; i++) { + if (nums[i] === majority) { + leftCnt++; + rightCnt--; + } + + let leftLen = i + 1; + let rightLen = n - i - 1; + + if (2 * leftCnt > leftLen && 2 * rightCnt > rightLen) { + return i; + } + } + + return -1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/minimum-number-of-operations-to-move-all-balls-to-each-box.md b/articles/minimum-number-of-operations-to-move-all-balls-to-each-box.md new file mode 100644 index 000000000..c00bbfdb0 --- /dev/null +++ b/articles/minimum-number-of-operations-to-move-all-balls-to-each-box.md @@ -0,0 +1,325 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def minOperations(self, boxes: str) -> List[int]: + n = len(boxes) + res = [0] * n + + for pos in range(n): + for i in range(n): + if boxes[i] == '1': + res[pos] += abs(pos - i) + + return res +``` + +```java +public class Solution { + public int[] minOperations(String boxes) { + int n = boxes.length(); + int[] res = new int[n]; + + for (int pos = 0; pos < n; pos++) { + for (int i = 0; i < n; i++) { + if (boxes.charAt(i) == '1') { + res[pos] += Math.abs(pos - i); + } + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector minOperations(string boxes) { + int n = boxes.size(); + vector res(n, 0); + + for (int pos = 0; pos < n; pos++) { + for (int i = 0; i < n; i++) { + if (boxes[i] == '1') { + res[pos] += abs(pos - i); + } + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} boxes + * @return {number[]} + */ + minOperations(boxes) { + const n = boxes.length; + const res = new Array(n).fill(0); + + for (let pos = 0; pos < n; pos++) { + for (let i = 0; i < n; i++) { + if (boxes[i] === '1') { + res[pos] += Math.abs(pos - i); + } + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: + * $O(1)$ extra space. + * $O(n)$ space for the output list. + +--- + +## 2. Prefix Sum + +::tabs-start + +```python +class Solution: + def minOperations(self, boxes: str) -> List[int]: + n = len(boxes) + res = [0] * n + + prefix_count = [0] * (n + 1) + index_sum = [0] * (n + 1) + for i in range(n): + prefix_count[i + 1] = prefix_count[i] + (boxes[i] == '1') + index_sum[i + 1] = index_sum[i] + (i if boxes[i] == '1' else 0) + + for i in range(n): + left = prefix_count[i] + left_sum = index_sum[i] + + right = prefix_count[n] - prefix_count[i + 1] + right_sum = index_sum[n] - index_sum[i + 1] + + res[i] = (i * left - left_sum) + (right_sum - i * right) + + return res +``` + +```java +public class Solution { + public int[] minOperations(String boxes) { + int n = boxes.length(); + int[] res = new int[n]; + int[] prefixCount = new int[n + 1]; + int[] indexSum = new int[n + 1]; + + for (int i = 0; i < n; i++) { + prefixCount[i + 1] = prefixCount[i] + (boxes.charAt(i) == '1' ? 1 : 0); + indexSum[i + 1] = indexSum[i] + (boxes.charAt(i) == '1' ? i : 0); + } + + for (int i = 0; i < n; i++) { + int left = prefixCount[i]; + int leftSum = indexSum[i]; + + int right = prefixCount[n] - prefixCount[i + 1]; + int rightSum = indexSum[n] - indexSum[i + 1]; + + res[i] = i * left - leftSum + (rightSum - i * right); + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector minOperations(string boxes) { + int n = boxes.size(); + vector res(n), prefixCount(n + 1, 0), indexSum(n + 1, 0); + + for (int i = 0; i < n; i++) { + prefixCount[i + 1] = prefixCount[i] + (boxes[i] == '1' ? 1 : 0); + indexSum[i + 1] = indexSum[i] + (boxes[i] == '1' ? i : 0); + } + + for (int i = 0; i < n; i++) { + int left = prefixCount[i]; + int leftSum = indexSum[i]; + int right = prefixCount[n] - prefixCount[i + 1]; + int rightSum = indexSum[n] - indexSum[i + 1]; + res[i] = i * left - leftSum + (rightSum - i * right); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} boxes + * @return {number[]} + */ + minOperations(boxes) { + const n = boxes.length; + const res = new Array(n).fill(0); + const prefixCount = new Array(n + 1).fill(0); + const indexSum = new Array(n + 1).fill(0); + + for (let i = 0; i < n; i++) { + prefixCount[i + 1] = prefixCount[i] + (boxes[i] === '1' ? 1 : 0); + indexSum[i + 1] = indexSum[i] + (boxes[i] === '1' ? i : 0); + } + + for (let i = 0; i < n; i++) { + const left = prefixCount[i]; + const leftSum = indexSum[i]; + const right = prefixCount[n] - prefixCount[i + 1]; + const rightSum = indexSum[n] - indexSum[i + 1]; + + res[i] = i * left - leftSum + (rightSum - i * right); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Prefix Sum (Optimal) + +::tabs-start + +```python +class Solution: + def minOperations(self, boxes: str) -> List[int]: + n = len(boxes) + res = [0] * n + + balls = moves = 0 + for i in range(n): + res[i] = balls + moves + moves += balls + balls += int(boxes[i]) + + balls = moves = 0 + for i in range(n - 1, -1, -1): + res[i] += balls + moves + moves += balls + balls += int(boxes[i]) + + return res +``` + +```java +public class Solution { + public int[] minOperations(String boxes) { + int n = boxes.length(); + int[] res = new int[n]; + + int balls = 0, moves = 0; + for (int i = 0; i < n; i++) { + res[i] = balls + moves; + moves += balls; + balls += boxes.charAt(i) - '0'; + } + + balls = moves = 0; + for (int i = n - 1; i >= 0; i--) { + res[i] += balls + moves; + moves += balls; + balls += boxes.charAt(i) - '0'; + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector minOperations(string boxes) { + int n = boxes.size(); + vector res(n, 0); + + int balls = 0, moves = 0; + for (int i = 0; i < n; i++) { + res[i] = balls + moves; + moves += balls; + balls += boxes[i] - '0'; + } + + balls = moves = 0; + for (int i = n - 1; i >= 0; i--) { + res[i] += balls + moves; + moves += balls; + balls += boxes[i] - '0'; + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} boxes + * @return {number[]} + */ + minOperations(boxes) { + const n = boxes.length; + const res = new Array(n).fill(0); + + let balls = 0, moves = 0; + for (let i = 0; i < n; i++) { + res[i] = balls + moves; + moves += balls; + balls += Number(boxes[i]); + } + + balls = moves = 0; + for (let i = n - 1; i >= 0; i--) { + res[i] += balls + moves; + moves += balls; + balls += Number(boxes[i]); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: + * $O(1)$ extra space. + * $O(n)$ space for the output list. \ No newline at end of file diff --git a/articles/minimum-remove-to-make-valid-parentheses.md b/articles/minimum-remove-to-make-valid-parentheses.md index 23627cf72..43c6d5981 100644 --- a/articles/minimum-remove-to-make-valid-parentheses.md +++ b/articles/minimum-remove-to-make-valid-parentheses.md @@ -128,6 +128,39 @@ class Solution { } ``` +```csharp +public class Solution { + public string MinRemoveToMakeValid(string s) { + List res = new List(); + int cnt = 0; + + foreach (char c in s) { + if (c == '(') { + res.Add(c); + cnt++; + } else if (c == ')' && cnt > 0) { + res.Add(c); + cnt--; + } else if (c != ')') { + res.Add(c); + } + } + + List filtered = new List(); + for (int i = res.Count - 1; i >= 0; i--) { + if (res[i] == '(' && cnt > 0) { + cnt--; + } else { + filtered.Add(res[i]); + } + } + + filtered.Reverse(); + return new string(filtered.ToArray()); + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -264,6 +297,37 @@ class Solution { } ``` +```csharp +public class Solution { + public string MinRemoveToMakeValid(string s) { + char[] arr = s.ToCharArray(); + int cnt = 0; + + for (int i = 0; i < s.Length; i++) { + if (s[i] == '(') { + cnt++; + } else if (s[i] == ')' && cnt > 0) { + cnt--; + } else if (s[i] == ')') { + arr[i] = '\0'; // mark invalid ')' + } + } + + List res = new List(); + for (int i = arr.Length - 1; i >= 0; i--) { + if (arr[i] == '(' && cnt > 0) { + cnt--; + } else if (arr[i] != '\0') { + res.Add(arr[i]); + } + } + + res.Reverse(); + return new string(res.ToArray()); + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -393,6 +457,40 @@ class Solution { } ``` +```csharp +public class Solution { + public string MinRemoveToMakeValid(string s) { + char[] arr = s.ToCharArray(); + Stack stack = new Stack(); + + for (int i = 0; i < arr.Length; i++) { + if (arr[i] == '(') { + stack.Push(i); + } else if (arr[i] == ')') { + if (stack.Count > 0) { + stack.Pop(); + } else { + arr[i] = '\0'; + } + } + } + + while (stack.Count > 0) { + arr[stack.Pop()] = '\0'; + } + + StringBuilder result = new StringBuilder(); + foreach (char c in arr) { + if (c != '\0') { + result.Append(c); + } + } + + return result.ToString(); + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -512,6 +610,32 @@ class Solution { } ``` +```csharp +public class Solution { + public string MinRemoveToMakeValid(string s) { + int openCnt = 0, closeCnt = 0; + foreach (char c in s) { + if (c == ')') closeCnt++; + } + + StringBuilder res = new StringBuilder(); + foreach (char c in s) { + if (c == '(') { + if (openCnt == closeCnt) continue; + openCnt++; + } else if (c == ')') { + closeCnt--; + if (openCnt == 0) continue; + openCnt--; + } + res.Append(c); + } + + return res.ToString(); + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/number-of-senior-citizens.md b/articles/number-of-senior-citizens.md new file mode 100644 index 000000000..11a6b0d29 --- /dev/null +++ b/articles/number-of-senior-citizens.md @@ -0,0 +1,149 @@ +## 1. String Parsing + +::tabs-start + +```python +class Solution: + def countSeniors(self, details: List[str]) -> int: + res = 0 + for d in details: + if int(d[11:13]) > 60: + res += 1 + return res +``` + +```java +public class Solution { + public int countSeniors(String[] details) { + int res = 0; + for (String d : details) { + if (Integer.parseInt(d.substring(11, 13)) > 60) { + res++; + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int countSeniors(vector& details) { + int res = 0; + for (const string& d : details) { + if (stoi(d.substr(11, 2)) > 60) { + res++; + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} details + * @return {number} + */ + countSeniors(details) { + let res = 0; + for (let d of details) { + if (parseInt(d.slice(11, 13)) > 60) { + res++; + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ + +--- + +## 2. Character-Based Extraction + +::tabs-start + +```python +class Solution: + def countSeniors(self, details: List[str]) -> int: + res = 0 + for d in details: + ten = ord(d[11]) - ord("0") + one = ord(d[12]) - ord("0") + age = one + 10 * ten + if age > 60: + res += 1 + return res +``` + +```java +public class Solution { + public int countSeniors(String[] details) { + int res = 0; + for (String d : details) { + int ten = d.charAt(11) - '0'; + int one = d.charAt(12) - '0'; + int age = one + 10 * ten; + if (age > 60) { + res++; + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int countSeniors(vector& details) { + int res = 0; + for (const string& d : details) { + int ten = d[11] - '0'; + int one = d[12] - '0'; + int age = one + 10 * ten; + if (age > 60) { + res++; + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} details + * @return {number} + */ + countSeniors(details) { + let res = 0; + for (let d of details) { + let ten = d.charCodeAt(11) - 48; + let one = d.charCodeAt(12) - 48; + let age = one + 10 * ten; + if (age > 60) { + res++; + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/number-of-sub-arrays-with-odd-sum.md b/articles/number-of-sub-arrays-with-odd-sum.md new file mode 100644 index 000000000..e13063014 --- /dev/null +++ b/articles/number-of-sub-arrays-with-odd-sum.md @@ -0,0 +1,521 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def numOfSubarrays(self, arr: List[int]) -> int: + n, res = len(arr), 0 + mod = int(1e9 + 7) + + for i in range(n): + curSum = 0 + for j in range(i, n): + curSum += arr[j] + if curSum % 2: + res = (res + 1) % mod + + return res +``` + +```java +public class Solution { + public int numOfSubarrays(int[] arr) { + int n = arr.length, res = 0; + int mod = (int)1e9 + 7; + + for (int i = 0; i < n; i++) { + int curSum = 0; + for (int j = i; j < n; j++) { + curSum += arr[j]; + if (curSum % 2 != 0) { + res = (res + 1) % mod; + } + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int numOfSubarrays(vector& arr) { + int n = arr.size(), res = 0; + int mod = 1e9 + 7; + + for (int i = 0; i < n; i++) { + int curSum = 0; + for (int j = i; j < n; j++) { + curSum += arr[j]; + if (curSum % 2 != 0) { + res = (res + 1) % mod; + } + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} arr + * @return {number} + */ + numOfSubarrays(arr) { + const n = arr.length; + let res = 0; + const mod = 1e9 + 7; + + for (let i = 0; i < n; i++) { + let curSum = 0; + for (let j = i; j < n; j++) { + curSum += arr[j]; + if (curSum % 2 !== 0) { + res = (res + 1) % mod; + } + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(1)$ + +--- + +## 2. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def numOfSubarrays(self, arr: List[int]) -> int: + mod = 10**9 + 7 + n = len(arr) + memo = {} + + def dp(i: int, parity: int) -> int: + if i == n: + return 0 + + if (i, parity) in memo: + return memo[(i, parity)] + + new_parity = (parity + arr[i]) % 2 + res = new_parity + dp(i + 1, new_parity) + memo[(i, parity)] = res % mod + return memo[(i, parity)] + + ans = 0 + for i in range(n): + ans = (ans + dp(i, 0)) % mod + + return ans +``` + +```java +public class Solution { + int[][] memo; + int[] arr; + int mod = (int)1e9 + 7; + + public int numOfSubarrays(int[] arr) { + int n = arr.length; + this.arr = arr; + memo = new int[n][2]; + for (int i = 0; i < n; i++) { + memo[i][0] = -1; + memo[i][1] = -1; + } + + int res = 0; + for (int i = 0; i < n; i++) { + res = (res + dp(i, 0)) % mod; + } + return res; + } + + private int dp(int i, int parity) { + if (i == arr.length) return 0; + if (memo[i][parity] != -1) return memo[i][parity]; + + int newParity = (parity + arr[i]) % 2; + int res = newParity + dp(i + 1, newParity); + return memo[i][parity] = res % mod; + } +} +``` + +```cpp +class Solution { +public: + int mod = 1e9 + 7; + vector> memo; + vector arr; + + int numOfSubarrays(vector& arr) { + this->arr = arr; + int n = arr.size(); + memo.assign(n, vector(2, -1)); + + int res = 0; + for (int i = 0; i < n; i++) { + res = (res + dp(i, 0)) % mod; + } + return res; + } + + int dp(int i, int parity) { + if (i == arr.size()) return 0; + if (memo[i][parity] != -1) return memo[i][parity]; + + int newParity = (parity + arr[i]) % 2; + int res = newParity + dp(i + 1, newParity); + return memo[i][parity] = res % mod; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} arr + * @return {number} + */ + numOfSubarrays(arr) { + const mod = 1e9 + 7; + const n = arr.length; + const memo = Array.from({ length: n }, () => Array(2).fill(-1)); + + const dp = (i, parity) => { + if (i === n) return 0; + if (memo[i][parity] !== -1) return memo[i][parity]; + + const newParity = (parity + arr[i]) % 2; + const res = newParity + dp(i + 1, newParity); + return memo[i][parity] = res % mod; + }; + + let res = 0; + for (let i = 0; i < n; i++) { + res = (res + dp(i, 0)) % mod; + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def numOfSubarrays(self, arr: List[int]) -> int: + n = len(arr) + mod = 10**9 + 7 + dp = [[0] * 2 for _ in range(n + 1)] + + for i in range(n - 1, -1, -1): + for parity in range(2): + new_parity = (parity + arr[i]) % 2 + dp[i][parity] = (new_parity + dp[i + 1][new_parity]) % mod + + res = 0 + for i in range(n): + res = (res + dp[i][0]) % mod + return res +``` + +```java +public class Solution { + public int numOfSubarrays(int[] arr) { + int n = arr.length; + int mod = (int)1e9 + 7; + int[][] dp = new int[n + 1][2]; + + for (int i = n - 1; i >= 0; i--) { + for (int parity = 0; parity <= 1; parity++) { + int newParity = (parity + arr[i]) % 2; + dp[i][parity] = (newParity + dp[i + 1][newParity]) % mod; + } + } + + int res = 0; + for (int i = 0; i < n; i++) { + res = (res + dp[i][0]) % mod; + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int numOfSubarrays(vector& arr) { + int n = arr.size(), mod = 1e9 + 7; + vector> dp(n + 1, vector(2, 0)); + + for (int i = n - 1; i >= 0; i--) { + for (int parity = 0; parity <= 1; parity++) { + int newParity = (parity + arr[i]) % 2; + dp[i][parity] = (newParity + dp[i + 1][newParity]) % mod; + } + } + + int res = 0; + for (int i = 0; i < n; i++) { + res = (res + dp[i][0]) % mod; + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} arr + * @return {number} + */ + numOfSubarrays(arr) { + const n = arr.length; + const mod = 1e9 + 7; + const dp = Array.from({ length: n + 1 }, () => [0, 0]); + + for (let i = n - 1; i >= 0; i--) { + for (let parity = 0; parity <= 1; parity++) { + const newParity = (parity + arr[i]) % 2; + dp[i][parity] = (newParity + dp[i + 1][newParity]) % mod; + } + } + + let res = 0; + for (let i = 0; i < n; i++) { + res = (res + dp[i][0]) % mod; + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 4. Prefix Sum - I + +::tabs-start + +```python +class Solution: + def numOfSubarrays(self, arr: List[int]) -> int: + cur_sum = odd_cnt = even_cnt = res = 0 + MOD = 10**9 + 7 + + for n in arr: + cur_sum += n + if cur_sum % 2: + res = (res + 1 + even_cnt) % MOD + odd_cnt += 1 + else: + res = (res + odd_cnt) % MOD + even_cnt += 1 + + return res +``` + +```java +public class Solution { + public int numOfSubarrays(int[] arr) { + int curSum = 0, oddCnt = 0, evenCnt = 0, res = 0; + int MOD = (int)1e9 + 7; + + for (int n : arr) { + curSum += n; + if (curSum % 2 != 0) { + res = (res + 1 + evenCnt) % MOD; + oddCnt++; + } else { + res = (res + oddCnt) % MOD; + evenCnt++; + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int numOfSubarrays(vector& arr) { + long long curSum = 0, oddCnt = 0, evenCnt = 0, res = 0; + const int MOD = 1e9 + 7; + + for (int n : arr) { + curSum += n; + if (curSum % 2 != 0) { + res = (res + 1 + evenCnt) % MOD; + oddCnt++; + } else { + res = (res + oddCnt) % MOD; + evenCnt++; + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} arr + * @return {number} + */ + numOfSubarrays(arr) { + let curSum = 0, oddCnt = 0, evenCnt = 0, res = 0; + const MOD = 1e9 + 7; + + for (let n of arr) { + curSum += n; + if (curSum % 2 !== 0) { + res = (res + 1 + evenCnt) % MOD; + oddCnt++; + } else { + res = (res + oddCnt) % MOD; + evenCnt++; + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ + +--- + +## 5. Prefix Sum - II + +::tabs-start + +```python +class Solution: + def numOfSubarrays(self, arr: List[int]) -> int: + count = [1, 0] + prefix = res = 0 + MOD = 10**9 + 7 + + for num in arr: + prefix = (prefix + num) % 2 + res = (res + count[1 - prefix]) % MOD + count[prefix] += 1 + + return res +``` + +```java +public class Solution { + public int numOfSubarrays(int[] arr) { + int[] count = {1, 0}; + int prefix = 0, res = 0; + int MOD = (int)1e9 + 7; + + for (int num : arr) { + prefix = (prefix + num) % 2; + res = (res + count[1 - prefix]) % MOD; + count[prefix]++; + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int numOfSubarrays(vector& arr) { + int count[2] = {1, 0}; + int prefix = 0, res = 0; + const int MOD = 1e9 + 7; + + for (int num : arr) { + prefix = (prefix + num) % 2; + res = (res + count[1 - prefix]) % MOD; + count[prefix]++; + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} arr + * @return {number} + */ + numOfSubarrays(arr) { + const count = [1, 0]; + let prefix = 0, res = 0; + const MOD = 1e9 + 7; + + for (const num of arr) { + prefix = (prefix + num) % 2; + res = (res + count[1 - prefix]) % MOD; + count[prefix]++; + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/number-of-ways-to-split-array.md b/articles/number-of-ways-to-split-array.md new file mode 100644 index 000000000..ca66c41c0 --- /dev/null +++ b/articles/number-of-ways-to-split-array.md @@ -0,0 +1,320 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def waysToSplitArray(self, nums: List[int]) -> int: + n = len(nums) + res = 0 + + for i in range(n - 1): + leftSum = 0 + for j in range(i + 1): + leftSum += nums[j] + + rightSum = 0 + for j in range(i + 1, n): + rightSum += nums[j] + + res += (1 if leftSum >= rightSum else 0) + + return res +``` + +```java +public class Solution { + public int waysToSplitArray(int[] nums) { + int n = nums.length; + int res = 0; + + for (int i = 0; i < n - 1; i++) { + long leftSum = 0; + for (int j = 0; j <= i; j++) { + leftSum += nums[j]; + } + + long rightSum = 0; + for (int j = i + 1; j < n; j++) { + rightSum += nums[j]; + } + + if (leftSum >= rightSum) { + res++; + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int waysToSplitArray(vector& nums) { + int n = nums.size(); + int res = 0; + + for (int i = 0; i < n - 1; i++) { + long long leftSum = 0; + for (int j = 0; j <= i; j++) { + leftSum += nums[j]; + } + + long long rightSum = 0; + for (int j = i + 1; j < n; j++) { + rightSum += nums[j]; + } + + if (leftSum >= rightSum) { + res++; + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + waysToSplitArray(nums) { + let n = nums.length; + let res = 0; + + for (let i = 0; i < n - 1; i++) { + let leftSum = 0; + for (let j = 0; j <= i; j++) { + leftSum += nums[j]; + } + + let rightSum = 0; + for (let j = i + 1; j < n; j++) { + rightSum += nums[j]; + } + + if (leftSum >= rightSum) { + res++; + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(1)$ + +--- + +## 2. Prefix Sum + +::tabs-start + +```python +class Solution: + def waysToSplitArray(self, nums: List[int]) -> int: + n = len(nums) + prefix = [0] * (n + 1) + + for i in range(n): + prefix[i + 1] = prefix[i] + nums[i] + + res = 0 + for i in range(1, n): + left = prefix[i] + right = prefix[n] - prefix[i] + if left >= right: + res += 1 + + return res +``` + +```java +public class Solution { + public int waysToSplitArray(int[] nums) { + int n = nums.length; + long[] prefix = new long[n + 1]; + + for (int i = 0; i < n; i++) { + prefix[i + 1] = prefix[i] + nums[i]; + } + + int res = 0; + for (int i = 1; i < n; i++) { + long left = prefix[i]; + long right = prefix[n] - prefix[i]; + if (left >= right) { + res++; + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int waysToSplitArray(vector& nums) { + int n = nums.size(); + vector prefix(n + 1, 0); + + for (int i = 0; i < n; i++) { + prefix[i + 1] = prefix[i] + nums[i]; + } + + int res = 0; + for (int i = 1; i < n; i++) { + long long left = prefix[i]; + long long right = prefix[n] - prefix[i]; + if (left >= right) { + res++; + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + waysToSplitArray(nums) { + const n = nums.length; + const prefix = Array(n + 1).fill(0); + + for (let i = 0; i < n; i++) { + prefix[i + 1] = prefix[i] + nums[i]; + } + + let res = 0; + for (let i = 1; i < n; i++) { + const left = prefix[i]; + const right = prefix[n] - prefix[i]; + if (left >= right) { + res++; + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Prefix Sum (Optimal) + +::tabs-start + +```python +class Solution: + def waysToSplitArray(self, nums: List[int]) -> int: + right = sum(nums) + left = res = 0 + + for i in range(len(nums) - 1): + left += nums[i] + right -= nums[i] + res += 1 if left >= right else 0 + + return res +``` + +```java +public class Solution { + public int waysToSplitArray(int[] nums) { + long right = 0, left = 0; + for (int num : nums) { + right += num; + } + + int res = 0; + for (int i = 0; i < nums.length - 1; i++) { + left += nums[i]; + right -= nums[i]; + if (left >= right) { + res++; + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int waysToSplitArray(vector& nums) { + long long right = 0, left = 0; + for (int num : nums) { + right += num; + } + + int res = 0; + for (int i = 0; i < nums.size() - 1; i++) { + left += nums[i]; + right -= nums[i]; + if (left >= right) { + res++; + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + waysToSplitArray(nums) { + let right = nums.reduce((a, b) => a + b, 0); + let left = 0, res = 0; + + for (let i = 0; i < nums.length - 1; i++) { + left += nums[i]; + right -= nums[i]; + if (left >= right) { + res++; + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/relative-sort-array.md b/articles/relative-sort-array.md new file mode 100644 index 000000000..8681e3fba --- /dev/null +++ b/articles/relative-sort-array.md @@ -0,0 +1,551 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def relativeSortArray(self, arr1: List[int], arr2: List[int]) -> List[int]: + res = [] + + for num2 in arr2: + for i, num1 in enumerate(arr1): + if num1 == num2: + res.append(num1) + arr1[i] = -1 + + arr1.sort() + for i in range(len(res), len(arr1)): + res.append(arr1[i]) + + return res +``` + +```java +public class Solution { + public int[] relativeSortArray(int[] arr1, int[] arr2) { + List res = new ArrayList<>(); + + for (int num2 : arr2) { + for (int i = 0; i < arr1.length; i++) { + if (arr1[i] == num2) { + res.add(arr1[i]); + arr1[i] = -1; + } + } + } + + Arrays.sort(arr1); + for (int i = res.size(); i < arr1.length; i++) { + res.add(arr1[i]); + } + + return res.stream().mapToInt(i -> i).toArray(); + } +} +``` + +```cpp +class Solution { +public: + vector relativeSortArray(vector& arr1, vector& arr2) { + vector res; + + for (int num2 : arr2) { + for (int i = 0; i < arr1.size(); i++) { + if (arr1[i] == num2) { + res.push_back(arr1[i]); + arr1[i] = -1; + } + } + } + + sort(arr1.begin(), arr1.end()); + for (int i = res.size(); i < arr1.size(); i++) { + res.push_back(arr1[i]); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} arr1 + * @param {number[]} arr2 + * @return {number[]} + */ + relativeSortArray(arr1, arr2) { + const res = []; + + for (let num2 of arr2) { + for (let i = 0; i < arr1.length; i++) { + if (arr1[i] === num2) { + res.push(arr1[i]); + arr1[i] = -1; + } + } + } + + arr1.sort((a, b) => a - b); + for (let i = res.length; i < arr1.length; i++) { + res.push(arr1[i]); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n + n \log n)$ +* Space complexity: + * $O(1)$ or $O(n)$ depending on the sorting algorithm. + * $O(n)$ space for the output list. + +> Where $n$ is the size of the array $arr1$, and $m$ is the size of the array $arr2$. + +--- + +## 2. Hash Map + +::tabs-start + +```python +class Solution: + def relativeSortArray(self, arr1: List[int], arr2: List[int]) -> List[int]: + arr2_set = set(arr2) + arr1_count = defaultdict(int) + end = [] + + for num in arr1: + if num not in arr2_set: + end.append(num) + arr1_count[num] += 1 + end.sort() + + res = [] + for num in arr2: + for _ in range(arr1_count[num]): + res.append(num) + return res + end +``` + +```java +public class Solution { + public int[] relativeSortArray(int[] arr1, int[] arr2) { + Set arr2Set = new HashSet<>(); + for (int num : arr2) arr2Set.add(num); + + Map count = new HashMap<>(); + List end = new ArrayList<>(); + for (int num : arr1) { + if (!arr2Set.contains(num)) end.add(num); + count.put(num, count.getOrDefault(num, 0) + 1); + } + Collections.sort(end); + + List res = new ArrayList<>(); + for (int num : arr2) { + int freq = count.get(num); + for (int i = 0; i < freq; i++) res.add(num); + } + res.addAll(end); + + return res.stream().mapToInt(i -> i).toArray(); + } +} +``` + +```cpp +class Solution { +public: + vector relativeSortArray(vector& arr1, vector& arr2) { + unordered_set arr2Set(arr2.begin(), arr2.end()); + unordered_map count; + vector end; + + for (int num : arr1) { + if (!arr2Set.count(num)) end.push_back(num); + count[num]++; + } + + sort(end.begin(), end.end()); + vector res; + + for (int num : arr2) { + for (int i = 0; i < count[num]; i++) { + res.push_back(num); + } + } + + res.insert(res.end(), end.begin(), end.end()); + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} arr1 + * @param {number[]} arr2 + * @return {number[]} + */ + relativeSortArray(arr1, arr2) { + const arr2Set = new Set(arr2); + const count = {}; + const end = []; + + for (let num of arr1) { + if (!arr2Set.has(num)) end.push(num); + count[num] = (count[num] || 0) + 1; + } + + end.sort((a, b) => a - b); + const res = []; + + for (let num of arr2) { + for (let i = 0; i < count[num]; i++) { + res.push(num); + } + } + + return res.concat(end); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n + m + n \log n)$ +* Space complexity: $O(n)$ + +> Where $n$ is the size of the array $arr1$, and $m$ is the size of the array $arr2$. + +--- + +## 3. Hash Map (Optimal) + +::tabs-start + +```python +class Solution: + def relativeSortArray(self, arr1: List[int], arr2: List[int]) -> List[int]: + count = {} + for num in arr1: + count[num] = count.get(num, 0) + 1 + + res = [] + for num in arr2: + res += [num] * count.pop(num) + + for num in sorted(count): + res += [num] * count[num] + + return res +``` + +```java +public class Solution { + public int[] relativeSortArray(int[] arr1, int[] arr2) { + Map count = new HashMap<>(); + for (int num : arr1) { + count.put(num, count.getOrDefault(num, 0) + 1); + } + + List res = new ArrayList<>(); + for (int num : arr2) { + int freq = count.remove(num); + for (int i = 0; i < freq; i++) { + res.add(num); + } + } + + List remaining = new ArrayList<>(count.keySet()); + Collections.sort(remaining); + for (int num : remaining) { + int freq = count.get(num); + for (int i = 0; i < freq; i++) { + res.add(num); + } + } + + return res.stream().mapToInt(i -> i).toArray(); + } +} +``` + +```cpp +class Solution { +public: + vector relativeSortArray(vector& arr1, vector& arr2) { + unordered_map count; + for (int num : arr1) { + count[num]++; + } + + vector res; + for (int num : arr2) { + for (int i = 0; i < count[num]; i++) { + res.push_back(num); + } + count.erase(num); + } + + vector remaining; + for (auto& [num, freq] : count) { + for (int i = 0; i < freq; i++) { + remaining.push_back(num); + } + } + + sort(remaining.begin(), remaining.end()); + res.insert(res.end(), remaining.begin(), remaining.end()); + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} arr1 + * @param {number[]} arr2 + * @return {number[]} + */ + relativeSortArray(arr1, arr2) { + const count = {}; + for (let num of arr1) { + count[num] = (count[num] || 0) + 1; + } + + const res = []; + for (let num of arr2) { + for (let i = 0; i < count[num]; i++) { + res.push(num); + } + delete count[num]; + } + + const remaining = Object.keys(count).map(Number).sort((a, b) => a - b); + for (let num of remaining) { + for (let i = 0; i < count[num]; i++) { + res.push(num); + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n + m + n \log n)$ +* Space complexity: $O(n)$ + +> Where $n$ is the size of the array $arr1$, and $m$ is the size of the array $arr2$. + +--- + +## 4. Counting Sort + +::tabs-start + +```python +class Solution: + def relativeSortArray(self, arr1: List[int], arr2: List[int]) -> List[int]: + max_val = max(arr1) + count = [0] * (max_val + 1) + + for num in arr1: + count[num] += 1 + + res = [] + for num in arr2: + res += [num] * count[num] + count[num] = 0 + + for num in range(len(count)): + res += [num] * count[num] + + return res +``` + +```java +public class Solution { + public int[] relativeSortArray(int[] arr1, int[] arr2) { + int max = 0; + for (int num : arr1) max = Math.max(max, num); + + int[] count = new int[max + 1]; + for (int num : arr1) count[num]++; + + List res = new ArrayList<>(); + for (int num : arr2) { + while (count[num]-- > 0) res.add(num); + } + + for (int num = 0; num < count.length; num++) { + while (count[num]-- > 0) res.add(num); + } + + return res.stream().mapToInt(i -> i).toArray(); + } +} +``` + +```cpp +class Solution { +public: + vector relativeSortArray(vector& arr1, vector& arr2) { + int max_val = *max_element(arr1.begin(), arr1.end()); + vector count(max_val + 1, 0); + + for (int num : arr1) count[num]++; + + vector res; + for (int num : arr2) { + while (count[num]-- > 0) res.push_back(num); + } + + for (int num = 0; num <= max_val; num++) { + while (count[num]-- > 0) res.push_back(num); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} arr1 + * @param {number[]} arr2 + * @return {number[]} + */ + relativeSortArray(arr1, arr2) { + let max = Math.max(...arr1); + let count = new Array(max + 1).fill(0); + + for (let num of arr1) count[num]++; + + let res = []; + for (let num of arr2) { + while (count[num]-- > 0) res.push(num); + } + + for (let num = 0; num < count.length; num++) { + while (count[num]-- > 0) res.push(num); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n + m + M)$ +* Space complexity: + * $O(M)$ extra space. + * $O(n)$ space for the output list. + +> Where $n$ is the size of the array $arr1$, $m$ is the size of the array $arr2$, and $M$ is the maximum value in the array $arr1$. + +--- + +## 5. Custom Sort + +::tabs-start + +```python +class Solution: + def relativeSortArray(self, arr1: List[int], arr2: List[int]) -> List[int]: + index = {num: i for i, num in enumerate(arr2)} + return sorted(arr1, key=lambda x: (index.get(x, 1000 + x))) +``` + +```java +public class Solution { + public int[] relativeSortArray(int[] arr1, int[] arr2) { + Map index = new HashMap<>(); + for (int i = 0; i < arr2.length; i++) { + index.put(arr2[i], i); + } + + Integer[] boxed = Arrays.stream(arr1).boxed().toArray(Integer[]::new); + Arrays.sort(boxed, (a, b) -> { + int ia = index.getOrDefault(a, 1000 + a); + int ib = index.getOrDefault(b, 1000 + b); + return Integer.compare(ia, ib); + }); + + return Arrays.stream(boxed).mapToInt(i -> i).toArray(); + } +} +``` + +```cpp +class Solution { +public: + vector relativeSortArray(vector& arr1, vector& arr2) { + unordered_map index; + for (int i = 0; i < arr2.size(); i++) { + index[arr2[i]] = i; + } + + sort(arr1.begin(), arr1.end(), [&](int a, int b) { + int ia = index.count(a) ? index[a] : 1000 + a; + int ib = index.count(b) ? index[b] : 1000 + b; + return ia < ib; + }); + + return arr1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} arr1 + * @param {number[]} arr2 + * @return {number[]} + */ + relativeSortArray(arr1, arr2) { + const index = new Map(); + arr2.forEach((num, i) => index.set(num, i)); + + return arr1.sort((a, b) => { + const ia = index.has(a) ? index.get(a) : 1000 + a; + const ib = index.has(b) ? index.get(b) : 1000 + b; + return ia - ib; + }); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n + m + n \log n)$ +* Space complexity: + * $O(m)$ extra space. + * $O(n)$ space for the output list. + +> Where $n$ is the size of the array $arr1$, and $m$ is the size of the array $arr2$. \ No newline at end of file diff --git a/articles/remove-duplicates-from-sorted-list.md b/articles/remove-duplicates-from-sorted-list.md index b50bbbdcb..9184c3e9c 100644 --- a/articles/remove-duplicates-from-sorted-list.md +++ b/articles/remove-duplicates-from-sorted-list.md @@ -88,7 +88,7 @@ class Solution { ### Time & Space Complexity -* Time complexity: $O(2 ^ n)$ +* Time complexity: $O(n)$ * Space complexity: $O(n)$ for recursion stack. --- diff --git a/articles/score-of-a-string.md b/articles/score-of-a-string.md new file mode 100644 index 000000000..9a5bf9bdf --- /dev/null +++ b/articles/score-of-a-string.md @@ -0,0 +1,60 @@ +## 1. Iteration + +::tabs-start + +```python +class Solution: + def scoreOfString(self, s: str) -> int: + res = 0 + for i in range(len(s) - 1): + res += abs(ord(s[i]) - ord(s[i + 1])) + return res +``` + +```java +public class Solution { + public int scoreOfString(String s) { + int res = 0; + for (int i = 0; i < s.length() - 1; i++) { + res += Math.abs(s.charAt(i) - s.charAt(i + 1)); + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int scoreOfString(string s) { + int res = 0; + for (int i = 0; i < s.length() - 1; i++) { + res += abs(s[i] - s[i + 1]); + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {number} + */ + scoreOfString(s) { + let res = 0; + for (let i = 0; i < s.length - 1; i++) { + res += Math.abs(s.charCodeAt(i) - s.charCodeAt(i + 1)); + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/shifting-letters-ii.md b/articles/shifting-letters-ii.md new file mode 100644 index 000000000..12d3e9499 --- /dev/null +++ b/articles/shifting-letters-ii.md @@ -0,0 +1,481 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def shiftingLetters(self, s: str, shifts: List[List[int]]) -> str: + s = [ord(c) - ord('a') for c in s] + + for l, r, d in shifts: + for i in range(l, r + 1): + s[i] += 1 if d else -1 + s[i] %= 26 + + s = [chr(ord('a') + c) for c in s] + return "".join(s) +``` + +```java +class Solution { + public String shiftingLetters(String s, int[][] shifts) { + char[] arr = s.toCharArray(); + int[] letters = new int[arr.length]; + + for (int i = 0; i < arr.length; i++) { + letters[i] = arr[i] - 'a'; + } + + for (int[] shift : shifts) { + int l = shift[0], r = shift[1], d = shift[2]; + for (int i = l; i <= r; i++) { + letters[i] = (letters[i] + (d == 1 ? 1 : -1) + 26) % 26; + } + } + + for (int i = 0; i < arr.length; i++) { + arr[i] = (char) (letters[i] + 'a'); + } + + return new String(arr); + } +} +``` + +```cpp +class Solution { +public: + string shiftingLetters(string s, vector>& shifts) { + vector letters(s.size()); + for (int i = 0; i < s.size(); i++) { + letters[i] = s[i] - 'a'; + } + + for (const auto& shift : shifts) { + int l = shift[0], r = shift[1], d = shift[2]; + for (int i = l; i <= r; i++) { + letters[i] = (letters[i] + (d == 1 ? 1 : -1) + 26) % 26; + } + } + + for (int i = 0; i < s.size(); i++) { + s[i] = letters[i] + 'a'; + } + + return s; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @param {number[][]} shifts + * @return {string} + */ + shiftingLetters(s, shifts) { + let arr = Array.from(s).map(c => c.charCodeAt(0) - 97); + + for (const [l, r, d] of shifts) { + for (let i = l; i <= r; i++) { + arr[i] = (arr[i] + (d === 1 ? 1 : -1) + 26) % 26; + } + } + + return arr.map(c => String.fromCharCode(c + 97)).join(''); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * m)$ +* Space complexity: $O(n)$ + +> Where $n$ is the length of the string $s$ and $m$ is the size of the array $shifts$. + +--- + +## 2. Sweep Line Algorithm + +::tabs-start + +```python +class Solution: + def shiftingLetters(self, s: str, shifts: List[List[int]]) -> str: + prefix_diff = [0] * (len(s) + 1) + + for left, right, d in shifts: + val = 1 if d == 1 else -1 + prefix_diff[left] += val + prefix_diff[right + 1] -= val + + diff = 0 + res = [ord(c) - ord("a") for c in s] + + for i in range(len(s)): + diff += prefix_diff[i] + res[i] = (diff + res[i] + 26) % 26 + + s = [chr(ord("a") + n) for n in res] + return "".join(s) +``` + +```java +class Solution { + public String shiftingLetters(String s, int[][] shifts) { + int n = s.length(); + int[] prefix_diff = new int[n + 1]; + + for (int[] shift : shifts) { + int left = shift[0], right = shift[1], d = shift[2]; + int val = d == 1 ? 1 : -1; + prefix_diff[left] += val; + prefix_diff[right + 1] -= val; + } + + int[] res = new int[n]; + for (int i = 0; i < n; i++) { + res[i] = s.charAt(i) - 'a'; + } + + int diff = 0; + for (int i = 0; i < n; i++) { + diff += prefix_diff[i]; + res[i] = (res[i] + diff % 26 + 26) % 26; + } + + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < n; i++) { + sb.append((char) ('a' + res[i])); + } + + return sb.toString(); + } +} +``` + +```cpp +class Solution { +public: + string shiftingLetters(string s, vector>& shifts) { + int n = s.size(); + vector prefix_diff(n + 1, 0); + + for (auto& shift : shifts) { + int left = shift[0], right = shift[1], d = shift[2]; + int val = d == 1 ? 1 : -1; + prefix_diff[left] += val; + prefix_diff[right + 1] -= val; + } + + int diff = 0; + vector res(n); + for (int i = 0; i < n; ++i) { + res[i] = s[i] - 'a'; + } + + for (int i = 0; i < n; ++i) { + diff += prefix_diff[i]; + res[i] = (diff % 26 + res[i] + 26) % 26; + } + + for (int i = 0; i < n; ++i) { + s[i] = 'a' + res[i]; + } + + return s; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @param {number[][]} shifts + * @return {string} + */ + shiftingLetters(s, shifts) { + const n = s.length; + const prefix_diff = Array(n + 1).fill(0); + + for (const [left, right, d] of shifts) { + const val = d === 1 ? 1 : -1; + prefix_diff[left] += val; + prefix_diff[right + 1] -= val; + } + + let diff = 0; + const res = Array.from(s).map(c => c.charCodeAt(0) - 'a'.charCodeAt(0)); + + for (let i = 0; i < n; i++) { + diff += prefix_diff[i]; + res[i] = (diff % 26 + res[i] + 26) % 26; + } + + return res.map(x => String.fromCharCode('a'.charCodeAt(0) + x)).join(''); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n + m)$ +* Space complexity: $O(n)$ + +> Where $n$ is the length of the string $s$ and $m$ is the size of the array $shifts$. + +--- + +## 3. Binary Indexed Tree (Fenwick Tree) + +::tabs-start + +```python +class BIT: + def __init__(self, size): + self.n = size + 2 + self.tree = [0] * self.n + + def update(self, index, delta): + index += 1 + while index < self.n: + self.tree[index] += delta + index += index & -index + + def prefix_sum(self, index): + index += 1 + total = 0 + while index > 0: + total += self.tree[index] + index -= index & -index + return total + + def range_update(self, left, right, delta): + self.update(left, delta) + self.update(right + 1, -delta) + + +class Solution: + def shiftingLetters(self, s: str, shifts: List[List[int]]) -> str: + n = len(s) + bit = BIT(n) + + for left, right, d in shifts: + delta = 1 if d == 1 else -1 + bit.range_update(left, right, delta) + + res = [] + for i in range(n): + shift = bit.prefix_sum(i) % 26 + code = (ord(s[i]) - ord('a') + shift + 26) % 26 + res.append(chr(ord('a') + code)) + + return ''.join(res) +``` + +```java +class BIT { + int[] tree; + int n; + + public BIT(int size) { + n = size + 2; + tree = new int[n]; + } + + public void update(int index, int delta) { + index++; + while (index < n) { + tree[index] += delta; + index += index & -index; + } + } + + public int prefixSum(int index) { + index++; + int sum = 0; + while (index > 0) { + sum += tree[index]; + index -= index & -index; + } + return sum; + } + + public void rangeUpdate(int left, int right, int delta) { + update(left, delta); + update(right + 1, -delta); + } +} + +public class Solution { + public String shiftingLetters(String s, int[][] shifts) { + int n = s.length(); + BIT bit = new BIT(n); + + for (int[] shift : shifts) { + int left = shift[0], right = shift[1], d = shift[2]; + int delta = d == 1 ? 1 : -1; + bit.rangeUpdate(left, right, delta); + } + + StringBuilder res = new StringBuilder(); + for (int i = 0; i < n; i++) { + int shift = bit.prefixSum(i) % 26; + int code = (s.charAt(i) - 'a' + shift + 26) % 26; + res.append((char) ('a' + code)); + } + + return res.toString(); + } +} +``` + +```cpp +class BIT { + vector tree; + int n; +public: + BIT(int size) { + n = size + 2; + tree.assign(n, 0); + } + + void update(int index, int delta) { + index++; + while (index < n) { + tree[index] += delta; + index += index & -index; + } + } + + int prefixSum(int index) { + index++; + int sum = 0; + while (index > 0) { + sum += tree[index]; + index -= index & -index; + } + return sum; + } + + void rangeUpdate(int left, int right, int delta) { + update(left, delta); + update(right + 1, -delta); + } +}; + +class Solution { +public: + string shiftingLetters(string s, vector>& shifts) { + int n = s.size(); + BIT bit(n); + + for (auto& shift : shifts) { + int left = shift[0], right = shift[1], d = shift[2]; + int delta = d == 1 ? 1 : -1; + bit.rangeUpdate(left, right, delta); + } + + string res; + for (int i = 0; i < n; i++) { + int shift = bit.prefixSum(i) % 26; + int code = (s[i] - 'a' + shift + 26) % 26; + res += char('a' + code); + } + + return res; + } +}; +``` + +```javascript +class BIT { + /** + * @constructor + * @param {number} size + */ + constructor(size) { + this.n = size + 2; + this.tree = new Array(this.n).fill(0); + } + + /** + * @param {number} index + * @param {number} delta + * @return {void} + */ + update(index, delta) { + index++; + while (index < this.n) { + this.tree[index] += delta; + index += index & -index; + } + } + + /** + * @param {number} index + * @return {number} + */ + prefixSum(index) { + index++; + let sum = 0; + while (index > 0) { + sum += this.tree[index]; + index -= index & -index; + } + return sum; + } + + /** + * @param {number} left + * @param {number} right + * @param {number} delta + * @return {void} + */ + rangeUpdate(left, right, delta) { + this.update(left, delta); + this.update(right + 1, -delta); + } +} + +class Solution { + /** + * @param {string} s + * @param {number[][]} shifts + * @return {string} + */ + shiftingLetters(s, shifts) { + const n = s.length; + const bit = new BIT(n); + for (const [left, right, d] of shifts) { + const delta = d === 1 ? 1 : -1; + bit.rangeUpdate(left, right, delta); + } + + let res = ""; + for (let i = 0; i < n; i++) { + const shift = bit.prefixSum(i) % 26; + const code = (s.charCodeAt(i) - 97 + shift + 26) % 26; + res += String.fromCharCode(97 + code); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O((m + n) * \log n)$ +* Space complexity: $O(n)$ + +> Where $n$ is the length of the string $s$ and $m$ is the size of the array $shifts$. \ No newline at end of file diff --git a/articles/sort-array-by-increasing-frequency.md b/articles/sort-array-by-increasing-frequency.md new file mode 100644 index 000000000..04a4e1c80 --- /dev/null +++ b/articles/sort-array-by-increasing-frequency.md @@ -0,0 +1,79 @@ +## 1. Custom Sort + +::tabs-start + +```python +class Solution: + def frequencySort(self, nums: List[int]) -> List[int]: + count = Counter(nums) + nums.sort(key=lambda n: (count[n], -n)) + return nums +``` + +```java +public class Solution { + public int[] frequencySort(int[] nums) { + Map count = new HashMap<>(); + for (int num : nums) { + count.put(num, count.getOrDefault(num, 0) + 1); + } + + Integer[] arr = Arrays.stream(nums).boxed().toArray(Integer[]::new); + Arrays.sort(arr, (a, b) -> { + int freqA = count.get(a), freqB = count.get(b); + if (freqA != freqB) return Integer.compare(freqA, freqB); + return Integer.compare(b, a); + }); + + return Arrays.stream(arr).mapToInt(i -> i).toArray(); + } +} +``` + +```cpp +class Solution { +public: + vector frequencySort(vector& nums) { + unordered_map count; + for (int num : nums) { + count[num]++; + } + + sort(nums.begin(), nums.end(), [&](int a, int b) { + if (count[a] != count[b]) return count[a] < count[b]; + return a > b; + }); + + return nums; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number[]} + */ + frequencySort(nums) { + const count = {}; + for (let num of nums) { + count[num] = (count[num] || 0) + 1; + } + + nums.sort((a, b) => { + if (count[a] !== count[b]) return count[a] - count[b]; + return b - a; + }); + + return nums; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/sort-the-jumbled-numbers.md b/articles/sort-the-jumbled-numbers.md new file mode 100644 index 000000000..b8c0cbf47 --- /dev/null +++ b/articles/sort-the-jumbled-numbers.md @@ -0,0 +1,244 @@ +## 1. Convert To Strings + Sorting + +::tabs-start + +```python +class Solution: + def sortJumbled(self, mapping: List[int], nums: List[int]) -> List[int]: + pairs = [] + + for i, n in enumerate(nums): + n = str(n) + mapped_n = 0 + for c in n: + mapped_n *= 10 + mapped_n += mapping[int(c)] + pairs.append((mapped_n, i)) + + pairs.sort() + return [nums[p[1]] for p in pairs] +``` + +```java +public class Solution { + public int[] sortJumbled(int[] mapping, int[] nums) { + int n = nums.length; + int[][] pairs = new int[n][2]; + + for (int i = 0; i < n; i++) { + String numStr = String.valueOf(nums[i]); + int mapped_n = 0; + for (char c : numStr.toCharArray()) { + mapped_n = mapped_n * 10 + mapping[c - '0']; + } + pairs[i][0] = mapped_n; + pairs[i][1] = i; + } + + Arrays.sort(pairs, (a, b) -> a[0] - b[0]); + + int[] res = new int[n]; + for (int i = 0; i < n; i++) { + res[i] = nums[pairs[i][1]]; + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector sortJumbled(vector& mapping, vector& nums) { + vector> pairs; + + for (int i = 0; i < nums.size(); ++i) { + string numStr = to_string(nums[i]); + int mapped_n = 0; + for (char c : numStr) { + mapped_n = mapped_n * 10 + mapping[c - '0']; + } + pairs.push_back({mapped_n, i}); + } + + sort(pairs.begin(), pairs.end()); + + vector res; + for (auto& p : pairs) { + res.push_back(nums[p.second]); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} mapping + * @param {number[]} nums + * @return {number[]} + */ + sortJumbled(mapping, nums) { + let pairs = []; + + for (let i = 0; i < nums.length; i++) { + let numStr = nums[i].toString(); + let mapped_n = 0; + for (let c of numStr) { + mapped_n = mapped_n * 10 + mapping[parseInt(c)]; + } + pairs.push([mapped_n, i]); + } + + pairs.sort((a, b) => a[0] - b[0]); + + return pairs.map(p => nums[p[1]]); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Iterate On Numbers + Sorting + +::tabs-start + +```python +class Solution: + def sortJumbled(self, mapping: List[int], nums: List[int]) -> List[int]: + pairs = [] + + for i, n in enumerate(nums): + mapped_n = 0 + base = 1 + + if n == 0: + mapped_n = mapping[0] + else: + while n > 0: + digit = n % 10 + n //= 10 + mapped_n += base * mapping[digit] + base *= 10 + + pairs.append((mapped_n, i)) + + pairs.sort() + return [nums[p[1]] for p in pairs] +``` + +```java +public class Solution { + public int[] sortJumbled(int[] mapping, int[] nums) { + int n = nums.length; + int[][] pairs = new int[n][2]; + + for (int i = 0; i < n; i++) { + int mapped_n = 0, base = 1; + int num = nums[i]; + + if (num == 0) { + mapped_n = mapping[0]; + } else { + while (num > 0) { + int digit = num % 10; + num /= 10; + mapped_n += base * mapping[digit]; + base *= 10; + } + } + + pairs[i][0] = mapped_n; + pairs[i][1] = i; + } + + Arrays.sort(pairs, (a, b) -> Integer.compare(a[0], b[0])); + + int[] res = new int[n]; + for (int i = 0; i < n; i++) { + res[i] = nums[pairs[i][1]]; + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector sortJumbled(vector& mapping, vector& nums) { + vector> pairs; + + for (int i = 0; i < nums.size(); i++) { + int mapped_n = 0, base = 1; + int num = nums[i]; + + if (num == 0) { + mapped_n = mapping[0]; + } else { + while (num > 0) { + int digit = num % 10; + num /= 10; + mapped_n += base * mapping[digit]; + base *= 10; + } + } + + pairs.push_back({mapped_n, i}); + } + + sort(pairs.begin(), pairs.end()); + + vector res; + for (auto& p : pairs) { + res.push_back(nums[p.second]); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} mapping + * @param {number[]} nums + * @return {number[]} + */ + sortJumbled(mapping, nums) { + let pairs = []; + + for (let i = 0; i < nums.length; i++) { + let numStr = nums[i].toString(); + let mapped_n = 0; + for (let c of numStr) { + mapped_n = mapped_n * 10 + mapping[parseInt(c)]; + } + pairs.push([mapped_n, i]); + } + + pairs.sort((a, b) => a[0] - b[0]); + + return pairs.map(p => nums[p[1]]); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/sort-the-people.md b/articles/sort-the-people.md new file mode 100644 index 000000000..c5d8ca911 --- /dev/null +++ b/articles/sort-the-people.md @@ -0,0 +1,256 @@ +## 1. Hash Map + +::tabs-start + +```python +class Solution: + def sortPeople(self, names: List[str], heights: List[int]) -> List[str]: + height_to_name = {} + for h, n in zip(heights, names): + height_to_name[h] = n + + res = [] + for h in reversed(sorted(heights)): + res.append(height_to_name[h]) + + return res +``` + +```java +public class Solution { + public String[] sortPeople(String[] names, int[] heights) { + Map map = new HashMap<>(); + for (int i = 0; i < heights.length; i++) { + map.put(heights[i], names[i]); + } + + Arrays.sort(heights); + String[] res = new String[heights.length]; + for (int i = 0; i < heights.length; i++) { + res[i] = map.get(heights[heights.length - 1 - i]); + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector sortPeople(vector& names, vector& heights) { + unordered_map map; + for (int i = 0; i < heights.size(); i++) { + map[heights[i]] = names[i]; + } + + sort(heights.begin(), heights.end()); + vector res; + for (int i = heights.size() - 1; i >= 0; i--) { + res.push_back(map[heights[i]]); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} names + * @param {number[]} heights + * @return {string[]} + */ + sortPeople(names, heights) { + const map = {}; + for (let i = 0; i < heights.length; i++) { + map[heights[i]] = names[i]; + } + + heights.sort((a, b) => a - b); + const res = []; + for (let i = heights.length - 1; i >= 0; i--) { + res.push(map[heights[i]]); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Sorting the Pairs + +::tabs-start + +```python +class Solution: + def sortPeople(self, names: List[str], heights: List[int]) -> List[str]: + arr = list(zip(heights, names)) + arr.sort(reverse=True) + return [name for _, name in arr] +``` + +```java +public class Solution { + public String[] sortPeople(String[] names, int[] heights) { + int n = names.length; + Pair[] arr = new Pair[n]; + + for (int i = 0; i < n; i++) { + arr[i] = new Pair(heights[i], names[i]); + } + + Arrays.sort(arr, (a, b) -> Integer.compare(b.height, a.height)); + + String[] res = new String[n]; + for (int i = 0; i < n; i++) { + res[i] = arr[i].name; + } + + return res; + } + + static class Pair { + int height; + String name; + + Pair(int height, String name) { + this.height = height; + this.name = name; + } + } +} +``` + +```cpp +class Solution { +public: + vector sortPeople(vector& names, vector& heights) { + vector> arr; + for (int i = 0; i < names.size(); i++) { + arr.emplace_back(heights[i], names[i]); + } + + sort(arr.begin(), arr.end(), [](auto& a, auto& b) { + return a.first > b.first; + }); + + vector res; + for (auto& [_, name] : arr) { + res.push_back(name); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} names + * @param {number[]} heights + * @return {string[]} + */ + sortPeople(names, heights) { + const arr = names.map((name, i) => [heights[i], name]); + arr.sort((a, b) => b[0] - a[0]); + return arr.map(pair => pair[1]); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Sorting the Indices + +::tabs-start + +```python +class Solution: + def sortPeople(self, names: List[str], heights: List[int]) -> List[str]: + indices = list(range(len(names))) + indices.sort(key=lambda i: -heights[i]) + return [names[i] for i in indices] +``` + +```java +public class Solution { + public String[] sortPeople(String[] names, int[] heights) { + Integer[] indices = new Integer[names.length]; + for (int i = 0; i < names.length; i++) { + indices[i] = i; + } + + Arrays.sort(indices, (i, j) -> Integer.compare(heights[j], heights[i])); + + String[] res = new String[names.length]; + for (int i = 0; i < names.length; i++) { + res[i] = names[indices[i]]; + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector sortPeople(vector& names, vector& heights) { + int n = names.size(); + vector indices(n); + iota(indices.begin(), indices.end(), 0); + + sort(indices.begin(), indices.end(), [&](int a, int b) { + return heights[a] > heights[b]; + }); + + vector res; + for (int i : indices) { + res.push_back(names[i]); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} names + * @param {number[]} heights + * @return {string[]} + */ + sortPeople(names, heights) { + const indices = names.map((_, i) => i); + indices.sort((a, b) => heights[b] - heights[a]); + return indices.map(i => names[i]); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/special-array-i.md b/articles/special-array-i.md new file mode 100644 index 000000000..40ccd36c6 --- /dev/null +++ b/articles/special-array-i.md @@ -0,0 +1,129 @@ +## 1. Modulo Comparision + +::tabs-start + +```python +class Solution: + def isArraySpecial(self, nums: List[int]) -> bool: + for i in range(1, len(nums)): + if nums[i - 1] % 2 == nums[i] % 2: + return False + return True +``` + +```java +public class Solution { + public boolean isArraySpecial(int[] nums) { + for (int i = 1; i < nums.length; i++) { + if (nums[i - 1] % 2 == nums[i] % 2) { + return false; + } + } + return true; + } +} +``` + +```cpp +class Solution { +public: + bool isArraySpecial(vector& nums) { + for (int i = 1; i < nums.size(); i++) { + if (nums[i - 1] % 2 == nums[i] % 2) { + return false; + } + } + return true; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {boolean} + */ + isArraySpecial(nums) { + for (let i = 1; i < nums.length; i++) { + if (nums[i - 1] % 2 === nums[i] % 2) { + return false; + } + } + return true; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ + +--- + +## 2. Bitwise Comparision + +::tabs-start + +```python +class Solution: + def isArraySpecial(self, nums: List[int]) -> bool: + for i in range(1, len(nums)): + if nums[i - 1] & 1 == nums[i] & 1: + return False + return True +``` + +```java +public class Solution { + public boolean isArraySpecial(int[] nums) { + for (int i = 1; i < nums.length; i++) { + if ((nums[i - 1] & 1) == (nums[i] & 1)) { + return false; + } + } + return true; + } +} +``` + +```cpp +class Solution { +public: + bool isArraySpecial(vector& nums) { + for (int i = 1; i < nums.size(); i++) { + if ((nums[i - 1] & 1) == (nums[i] & 1)) { + return false; + } + } + return true; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {boolean} + */ + isArraySpecial(nums) { + for (let i = 1; i < nums.length; i++) { + if ((nums[i - 1] & 1) === (nums[i] & 1)) { + return false; + } + } + return true; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/string-matching-in-an-array.md b/articles/string-matching-in-an-array.md new file mode 100644 index 000000000..f369be41c --- /dev/null +++ b/articles/string-matching-in-an-array.md @@ -0,0 +1,1157 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def stringMatching(self, words: List[str]) -> List[str]: + res = [] + + for i in range(len(words)): + for j in range(len(words)): + if i == j: + continue + + if words[i] in words[j]: + res.append(words[i]) + break + + return res +``` + +```java +public class Solution { + public List stringMatching(String[] words) { + List res = new ArrayList<>(); + + for (int i = 0; i < words.length; i++) { + for (int j = 0; j < words.length; j++) { + if (i == j) { + continue; + } + + if (words[j].contains(words[i])) { + res.add(words[i]); + break; + } + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector stringMatching(vector& words) { + vector res; + + for (int i = 0; i < words.size(); i++) { + for (int j = 0; j < words.size(); j++) { + if (i == j) { + continue; + } + + if (words[j].find(words[i]) != string::npos) { + res.push_back(words[i]); + break; + } + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} words + * @return {string[]} + */ + stringMatching(words) { + let res = []; + + for (let i = 0; i < words.length; i++) { + for (let j = 0; j < words.length; j++) { + if (i === j) { + continue; + } + + if (words[j].includes(words[i])) { + res.push(words[i]); + break; + } + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2 * m ^ 2)$ +* Space complexity: + * $O(1)$ extra space. + * $O(n * m)$ space for the output list. + +> Where $n$ is the number of words, and $m$ is the length of the longest word. + +--- + +## 2. Sorting + +::tabs-start + +```python +class Solution: + def stringMatching(self, words: List[str]) -> List[str]: + res = [] + words.sort(key=len) + + for i in range(len(words)): + for j in range(i + 1, len(words)): + if words[i] in words[j]: + res.append(words[i]) + break + + return res +``` + +```java +public class Solution { + public List stringMatching(String[] words) { + List res = new ArrayList<>(); + Arrays.sort(words, Comparator.comparingInt(String::length)); + + for (int i = 0; i < words.length; i++) { + for (int j = i + 1; j < words.length; j++) { + if (words[j].contains(words[i])) { + res.add(words[i]); + break; + } + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector stringMatching(vector& words) { + vector res; + sort(words.begin(), words.end(), [](const string& a, const string& b) { + return a.length() < b.length(); + }); + + for (int i = 0; i < words.size(); i++) { + for (int j = i + 1; j < words.size(); j++) { + if (words[j].find(words[i]) != string::npos) { + res.push_back(words[i]); + break; + } + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} words + * @return {string[]} + */ + stringMatching(words) { + let res = []; + words.sort((a, b) => a.length - b.length); + + for (let i = 0; i < words.length; i++) { + for (let j = i + 1; j < words.length; j++) { + if (words[j].includes(words[i])) { + res.push(words[i]); + break; + } + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2 * m ^ 2)$ +* Space complexity: + * $O(1)$ or $O(n)$ depending on the sorting algorithm. + * $O(n * m)$ space for the output list. + +> Where $n$ is the number of words, and $m$ is the length of the longest word. + +--- + +## 3. Knuth-Morris-Pratt (KMP) Algorithm + +::tabs-start + +```python +class Solution: + def stringMatching(self, words: List[str]) -> List[str]: + def kmp(word1: str, word2: str) -> int: + lps = [0] * len(word2) + prevLPS, i = 0, 1 + + while i < len(word2): + if word2[i] == word2[prevLPS]: + lps[i] = prevLPS + 1 + prevLPS += 1 + i += 1 + elif prevLPS == 0: + lps[i] = 0 + i += 1 + else: + prevLPS = lps[prevLPS - 1] + + i = j = 0 + while i < len(word1): + if word1[i] == word2[j]: + i += 1 + j += 1 + else: + if j == 0: + i += 1 + else: + j = lps[j - 1] + + if j == len(word2): + return i - len(word2) + + return -1 + + res = [] + words.sort(key=len) + + for i in range(len(words)): + for j in range(i + 1, len(words)): + if kmp(words[j], words[i]) != -1: + res.append(words[i]) + break + + return res +``` + +```java +public class Solution { + public List stringMatching(String[] words) { + List res = new ArrayList<>(); + Arrays.sort(words, Comparator.comparingInt(String::length)); + + for (int i = 0; i < words.length; i++) { + for (int j = i + 1; j < words.length; j++) { + if (kmp(words[j], words[i]) != -1) { + res.add(words[i]); + break; + } + } + } + + return res; + } + + private int kmp(String word1, String word2) { + int[] lps = new int[word2.length()]; + int prevLPS = 0, i = 1; + + while (i < word2.length()) { + if (word2.charAt(i) == word2.charAt(prevLPS)) { + lps[i] = prevLPS + 1; + prevLPS++; + i++; + } else if (prevLPS == 0) { + lps[i] = 0; + i++; + } else { + prevLPS = lps[prevLPS - 1]; + } + } + + i = 0; + int j = 0; + while (i < word1.length()) { + if (word1.charAt(i) == word2.charAt(j)) { + i++; + j++; + } else { + if (j == 0) { + i++; + } else { + j = lps[j - 1]; + } + } + + if (j == word2.length()) { + return i - word2.length(); + } + } + + return -1; + } +} +``` + +```cpp +class Solution { +public: + vector stringMatching(vector& words) { + vector res; + sort(words.begin(), words.end(), [](const string& a, const string& b) { + return a.length() < b.length(); + }); + + for (int i = 0; i < words.size(); i++) { + for (int j = i + 1; j < words.size(); j++) { + if (kmp(words[j], words[i]) != -1) { + res.push_back(words[i]); + break; + } + } + } + + return res; + } + +private: + int kmp(const string& word1, const string& word2) { + vector lps(word2.size(), 0); + int prevLPS = 0, i = 1; + + while (i < word2.size()) { + if (word2[i] == word2[prevLPS]) { + lps[i++] = ++prevLPS; + } else if (prevLPS == 0) { + lps[i++] = 0; + } else { + prevLPS = lps[prevLPS - 1]; + } + } + + i = 0; + int j = 0; + while (i < word1.size()) { + if (word1[i] == word2[j]) { + i++; + j++; + } else { + if (j == 0) { + i++; + } else { + j = lps[j - 1]; + } + } + + if (j == word2.size()) { + return i - word2.size(); + } + } + + return -1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} words + * @return {string[]} + */ + stringMatching(words) { + const kmp = (word1, word2) => { + const lps = Array(word2.length).fill(0); + let prevLPS = 0, i = 1; + + while (i < word2.length) { + if (word2[i] === word2[prevLPS]) { + lps[i++] = ++prevLPS; + } else if (prevLPS === 0) { + lps[i++] = 0; + } else { + prevLPS = lps[prevLPS - 1]; + } + } + + i = 0; + let j = 0; + while (i < word1.length) { + if (word1[i] === word2[j]) { + i++; + j++; + } else { + if (j === 0) { + i++; + } else { + j = lps[j - 1]; + } + } + + if (j === word2.length) { + return i - word2.length; + } + } + + return -1; + }; + + let res = []; + words.sort((a, b) => a.length - b.length); + + for (let i = 0; i < words.length; i++) { + for (let j = i + 1; j < words.length; j++) { + if (kmp(words[j], words[i]) !== -1) { + res.push(words[i]); + break; + } + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2 * m)$ +* Space complexity: + * $O(m)$ extra space. + * $O(1)$ or $O(n)$ space depending on the sorting algorithm. + * $O(n * m)$ space for the output list. + +> Where $n$ is the number of words, and $m$ is the length of the longest word. + +--- + +## 4. Rabin-Karp Algorithm (Rolling Hash) + +::tabs-start + +```python +class Solution: + def stringMatching(self, words: List[str]) -> List[str]: + def rabinKarp(word1: str, word2: str) -> int: + base1, mod1 = 31, 768258391 + base2, mod2 = 37, 685683731 + n, m = len(word1), len(word2) + + power1, power2 = 1, 1 + for _ in range(m): + power1 = (power1 * base1) % mod1 + power2 = (power2 * base2) % mod2 + + word1_hash1 = word1_hash2 = 0 + word2_hash1 = word2_hash2 = 0 + + for i in range(m): + word1_hash1 = (word1_hash1 * base1 + ord(word2[i])) % mod1 + word1_hash2 = (word1_hash2 * base2 + ord(word2[i])) % mod2 + word2_hash1 = (word2_hash1 * base1 + ord(word1[i])) % mod1 + word2_hash2 = (word2_hash2 * base2 + ord(word1[i])) % mod2 + + for i in range(n - m + 1): + if word2_hash1 == word1_hash1 and word2_hash2 == word1_hash2: + return i + + if i + m < n: + word2_hash1 = (word2_hash1 * base1 - ord(word1[i]) * power1 + ord(word1[i + m])) % mod1 + word2_hash2 = (word2_hash2 * base2 - ord(word1[i]) * power2 + ord(word1[i + m])) % mod2 + + word2_hash1 = (word2_hash1 + mod1) % mod1 + word2_hash2 = (word2_hash2 + mod2) % mod2 + + return -1 + + res = [] + words.sort(key=len) + + for i in range(len(words)): + for j in range(i + 1, len(words)): + if rabinKarp(words[j], words[i]) != -1: + res.append(words[i]) + break + + return res +``` + +```java +public class Solution { + public List stringMatching(String[] words) { + List res = new ArrayList<>(); + Arrays.sort(words, Comparator.comparingInt(String::length)); + + for (int i = 0; i < words.length; i++) { + for (int j = i + 1; j < words.length; j++) { + if (rabinKarp(words[j], words[i]) != -1) { + res.add(words[i]); + break; + } + } + } + + return res; + } + + private int rabinKarp(String word1, String word2) { + int base1 = 31, mod1 = 768258391; + int base2 = 37, mod2 = 685683731; + int n = word1.length(), m = word2.length(); + + long power1 = 1, power2 = 1; + for (int k = 0; k < m; k++) { + power1 = (power1 * base1) % mod1; + power2 = (power2 * base2) % mod2; + } + + long word1Hash1 = 0, word1Hash2 = 0; + long word2Hash1 = 0, word2Hash2 = 0; + + for (int i = 0; i < m; i++) { + word1Hash1 = (word1Hash1 * base1 + word2.charAt(i)) % mod1; + word1Hash2 = (word1Hash2 * base2 + word2.charAt(i)) % mod2; + word2Hash1 = (word2Hash1 * base1 + word1.charAt(i)) % mod1; + word2Hash2 = (word2Hash2 * base2 + word1.charAt(i)) % mod2; + } + + for (int i = 0; i <= n - m; i++) { + if (word2Hash1 == word1Hash1 && word2Hash2 == word1Hash2) { + return i; + } + + if (i + m < n) { + word2Hash1 = (word2Hash1 * base1 - word1.charAt(i) * power1 + word1.charAt(i + m)) % mod1; + word2Hash2 = (word2Hash2 * base2 - word1.charAt(i) * power2 + word1.charAt(i + m)) % mod2; + + if (word2Hash1 < 0) word2Hash1 += mod1; + if (word2Hash2 < 0) word2Hash2 += mod2; + } + } + + return -1; + } +} +``` + +```cpp +class Solution { +public: + vector stringMatching(vector& words) { + vector res; + sort(words.begin(), words.end(), [](const string& a, const string& b) { + return a.length() < b.length(); + }); + + for (int i = 0; i < words.size(); i++) { + for (int j = i + 1; j < words.size(); j++) { + if (rabinKarp(words[j], words[i]) != -1) { + res.push_back(words[i]); + break; + } + } + } + + return res; + } + +private: + int rabinKarp(const string& word1, const string& word2) { + int base1 = 31, mod1 = 768258391; + int base2 = 37, mod2 = 685683731; + int n = word1.size(), m = word2.size(); + + long long power1 = 1, power2 = 1; + for (int i = 0; i < m; i++) { + power1 = (power1 * base1) % mod1; + power2 = (power2 * base2) % mod2; + } + + long long word1Hash1 = 0, word1Hash2 = 0; + long long word2Hash1 = 0, word2Hash2 = 0; + + for (int i = 0; i < m; i++) { + word1Hash1 = (word1Hash1 * base1 + word2[i]) % mod1; + word1Hash2 = (word1Hash2 * base2 + word2[i]) % mod2; + word2Hash1 = (word2Hash1 * base1 + word1[i]) % mod1; + word2Hash2 = (word2Hash2 * base2 + word1[i]) % mod2; + } + + for (int i = 0; i <= n - m; i++) { + if (word2Hash1 == word1Hash1 && word2Hash2 == word1Hash2) { + return i; + } + + if (i + m < n) { + word2Hash1 = (word2Hash1 * base1 - word1[i] * power1 + word1[i + m]) % mod1; + word2Hash2 = (word2Hash2 * base2 - word1[i] * power2 + word1[i + m]) % mod2; + + if (word2Hash1 < 0) word2Hash1 += mod1; + if (word2Hash2 < 0) word2Hash2 += mod2; + } + } + + return -1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} words + * @return {string[]} + */ + stringMatching(words) { + const rabinKarp = (word1, word2) => { + const base1 = 31, mod1 = 768258391; + const base2 = 37, mod2 = 685683731; + const n = word1.length, m = word2.length; + + let power1 = 1, power2 = 1; + for (let k = 0; k < m; k++) { + power1 = (power1 * base1) % mod1; + power2 = (power2 * base2) % mod2; + } + + let hash1 = 0, hash2 = 0; + let cur1 = 0, cur2 = 0; + + for (let i = 0; i < m; i++) { + hash1 = (hash1 * base1 + word2.charCodeAt(i)) % mod1; + hash2 = (hash2 * base2 + word2.charCodeAt(i)) % mod2; + cur1 = (cur1 * base1 + word1.charCodeAt(i)) % mod1; + cur2 = (cur2 * base2 + word1.charCodeAt(i)) % mod2; + } + + for (let i = 0; i <= n - m; i++) { + if (cur1 === hash1 && cur2 === hash2) { + return i; + } + + if (i + m < n) { + cur1 = (cur1 * base1 - word1.charCodeAt(i) * power1 + word1.charCodeAt(i + m)) % mod1; + cur2 = (cur2 * base2 - word1.charCodeAt(i) * power2 + word1.charCodeAt(i + m)) % mod2; + + cur1 = (cur1 + mod1) % mod1; + cur2 = (cur2 + mod2) % mod2; + } + } + + return -1; + }; + + words.sort((a, b) => a.length - b.length); + let res = []; + + for (let i = 0; i < words.length; i++) { + for (let j = i + 1; j < words.length; j++) { + if (rabinKarp(words[j], words[i]) !== -1) { + res.push(words[i]); + break; + } + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2 * m)$ +* Space complexity: + * $O(1)$ or $O(n)$ space depending on the sorting algorithm. + * $O(n * m)$ space for the output list. + +> Where $n$ is the number of words, and $m$ is the length of the longest word. + +--- + +## 5. Z-Algorithm + +::tabs-start + +```python +class Solution: + def stringMatching(self, words: List[str]) -> List[str]: + def zAlgorithm(word1: str, word2: str) -> int: + s = word2 + "$" + word1 + n = len(s) + z = [0] * n + l, r = 0, 0 + + for i in range(1, n): + if i <= r: + z[i] = min(r - i + 1, z[i - l]) + while i + z[i] < n and s[z[i]] == s[i + z[i]]: + z[i] += 1 + if i + z[i] - 1 > r: + l, r = i, i + z[i] - 1 + + for i in range(len(word2) + 1, n): + if z[i] == len(word2): + return i - len(word2) - 1 + + return -1 + + res = [] + words.sort(key=len) + + for i in range(len(words)): + for j in range(i + 1, len(words)): + if zAlgorithm(words[j], words[i]) != -1: + res.append(words[i]) + break + + return res +``` + +```java +public class Solution { + public List stringMatching(String[] words) { + List res = new ArrayList<>(); + Arrays.sort(words, Comparator.comparingInt(String::length)); + + for (int i = 0; i < words.length; i++) { + for (int j = i + 1; j < words.length; j++) { + if (zAlgorithm(words[j], words[i]) != -1) { + res.add(words[i]); + break; + } + } + } + + return res; + } + + private int zAlgorithm(String word1, String word2) { + String s = word2 + "$" + word1; + int n = s.length(); + int[] z = new int[n]; + int l = 0, r = 0; + + for (int i = 1; i < n; i++) { + if (i <= r) { + z[i] = Math.min(r - i + 1, z[i - l]); + } + while (i + z[i] < n && s.charAt(z[i]) == s.charAt(i + z[i])) { + z[i]++; + } + if (i + z[i] - 1 > r) { + l = i; + r = i + z[i] - 1; + } + } + + for (int i = word2.length() + 1; i < n; i++) { + if (z[i] == word2.length()) { + return i - word2.length() - 1; + } + } + + return -1; + } +} +``` + +```cpp +class Solution { +public: + vector stringMatching(vector& words) { + vector res; + sort(words.begin(), words.end(), [](const string& a, const string& b) { + return a.length() < b.length(); + }); + + for (int i = 0; i < words.size(); i++) { + for (int j = i + 1; j < words.size(); j++) { + if (zAlgorithm(words[j], words[i]) != -1) { + res.push_back(words[i]); + break; + } + } + } + + return res; + } + +private: + int zAlgorithm(const string& word1, const string& word2) { + string s = word2 + "$" + word1; + int n = s.size(); + vector z(n, 0); + int l = 0, r = 0; + + for (int i = 1; i < n; i++) { + if (i <= r) { + z[i] = min(r - i + 1, z[i - l]); + } + while (i + z[i] < n && s[z[i]] == s[i + z[i]]) { + z[i]++; + } + if (i + z[i] - 1 > r) { + l = i; + r = i + z[i] - 1; + } + } + + for (int i = word2.size() + 1; i < n; i++) { + if (z[i] == word2.size()) { + return i - word2.size() - 1; + } + } + + return -1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} words + * @return {string[]} + */ + stringMatching(words) { + const zAlgorithm = (word1, word2) => { + const s = word2 + "$" + word1; + const n = s.length; + const z = Array(n).fill(0); + let l = 0, r = 0; + + for (let i = 1; i < n; i++) { + if (i <= r) { + z[i] = Math.min(r - i + 1, z[i - l]); + } + while (i + z[i] < n && s[z[i]] === s[i + z[i]]) { + z[i]++; + } + if (i + z[i] - 1 > r) { + l = i; + r = i + z[i] - 1; + } + } + + for (let i = word2.length + 1; i < n; i++) { + if (z[i] === word2.length) { + return i - word2.length - 1; + } + } + + return -1; + }; + + words.sort((a, b) => a.length - b.length); + let res = []; + + for (let i = 0; i < words.length; i++) { + for (let j = i + 1; j < words.length; j++) { + if (zAlgorithm(words[j], words[i]) !== -1) { + res.push(words[i]); + break; + } + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2 * m)$ +* Space complexity: + * $O(m)$ extra space. + * $O(1)$ or $O(n)$ space depending on the sorting algorithm. + * $O(n * m)$ space for the output list. + +> Where $n$ is the number of words, and $m$ is the length of the longest word. + +--- + +## 6. Trie + +::tabs-start + +```python +class TrieNode: + def __init__(self): + self.children = [None] * 26 + self.cnt = 0 + +class Trie: + def __init__(self): + self.root = TrieNode() + + def insert_suffixes(self, word: str) -> None: + for i in range(len(word)): + node = self.root + for j in range(i, len(word)): + idx = ord(word[j]) - ord('a') + if not node.children[idx]: + node.children[idx] = TrieNode() + + node = node.children[idx] + node.cnt += 1 + + def search(self, word: str) -> bool: + node = self.root + for c in word: + idx = ord(c) - ord('a') + node = node.children[idx] + return node.cnt > 1 + +class Solution: + def stringMatching(self, words: List[str]) -> List[str]: + res = [] + trie = Trie() + + for word in words: + trie.insert_suffixes(word) + + for word in words: + if trie.search(word): + res.append(word) + + return res +``` + +```java +class TrieNode { + TrieNode[] children; + int cnt; + + TrieNode() { + children = new TrieNode[26]; + cnt = 0; + } +} + +class Trie { + TrieNode root; + + Trie() { + root = new TrieNode(); + } + + void insertSuffixes(String word) { + for (int i = 0; i < word.length(); i++) { + TrieNode node = root; + for (int j = i; j < word.length(); j++) { + int idx = word.charAt(j) - 'a'; + if (node.children[idx] == null) { + node.children[idx] = new TrieNode(); + } + + node = node.children[idx]; + node.cnt++; + } + } + } + + boolean search(String word) { + TrieNode node = root; + for (int i = 0; i < word.length(); i++) { + int idx = word.charAt(i) - 'a'; + node = node.children[idx]; + } + return node.cnt > 1; + } +} + +class Solution { + public List stringMatching(String[] words) { + List res = new ArrayList<>(); + Trie trie = new Trie(); + + for (String word : words) { + trie.insertSuffixes(word); + } + + for (String word : words) { + if (trie.search(word)) { + res.add(word); + } + } + + return res; + } +} +``` + +```cpp +class TrieNode { +public: + TrieNode* children[26]; + int cnt; + + TrieNode() { + for (int i = 0; i < 26; i++) children[i] = nullptr; + cnt = 0; + } +}; + +class Trie { +public: + TrieNode* root; + + Trie() { + root = new TrieNode(); + } + + void insertSuffixes(const string& word) { + for (int i = 0; i < word.size(); i++) { + TrieNode* node = root; + for (int j = i; j < word.size(); j++) { + int idx = word[j] - 'a'; + if (!node->children[idx]) { + node->children[idx] = new TrieNode(); + } + + node = node->children[idx]; + node->cnt++; + } + } + } + + bool search(const string& word) { + TrieNode* node = root; + for (char c : word) { + int idx = c - 'a'; + node = node->children[idx]; + } + return node->cnt > 1; + } +}; + +class Solution { +public: + vector stringMatching(vector& words) { + vector res; + Trie trie; + + for (const string& word : words) { + trie.insertSuffixes(word); + } + + for (const string& word : words) { + if (trie.search(word)) { + res.push_back(word); + } + } + + return res; + } +}; +``` + +```javascript +class TrieNode { + constructor() { + this.children = new Array(26).fill(null); + this.cnt = 0; + } +} + +class Trie { + constructor() { + this.root = new TrieNode(); + } + + /** + * @param {string} word + * @return {void} + */ + insertSuffixes(word) { + for (let i = 0; i < word.length; i++) { + let node = this.root; + for (let j = i; j < word.length; j++) { + let idx = word.charCodeAt(j) - 97; + if (!node.children[idx]) { + node.children[idx] = new TrieNode(); + } + + node = node.children[idx]; + node.cnt++; + } + } + } + + /** + * @param {string} word + * @return {boolean} + */ + search(word) { + let node = this.root; + for (let i = 0; i < word.length; i++) { + let idx = word.charCodeAt(i) - 97; + node = node.children[idx]; + } + return node.cnt > 1; + } +} + +class Solution { + /** + * @param {string[]} words + * @return {string[]} + */ + stringMatching(words) { + const res = []; + const trie = new Trie(); + + for (let word of words) { + trie.insertSuffixes(word); + } + + for (let word of words) { + if (trie.search(word)) { + res.push(word); + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * m ^ 2)$ +* Space complexity: + * $O(n * m ^ 2)$ extra space. + * $O(n * m)$ space for the output list. + +> Where $n$ is the number of words, and $m$ is the length of the longest word. \ No newline at end of file diff --git a/articles/subarray-sums-divisible-by-k.md b/articles/subarray-sums-divisible-by-k.md new file mode 100644 index 000000000..f1faeaf61 --- /dev/null +++ b/articles/subarray-sums-divisible-by-k.md @@ -0,0 +1,280 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def subarraysDivByK(self, nums: List[int], k: int) -> int: + n, res = len(nums), 0 + + for i in range(n): + curSum = 0 + for j in range(i, n): + curSum += nums[j] + if curSum % k == 0: + res += 1 + + return res +``` + +```java +public class Solution { + public int subarraysDivByK(int[] nums, int k) { + int n = nums.length, res = 0; + + for (int i = 0; i < n; i++) { + int curSum = 0; + for (int j = i; j < n; j++) { + curSum += nums[j]; + if (curSum % k == 0) { + res++; + } + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int subarraysDivByK(vector& nums, int k) { + int n = nums.size(), res = 0; + + for (int i = 0; i < n; i++) { + int curSum = 0; + for (int j = i; j < n; j++) { + curSum += nums[j]; + if (curSum % k == 0) { + res++; + } + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {number} + */ + subarraysDivByK(nums, k) { + const n = nums.length; + let res = 0; + + for (let i = 0; i < n; i++) { + let curSum = 0; + for (let j = i; j < n; j++) { + curSum += nums[j]; + if (curSum % k === 0) { + res++; + } + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(1)$ + +--- + +## 2. Prefix Sum + Hash Map + +::tabs-start + +```python +class Solution: + def subarraysDivByK(self, nums: List[int], k: int) -> int: + prefix_sum = 0 + res = 0 + prefix_cnt = defaultdict(int) + prefix_cnt[0] = 1 + + for n in nums: + prefix_sum += n + remain = prefix_sum % k + + res += prefix_cnt[remain] + prefix_cnt[remain] += 1 + + return res +``` + +```java +public class Solution { + public int subarraysDivByK(int[] nums, int k) { + int prefixSum = 0, res = 0; + Map prefixCnt = new HashMap<>(); + prefixCnt.put(0, 1); + + for (int n : nums) { + prefixSum += n; + int remain = prefixSum % k; + if (remain < 0) remain += k; + + res += prefixCnt.getOrDefault(remain, 0); + prefixCnt.put(remain, prefixCnt.getOrDefault(remain, 0) + 1); + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int subarraysDivByK(vector& nums, int k) { + int prefixSum = 0, res = 0; + unordered_map prefixCnt; + prefixCnt[0] = 1; + + for (int n : nums) { + prefixSum += n; + int remain = prefixSum % k; + if (remain < 0) remain += k; + + res += prefixCnt[remain]; + prefixCnt[remain]++; + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {number} + */ + subarraysDivByK(nums, k) { + let prefixSum = 0, res = 0; + const prefixCnt = new Map(); + prefixCnt.set(0, 1); + + for (let n of nums) { + prefixSum += n; + let remain = prefixSum % k; + if (remain < 0) remain += k; + + res += prefixCnt.get(remain) || 0; + prefixCnt.set(remain, (prefixCnt.get(remain) || 0) + 1); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(k)$ + +--- + +## 3. Prefix Sum + Array + +::tabs-start + +```python +class Solution: + def subarraysDivByK(self, nums: List[int], k: int) -> int: + count = [0] * k + count[0] = 1 + prefix = res = 0 + + for num in nums: + prefix = (prefix + num + k) % k + res += count[prefix] + count[prefix] += 1 + + return res +``` + +```java +public class Solution { + public int subarraysDivByK(int[] nums, int k) { + int[] count = new int[k]; + count[0] = 1; + int prefix = 0, res = 0; + + for (int num : nums) { + prefix = (prefix + num % k + k) % k; + res += count[prefix]; + count[prefix]++; + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int subarraysDivByK(vector& nums, int k) { + vector count(k, 0); + count[0] = 1; + int prefix = 0, res = 0; + + for (int num : nums) { + prefix = (prefix + num % k + k) % k; + res += count[prefix]; + count[prefix]++; + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {number} + */ + subarraysDivByK(nums, k) { + const count = Array(k).fill(0); + count[0] = 1; + let prefix = 0, res = 0; + + for (let num of nums) { + prefix = (prefix + num % k + k) % k; + res += count[prefix]; + count[prefix]++; + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n + k)$ +* Space complexity: $O(k)$ \ No newline at end of file diff --git a/articles/uncommon-words-from-two-sentences.md b/articles/uncommon-words-from-two-sentences.md new file mode 100644 index 000000000..67be56a4b --- /dev/null +++ b/articles/uncommon-words-from-two-sentences.md @@ -0,0 +1,183 @@ +## 1. Hash Map - I + +::tabs-start + +```python +class Solution: + def uncommonFromSentences(self, s1: str, s2: str) -> List[str]: + count = defaultdict(int) + for w in s1.split(" ") + s2.split(" "): + count[w] += 1 + + res = [] + for w, cnt in count.items(): + if cnt == 1: + res.append(w) + return res +``` + +```java +public class Solution { + public String[] uncommonFromSentences(String s1, String s2) { + String[] words = (s1 + " " + s2).split(" "); + Map count = new HashMap<>(); + + for (String w : words) { + count.put(w, count.getOrDefault(w, 0) + 1); + } + + List res = new ArrayList<>(); + for (Map.Entry entry : count.entrySet()) { + if (entry.getValue() == 1) { + res.add(entry.getKey()); + } + } + + return res.toArray(new String[0]); + } +} +``` + +```cpp +class Solution { +public: + vector uncommonFromSentences(string s1, string s2) { + unordered_map count; + istringstream ss(s1 + " " + s2); + string w; + while (ss >> w) { + count[w]++; + } + + vector res; + for (auto& [word, freq] : count) { + if (freq == 1) { + res.push_back(word); + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s1 + * @param {string} s2 + * @return {string[]} + */ + uncommonFromSentences(s1, s2) { + const words = (s1 + " " + s2).split(" "); + const count = new Map(); + + for (const w of words) { + count.set(w, (count.get(w) || 0) + 1); + } + + const res = []; + for (const [w, c] of count.entries()) { + if (c === 1) { + res.push(w); + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n + m)$ +* Space complexity: $O(n + m)$ + +> Where $n$ and $m$ are the lengths of the strings $s1$ and $s2$, respectively. + +--- + +## 2. Hash Map - II + +::tabs-start + +```python +class Solution: + def uncommonFromSentences(self, s1: str, s2: str) -> List[str]: + return [w for w, cnt in Counter(s1.split(" ") + s2.split(" ")).items() if cnt == 1] +``` + +```java +public class Solution { + public String[] uncommonFromSentences(String s1, String s2) { + String[] words = (s1 + " " + s2).split(" "); + Map count = new HashMap<>(); + + for (String w : words) { + count.put(w, count.getOrDefault(w, 0) + 1); + } + + return count.entrySet() + .stream() + .filter(e -> e.getValue() == 1) + .map(Map.Entry::getKey) + .toArray(String[]::new); + } +} +``` + +```cpp +class Solution { +public: + vector uncommonFromSentences(string s1, string s2) { + unordered_map count; + istringstream ss(s1 + " " + s2); + string w; + while (ss >> w) { + count[w]++; + } + + vector res; + for (auto& [w, c] : count) { + if (c == 1) { + res.push_back(w); + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s1 + * @param {string} s2 + * @return {string[]} + */ + uncommonFromSentences(s1, s2) { + const words = (s1 + " " + s2).split(" "); + const count = new Map(); + + for (const w of words) { + count.set(w, (count.get(w) || 0) + 1); + } + + return [...count.entries()] + .filter(([_, c]) => c === 1) + .map(([w]) => w); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n + m)$ +* Space complexity: $O(n + m)$ + +> Where $n$ and $m$ are the lengths of the strings $s1$ and $s2$, respectively. \ No newline at end of file diff --git a/articles/valid-palindrome-ii.md b/articles/valid-palindrome-ii.md index 05367b687..6b1fbf303 100644 --- a/articles/valid-palindrome-ii.md +++ b/articles/valid-palindrome-ii.md @@ -119,6 +119,31 @@ class Solution { } ``` +```csharp +public class Solution { + public bool ValidPalindrome(string s) { + if (IsPalindrome(s)) return true; + + for (int i = 0; i < s.Length; i++) { + string newS = s.Substring(0, i) + s.Substring(i + 1); + if (IsPalindrome(newS)) return true; + } + + return false; + } + + private bool IsPalindrome(string str) { + int left = 0, right = str.Length - 1; + while (left < right) { + if (str[left] != str[right]) return false; + left++; + right--; + } + return true; + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -250,6 +275,36 @@ class Solution { } ``` +```csharp +public class Solution { + public bool ValidPalindrome(string s) { + int l = 0, r = s.Length - 1; + + while (l < r) { + if (s[l] != s[r]) { + string skipL = s.Substring(l + 1, r - l); + string skipR = s.Substring(l, r - l); + return IsPalindrome(skipL) || IsPalindrome(skipR); + } + l++; + r--; + } + + return true; + } + + private bool IsPalindrome(string str) { + int left = 0, right = str.Length - 1; + while (left < right) { + if (str[left] != str[right]) return false; + left++; + right--; + } + return true; + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -387,6 +442,32 @@ class Solution { } ``` +```csharp +public class Solution { + public bool ValidPalindrome(string s) { + bool IsPalindrome(int l, int r) { + while (l < r) { + if (s[l] != s[r]) return false; + l++; + r--; + } + return true; + } + + int left = 0, right = s.Length - 1; + while (left < right) { + if (s[left] != s[right]) { + return IsPalindrome(left + 1, right) || IsPalindrome(left, right - 1); + } + left++; + right--; + } + + return true; + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/valid-word-abbreviation.md b/articles/valid-word-abbreviation.md new file mode 100644 index 000000000..79c44a2e7 --- /dev/null +++ b/articles/valid-word-abbreviation.md @@ -0,0 +1,163 @@ +## 1. Two Pointers + +::tabs-start + +```python +class Solution: + def validWordAbbreviation(self, word: str, abbr: str) -> bool: + n, m = len(word), len(abbr) + i = j = 0 + + while i < n and j < m: + if abbr[j] == '0': + return False + + if word[i] == abbr[j]: + i, j = i + 1, j + 1 + elif abbr[j].isalpha(): + return False + else: + subLen = 0 + while j < m and abbr[j].isdigit(): + subLen = subLen * 10 + int(abbr[j]) + j += 1 + i += subLen + + return i == n and j == m +``` + +```java +public class Solution { + public boolean validWordAbbreviation(String word, String abbr) { + int n = word.length(), m = abbr.length(); + int i = 0, j = 0; + + while (i < n && j < m) { + if (abbr.charAt(j) == '0') return false; + + if (Character.isLetter(abbr.charAt(j))) { + if (i < n && word.charAt(i) == abbr.charAt(j)) { + i++; + j++; + } else { + return false; + } + } else { + int subLen = 0; + while (j < m && Character.isDigit(abbr.charAt(j))) { + subLen = subLen * 10 + (abbr.charAt(j) - '0'); + j++; + } + i += subLen; + } + } + + return i == n && j == m; + } +} +``` + +```cpp +class Solution { +public: + bool validWordAbbreviation(string word, string abbr) { + int n = word.length(), m = abbr.length(); + int i = 0, j = 0; + + while (i < n && j < m) { + if (abbr[j] == '0') return false; + + if (isalpha(abbr[j])) { + if (word[i] == abbr[j]) { + i++; j++; + } else { + return false; + } + } else { + int subLen = 0; + while (j < m && isdigit(abbr[j])) { + subLen = subLen * 10 + (abbr[j] - '0'); + j++; + } + i += subLen; + } + } + + return i == n && j == m; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} word + * @param {string} abbr + * @return {boolean} + */ + validWordAbbreviation(word, abbr) { + let n = word.length, m = abbr.length; + let i = 0, j = 0; + + while (i < n && j < m) { + if (abbr[j] === '0') return false; + + if (isNaN(abbr[j])) { + if (word[i] === abbr[j]) { + i++; j++; + } else { + return false; + } + } else { + let subLen = 0; + while (j < m && !isNaN(abbr[j]) && abbr[j] !== ' ') { + subLen = subLen * 10 + parseInt(abbr[j]); + j++; + } + i += subLen; + } + } + + return i === n && j === m; + } +} +``` + +```csharp +public class Solution { + public bool ValidWordAbbreviation(string word, string abbr) { + int n = word.Length, m = abbr.Length; + int i = 0, j = 0; + + while (i < n && j < m) { + if (abbr[j] == '0') return false; + + if (char.IsLetter(abbr[j])) { + if (i < n && word[i] == abbr[j]) { + i++; j++; + } else { + return false; + } + } else { + int subLen = 0; + while (j < m && char.IsDigit(abbr[j])) { + subLen = subLen * 10 + (abbr[j] - '0'); + j++; + } + i += subLen; + } + } + + return i == n && j == m; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n + m)$ +* Space complexity: $O(1)$ + +> Where $n$ and $m$ are the lengths of the strings $word$ and $abbr$, respectively. \ No newline at end of file diff --git a/articles/word-subsets.md b/articles/word-subsets.md new file mode 100644 index 000000000..d96eab686 --- /dev/null +++ b/articles/word-subsets.md @@ -0,0 +1,293 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def wordSubsets(self, words1: List[str], words2: List[str]) -> List[str]: + res = [] + for w1 in words1: + count1 = Counter(w1) + is_subset = True + + for w2 in words2: + count2 = Counter(w2) + for c in count2: + if count2[c] > count1[c]: + is_subset = False + break + + if not is_subset: break + + if is_subset: + res.append(w1) + + return res +``` + +```java +public class Solution { + public List wordSubsets(String[] words1, String[] words2) { + List res = new ArrayList<>(); + + for (String w1 : words1) { + int[] count1 = new int[26]; + for (char c : w1.toCharArray()) count1[c - 'a']++; + + boolean isSubset = true; + for (String w2 : words2) { + int[] count2 = new int[26]; + for (char c : w2.toCharArray()) count2[c - 'a']++; + + for (int i = 0; i < 26; i++) { + if (count2[i] > count1[i]) { + isSubset = false; + break; + } + } + + if (!isSubset) break; + } + + if (isSubset) res.add(w1); + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector wordSubsets(vector& words1, vector& words2) { + vector res; + + for (const string& w1 : words1) { + vector count1(26, 0); + for (char c : w1) count1[c - 'a']++; + + bool isSubset = true; + for (const string& w2 : words2) { + vector count2(26, 0); + for (char c : w2) count2[c - 'a']++; + + for (int i = 0; i < 26; i++) { + if (count2[i] > count1[i]) { + isSubset = false; + break; + } + } + + if (!isSubset) break; + } + + if (isSubset) res.push_back(w1); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} words1 + * @param {string[]} words2 + * @return {string[]} + */ + wordSubsets(words1, words2) { + const res = []; + + for (const w1 of words1) { + const count1 = Array(26).fill(0); + for (const c of w1) count1[c.charCodeAt(0) - 97]++; + + let isSubset = true; + for (const w2 of words2) { + const count2 = Array(26).fill(0); + for (const c of w2) count2[c.charCodeAt(0) - 97]++; + + for (let i = 0; i < 26; i++) { + if (count2[i] > count1[i]) { + isSubset = false; + break; + } + } + + if (!isSubset) break; + } + + if (isSubset) res.push(w1); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(N * n + N * M * m)$ +* Space complexity: + * $O(1)$ extra space, since we have at most $26$ different characters. + * $O(N * n)$ space for the output list. + +> Where $N$ is the size of the array $words1$, $n$ is the length of the longest word in $words1$, $M$ is the size of the array $words2$, and $m$ is the length of the longest word in $words2$. + +--- + +## 2. Greedy + Hash Map + +::tabs-start + +```python +class Solution: + def wordSubsets(self, words1: List[str], words2: List[str]) -> List[str]: + count_2 = defaultdict(int) + for w in words2: + count_w = Counter(w) + for c, cnt in count_w.items(): + count_2[c] = max(count_2[c], cnt) + + res = [] + for w in words1: + count_w = Counter(w) + flag = True + for c, cnt in count_2.items(): + if count_w[c] < cnt: + flag = False + break + if flag: + res.append(w) + + return res +``` + +```java +public class Solution { + public List wordSubsets(String[] words1, String[] words2) { + int[] count2 = new int[26]; + for (String w : words2) { + int[] countW = new int[26]; + for (char c : w.toCharArray()) { + countW[c - 'a']++; + } + for (int i = 0; i < 26; i++) { + count2[i] = Math.max(count2[i], countW[i]); + } + } + + List res = new ArrayList<>(); + for (String w : words1) { + int[] countW = new int[26]; + for (char c : w.toCharArray()) { + countW[c - 'a']++; + } + + boolean flag = true; + for (int i = 0; i < 26; i++) { + if (countW[i] < count2[i]) { + flag = false; + break; + } + } + + if (flag) { + res.add(w); + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector wordSubsets(vector& words1, vector& words2) { + vector count2(26, 0); + for (string& w : words2) { + vector countW(26, 0); + for (char c : w) countW[c - 'a']++; + for (int i = 0; i < 26; ++i) + count2[i] = max(count2[i], countW[i]); + } + + vector res; + for (string& w : words1) { + vector countW(26, 0); + for (char c : w) countW[c - 'a']++; + + bool flag = true; + for (int i = 0; i < 26; ++i) { + if (countW[i] < count2[i]) { + flag = false; + break; + } + } + + if (flag) res.push_back(w); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} words1 + * @param {string[]} words2 + * @return {string[]} + */ + wordSubsets(words1, words2) { + const count2 = new Array(26).fill(0); + for (let w of words2) { + const countW = new Array(26).fill(0); + for (let c of w) { + countW[c.charCodeAt(0) - 97]++; + } + for (let i = 0; i < 26; i++) { + count2[i] = Math.max(count2[i], countW[i]); + } + } + + const res = []; + for (let w of words1) { + const countW = new Array(26).fill(0); + for (let c of w) { + countW[c.charCodeAt(0) - 97]++; + } + + let flag = true; + for (let i = 0; i < 26; i++) { + if (countW[i] < count2[i]) { + flag = false; + break; + } + } + + if (flag) res.push(w); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(N * n + M * m)$ +* Space complexity: + * $O(1)$ extra space, since we have at most $26$ different characters. + * $O(N * n)$ space for the output list. + +> Where $N$ is the size of the array $words1$, $n$ is the length of the longest word in $words1$, $M$ is the size of the array $words2$, and $m$ is the length of the longest word in $words2$. \ No newline at end of file From c92da38a5b7a8a5e9b2bb3a1fa0e4338894a53ce Mon Sep 17 00:00:00 2001 From: Chiao Yang Date: Fri, 18 Apr 2025 14:45:41 +0800 Subject: [PATCH 40/45] Update 0020-valid-parentheses.py avoid using 'map' as variable name to avoid overriding a built-in Signed-off-by: Chiao Yang --- python/0020-valid-parentheses.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/python/0020-valid-parentheses.py b/python/0020-valid-parentheses.py index 4d0ae3424..ce3bf39cd 100644 --- a/python/0020-valid-parentheses.py +++ b/python/0020-valid-parentheses.py @@ -1,13 +1,13 @@ class Solution: def isValid(self, s: str) -> bool: - Map = {")": "(", "]": "[", "}": "{"} + bracketMap = {")": "(", "]": "[", "}": "{"} stack = [] for c in s: - if c not in Map: + if c not in bracketMap: stack.append(c) continue - if not stack or stack[-1] != Map[c]: + if not stack or stack[-1] != bracketMap[c]: return False stack.pop() From 525065cba2b33369f6d790f75d27f78bf480c429 Mon Sep 17 00:00:00 2001 From: Sri Hari <94112314+Srihari2222@users.noreply.github.com> Date: Sat, 19 Apr 2025 23:19:31 +0530 Subject: [PATCH 41/45] Sri Hari: Batch-6/Neetcode-All/Added-articles (#4061) * Batch-6/Neetcode-ALL/Added-articles * Batch-6/Neetcode-ALL/Added-articles --- .../analyze-user-website-visit-pattern.md | 224 ++++++++ .../binary-tree-vertical-order-traversal.md | 22 +- articles/maximum-profit-in-job-scheduling.md | 155 ++++++ articles/put-marbles-in-bags.md | 501 ++++++++++++++++++ articles/reorganize-string.md | 117 ++++ 5 files changed, 1008 insertions(+), 11 deletions(-) create mode 100644 articles/analyze-user-website-visit-pattern.md create mode 100644 articles/put-marbles-in-bags.md diff --git a/articles/analyze-user-website-visit-pattern.md b/articles/analyze-user-website-visit-pattern.md new file mode 100644 index 000000000..c43fce6ba --- /dev/null +++ b/articles/analyze-user-website-visit-pattern.md @@ -0,0 +1,224 @@ +## 1. Hash Map + +::tabs-start + +```python +class Solution: + def mostVisitedPattern(self, username: List[str], timestamp: List[int], website: List[str]) -> List[str]: + arr = list(zip(timestamp, username, website)) + arr.sort() + + mp = defaultdict(list) + for time, user, site in arr: + mp[user].append(site) + + count = defaultdict(int) + for user in mp: + patterns = set() + cur = mp[user] + for i in range(len(cur)): + for j in range(i + 1, len(cur)): + for k in range(j + 1, len(cur)): + patterns.add((cur[i], cur[j], cur[k])) + for p in patterns: + count[p] += 1 + + max_count = 0 + res = tuple() + for pattern in count: + if count[pattern] > max_count or (count[pattern] == max_count and pattern < res): + max_count = count[pattern] + res = pattern + + return list(res) +``` + +```java +public class Solution { + public List mostVisitedPattern(String[] username, int[] timestamp, String[] website) { + int n = timestamp.length; + List arr = new ArrayList<>(); + for (int i = 0; i < n; i++) arr.add(new int[]{timestamp[i], i}); + arr.sort((a, b) -> Integer.compare(a[0], b[0])); + + Map> mp = new HashMap<>(); + for (int[] p : arr) { + int idx = p[1]; + mp.computeIfAbsent(username[idx], k -> new ArrayList<>()).add(website[idx]); + } + + Map count = new HashMap<>(); + for (String user : mp.keySet()) { + List cur = mp.get(user); + Set patterns = new HashSet<>(); + for (int i = 0; i < cur.size(); i++) + for (int j = i + 1; j < cur.size(); j++) + for (int k = j + 1; k < cur.size(); k++) + patterns.add(cur.get(i) + "#" + cur.get(j) + "#" + cur.get(k)); + for (String p : patterns) + count.put(p, count.getOrDefault(p, 0) + 1); + } + + String res = ""; + int max_count = 0; + for (String p : count.keySet()) { + int c = count.get(p); + if (c > max_count || (c == max_count && p.compareTo(res) < 0)) { + max_count = c; + res = p; + } + } + return Arrays.asList(res.split("#")); + } +} +``` + +```cpp +class Solution { +public: + vector mostVisitedPattern(vector& username, vector& timestamp, vector& website) { + int n = timestamp.size(); + vector> arr; + for (int i = 0; i < n; ++i) arr.push_back({timestamp[i], i}); + sort(arr.begin(), arr.end(), + [](auto& a, auto& b){ return a.first < b.first; }); + + unordered_map> mp; + for (auto& p : arr) mp[username[p.second]].push_back(website[p.second]); + + unordered_map count; + for (auto& kv : mp) { + auto& cur = kv.second; + unordered_set patterns; + for (int i = 0; i < (int)cur.size(); ++i) + for (int j = i + 1; j < (int)cur.size(); ++j) + for (int k = j + 1; k < (int)cur.size(); ++k) + patterns.insert(cur[i] + "#" + cur[j] + "#" + cur[k]); + for (auto& p : patterns) ++count[p]; + } + + int maxCnt = 0; + string res; + for (auto& kv : count) + if (kv.second > maxCnt || + (kv.second == maxCnt && (res.empty() || kv.first < res))) { + maxCnt = kv.second; + res = kv.first; + } + + vector ans; + string tmp; + for (char ch : res) { + if (ch == '#') { + ans.push_back(tmp); + tmp.clear(); + } else { + tmp += ch; + } + } + ans.push_back(tmp); + return ans; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} username + * @param {number[]} timestamp + * @param {string[]} website + * @return {string[]} + */ + mostVisitedPattern(username, timestamp, website) { + const n = timestamp.length; + const arr = []; + for (let i = 0; i < n; i++) arr.push([timestamp[i], i]); + arr.sort((a, b) => a[0] - b[0]); + + const mp = new Map(); + for (const [, idx] of arr) { + const user = username[idx], site = website[idx]; + if (!mp.has(user)) mp.set(user, []); + mp.get(user).push(site); + } + + const count = new Map(); + for (const user of mp.keys()) { + const cur = mp.get(user); + const patterns = new Set(); + for (let i = 0; i < cur.length; i++) { + for (let j = i + 1; j < cur.length; j++) { + for (let k = j + 1; k < cur.length; k++) { + patterns.add(`${cur[i]}#${cur[j]}#${cur[k]}`); + } + } + } + for (const p of patterns) { + count.set(p, (count.get(p) || 0) + 1); + } + } + + let maxCnt = 0, res = ""; + for (const [pat, c] of count.entries()) { + if (c > maxCnt || (c === maxCnt && (res === "" || pat < res))) { + maxCnt = c; + res = pat; + } + } + return res.split("#"); + } +} +``` + +```csharp +public class Solution { + public List MostVisitedPattern(string[] username, int[] timestamp, string[] website) { + int n = timestamp.Length; + var arr = new List<(int t, int i)>(); + for (int i = 0; i < n; i++) arr.Add((timestamp[i], i)); + arr.Sort((a, b) => a.t.CompareTo(b.t)); + + var mp = new Dictionary>(); + foreach (var (t, idx) in arr) { + string user = username[idx], site = website[idx]; + if (!mp.ContainsKey(user)) mp[user] = new List(); + mp[user].Add(site); + } + + var count = new Dictionary(); + foreach (var kv in mp) { + var cur = kv.Value; + var patterns = new HashSet(); + for (int i = 0; i < cur.Count; i++) + for (int j = i + 1; j < cur.Count; j++) + for (int k = j + 1; k < cur.Count; k++) + patterns.Add($"{cur[i]}#{cur[j]}#{cur[k]}"); + foreach (var p in patterns) { + count[p] = count.ContainsKey(p) ? count[p] + 1 : 1; + } + } + + int maxCnt = 0; + string res = ""; + foreach (var kv in count) { + if (kv.Value > maxCnt || + (kv.Value == maxCnt && + (res == "" || string.Compare(kv.Key, res, StringComparison.Ordinal) < 0))) { + maxCnt = kv.Value; + res = kv.Key; + } + } + return new List(res.Split('#')); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n + n * u + n ^ 3 * w)$ +* Space complexity: $O(n * u + n ^ 3 * w)$ + +> Where $n$ is the size of the array $timestamp$, $u$ is the maximum length of any string in the array $username$, and $w$ is the maximum length of any string in the array $website$. \ No newline at end of file diff --git a/articles/binary-tree-vertical-order-traversal.md b/articles/binary-tree-vertical-order-traversal.md index 506fdad2c..c66e08d0b 100644 --- a/articles/binary-tree-vertical-order-traversal.md +++ b/articles/binary-tree-vertical-order-traversal.md @@ -151,8 +151,8 @@ class Solution { * } */ public class Solution { - public IList> VerticalOrder(TreeNode root) { - if (root == null) return new List>(); + public List> VerticalOrder(TreeNode root) { + if (root == null) return new List>(); var cols = new SortedDictionary>(); var queue = new Queue<(TreeNode node, int pos)>(); @@ -169,7 +169,7 @@ public class Solution { if (node.right != null) queue.Enqueue((node.right, pos + 1)); } - return cols.Values.ToList>(); + return cols.Values.ToList>(); } } ``` @@ -354,10 +354,10 @@ class Solution { public class Solution { private SortedDictionary> cols = new(); - public IList> VerticalOrder(TreeNode root) { + public List> VerticalOrder(TreeNode root) { DFS(root, 0, 0); - List> res = new(); + List> res = new(); foreach (var entry in cols) { var list = entry.Value.OrderBy(x => x.Item1).Select(x => x.Item2).ToList(); res.Add(list); @@ -565,8 +565,8 @@ class Solution { * } */ public class Solution { - public IList> VerticalOrder(TreeNode root) { - if (root == null) return new List>(); + public List> VerticalOrder(TreeNode root) { + if (root == null) return new List>(); Dictionary> cols = new(); Queue<(TreeNode node, int col)> queue = new(); @@ -585,7 +585,7 @@ public class Solution { if (node.right != null) queue.Enqueue((node.right, col + 1)); } - var res = new List>(); + var res = new List>(); for (int c = minCol; c <= maxCol; c++) { res.Add(cols[c]); } @@ -801,10 +801,10 @@ public class Solution { private Dictionary> cols = new(); private int minCol = 0, maxCol = 0; - public IList> VerticalOrder(TreeNode root) { - if (root == null) return new List>(); + public List> VerticalOrder(TreeNode root) { + if (root == null) return new List>(); DFS(root, 0, 0); - var res = new List>(); + var res = new List>(); for (int c = minCol; c <= maxCol; c++) { var list = cols.ContainsKey(c) ? cols[c] : new List<(int, int)>(); diff --git a/articles/maximum-profit-in-job-scheduling.md b/articles/maximum-profit-in-job-scheduling.md index 181614b62..0c0d40541 100644 --- a/articles/maximum-profit-in-job-scheduling.md +++ b/articles/maximum-profit-in-job-scheduling.md @@ -155,6 +155,42 @@ class Solution { } ``` +```csharp +public class Solution { + public int JobScheduling(int[] startTime, int[] endTime, int[] profit) { + int n = startTime.Length; + var intervals = new List<(int start, int end, int profit)>(); + + for (int i = 0; i < n; i++) { + intervals.Add((startTime[i], endTime[i], profit[i])); + } + + intervals.Sort((a, b) => a.start.CompareTo(b.start)); + Dictionary cache = new(); + + int Dfs(int i) { + if (i == n) return 0; + if (cache.ContainsKey(i)) return cache[i]; + + // Option 1: don't include + int res = Dfs(i + 1); + + // Option 2: include current job + int j = i + 1; + while (j < n && intervals[j].start < intervals[i].end) { + j++; + } + + res = Math.Max(res, intervals[i].profit + Dfs(j)); + cache[i] = res; + return res; + } + + return Dfs(0); + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -328,6 +364,48 @@ class Solution { } ``` +```csharp +public class Solution { + private int[][] intervals; + private int[] cache; + + public int JobScheduling(int[] startTime, int[] endTime, int[] profit) { + int n = startTime.Length; + intervals = new int[n][]; + cache = new int[n]; + Array.Fill(cache, -1); + + for (int i = 0; i < n; i++) { + intervals[i] = new int[] { startTime[i], endTime[i], profit[i] }; + } + + Array.Sort(intervals, (a, b) => a[0].CompareTo(b[0])); + return Dfs(0); + } + + private int Dfs(int i) { + if (i == intervals.Length) return 0; + if (cache[i] != -1) return cache[i]; + + int res = Dfs(i + 1); + + int left = i + 1, right = intervals.Length, j = intervals.Length; + while (left < right) { + int mid = left + (right - left) / 2; + if (intervals[mid][0] >= intervals[i][1]) { + j = mid; + right = mid; + } else { + left = mid + 1; + } + } + + cache[i] = Math.Max(res, intervals[i][2] + Dfs(j)); + return cache[i]; + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -517,6 +595,52 @@ class Solution { } ``` +```csharp +public class Solution { + private int[] startTime, endTime, profit, cache; + private int[] index; + private int n; + + public int JobScheduling(int[] startTime, int[] endTime, int[] profit) { + this.n = startTime.Length; + this.startTime = startTime; + this.endTime = endTime; + this.profit = profit; + this.index = new int[n]; + this.cache = new int[n]; + Array.Fill(cache, -1); + + for (int i = 0; i < n; i++) { + index[i] = i; + } + + Array.Sort(index, (a, b) => startTime[a].CompareTo(startTime[b])); + + return Dfs(0); + } + + private int Dfs(int i) { + if (i == n) return 0; + if (cache[i] != -1) return cache[i]; + + int res = Dfs(i + 1); + + int left = i + 1, right = n, j = n; + while (left < right) { + int mid = left + (right - left) / 2; + if (startTime[index[mid]] >= endTime[index[i]]) { + j = mid; + right = mid; + } else { + left = mid + 1; + } + } + + return cache[i] = Math.Max(res, profit[index[i]] + Dfs(j)); + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -647,6 +771,37 @@ class Solution { } ``` +```csharp +public class Solution { + public int JobScheduling(int[] startTime, int[] endTime, int[] profit) { + int n = startTime.Length; + int[] index = new int[n]; + for (int i = 0; i < n; i++) index[i] = i; + + Array.Sort(index, (a, b) => startTime[a].CompareTo(startTime[b])); + + int[] dp = new int[n + 1]; + + for (int i = n - 1; i >= 0; i--) { + int left = i + 1, right = n, j = n; + while (left < right) { + int mid = left + (right - left) / 2; + if (startTime[index[mid]] >= endTime[index[i]]) { + j = mid; + right = mid; + } else { + left = mid + 1; + } + } + + dp[i] = Math.Max(dp[i + 1], profit[index[i]] + dp[j]); + } + + return dp[0]; + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/put-marbles-in-bags.md b/articles/put-marbles-in-bags.md new file mode 100644 index 000000000..a0abf8a29 --- /dev/null +++ b/articles/put-marbles-in-bags.md @@ -0,0 +1,501 @@ +## 1. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def putMarbles(self, weights: List[int], k: int) -> int: + n = len(weights) + cache = {} + + def dfs(i, k): + if (i, k) in cache: + return cache[(i, k)] + if k == 0: + return [0, 0] + if i == n - 1 or n - i - 1 < k: + return [-float("inf"), float("inf")] + + res = [0, float("inf")] # [maxScore, minScore] + + # make partition + cur = dfs(i + 1, k - 1) + res[0] = max(res[0], weights[i] + weights[i + 1] + cur[0]) + res[1] = min(res[1], weights[i] + weights[i + 1] + cur[1]) + + # skip + cur = dfs(i + 1, k) + res[0] = max(res[0], cur[0]) + res[1] = min(res[1], cur[1]) + + cache[(i, k)] = res + return res + + ans = dfs(0, k - 1) + return ans[0] - ans[1] +``` + +```java +public class Solution { + Map cache = new HashMap<>(); + + public long putMarbles(int[] weights, int k) { + int n = weights.length; + long[] ans = dfs(0, k - 1, weights, n); + return ans[0] - ans[1]; + } + + private long[] dfs(int i, int k, int[] weights, int n) { + String key = i + "," + k; + if (cache.containsKey(key)) return cache.get(key); + if (k == 0) return new long[]{0L, 0L}; + if (i == n - 1 || n - i - 1 < k) { + return new long[]{(long)-1e15, (long)1e15}; + } + + long[] res = new long[]{0L, (long)1e15}; + + long[] cur = dfs(i + 1, k - 1, weights, n); + res[0] = Math.max(res[0], weights[i] + weights[i + 1] + cur[0]); + res[1] = Math.min(res[1], weights[i] + weights[i + 1] + cur[1]); + + cur = dfs(i + 1, k, weights, n); + res[0] = Math.max(res[0], cur[0]); + res[1] = Math.min(res[1], cur[1]); + + cache.put(key, res); + return res; + } +} +``` + +```cpp +class Solution { +public: + unordered_map> cache; + + long long putMarbles(vector& weights, int k) { + int n = weights.size(); + auto ans = dfs(0, k - 1, weights, n); + return ans.first - ans.second; + } + + pair dfs(int i, int k, vector& weights, int n) { + string key = to_string(i) + "," + to_string(k); + if (cache.count(key)) return cache[key]; + if (k == 0) return {0LL, 0LL}; + if (i == n - 1 || n - i - 1 < k) { + return {-1000000000000000LL, 1000000000000000LL}; + } + + pair res = {0LL, 1000000000000000LL}; + + auto cur = dfs(i + 1, k - 1, weights, n); + res.first = max(res.first, (long long)weights[i] + weights[i + 1] + cur.first); + res.second = min(res.second, (long long)weights[i] + weights[i + 1] + cur.second); + + cur = dfs(i + 1, k, weights, n); + res.first = max(res.first, cur.first); + res.second = min(res.second, cur.second); + + return cache[key] = res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} weights + * @param {number} k + * @return {number} + */ + putMarbles(weights, k) { + const n = weights.length; + const cache = new Map(); + + const dfs = (i, k) => { + const key = `${i},${k}`; + if (cache.has(key)) return cache.get(key); + if (k === 0) return [0, 0]; + if (i === n - 1 || n - i - 1 < k) return [-1e15, 1e15]; + + let res = [0, 1e15]; + + let cur = dfs(i + 1, k - 1); + res[0] = Math.max(res[0], weights[i] + weights[i + 1] + cur[0]); + res[1] = Math.min(res[1], weights[i] + weights[i + 1] + cur[1]); + + cur = dfs(i + 1, k); + res[0] = Math.max(res[0], cur[0]); + res[1] = Math.min(res[1], cur[1]); + + cache.set(key, res); + return res; + }; + + const ans = dfs(0, k - 1); + return ans[0] - ans[1]; + } +} +``` + +```csharp +public class Solution { + Dictionary cache = new Dictionary(); + + public long PutMarbles(int[] weights, int k) { + int n = weights.Length; + long[] ans = Dfs(0, k - 1, weights, n); + return (int)(ans[0] - ans[1]); + } + + private long[] Dfs(int i, int k, int[] weights, int n) { + string key = $"{i},{k}"; + if (cache.ContainsKey(key)) return cache[key]; + if (k == 0) return new long[] { 0L, 0L }; + if (i == n - 1 || n - i - 1 < k) { + return new long[] { -1000000000000000L, 1000000000000000L }; + } + + long[] res = new long[] { 0L, 1000000000000000L }; + + long[] cur = Dfs(i + 1, k - 1, weights, n); + res[0] = Math.Max(res[0], weights[i] + weights[i + 1] + cur[0]); + res[1] = Math.Min(res[1], weights[i] + weights[i + 1] + cur[1]); + + cur = Dfs(i + 1, k, weights, n); + res[0] = Math.Max(res[0], cur[0]); + res[1] = Math.Min(res[1], cur[1]); + + cache[key] = res; + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * k)$ +* Space complexity: $O(n * k)$ + +> Where $n$ is the number of marbles, and $k$ is the number of bags. + +--- + +## 2. Greedy + Sorting + +::tabs-start + +```python +class Solution: + def putMarbles(self, weights: List[int], k: int) -> int: + if k == 1: + return 0 + + splits = [] + for i in range(len(weights) - 1): + splits.append(weights[i] + weights[i + 1]) + + splits.sort() + i = k - 1 + + max_score = sum(splits[-i:]) + min_score = sum(splits[:i]) + return max_score - min_score +``` + +```java +public class Solution { + public long putMarbles(int[] weights, int k) { + if (k == 1) return 0L; + + int n = weights.length; + List splits = new ArrayList<>(); + + for (int i = 0; i < n - 1; i++) { + splits.add(weights[i] + weights[i + 1]); + } + + Collections.sort(splits); + int i = k - 1; + long maxScore = 0, minScore = 0; + + for (int j = 0; j < i; j++) minScore += splits.get(j); + for (int j = splits.size() - i; j < splits.size(); j++) { + maxScore += splits.get(j); + } + + return maxScore - minScore; + } +} +``` + +```cpp +class Solution { +public: + long long putMarbles(vector& weights, int k) { + if (k == 1) return 0LL; + + int n = weights.size(); + vector splits; + + for (int i = 0; i < n - 1; ++i) { + splits.push_back(weights[i] + weights[i + 1]); + } + + sort(splits.begin(), splits.end()); + int i = k - 1; + long long minScore = 0, maxScore = 0; + + for (int j = 0; j < i; ++j) minScore += splits[j]; + for (int j = splits.size() - i; j < splits.size(); ++j) { + maxScore += splits[j]; + } + + return maxScore - minScore; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} weights + * @param {number} k + * @return {number} + */ + putMarbles(weights, k) { + if (k === 1) return 0; + + const splits = []; + for (let i = 0; i < weights.length - 1; i++) { + splits.push(weights[i] + weights[i + 1]); + } + + splits.sort((a, b) => a - b); + const i = k - 1; + + let minScore = 0, maxScore = 0; + for (let j = 0; j < i; j++) minScore += splits[j]; + for (let j = splits.length - i; j < splits.length; j++) { + maxScore += splits[j]; + } + + return maxScore - minScore; + } +} +``` + +```csharp +public class Solution { + public long PutMarbles(int[] weights, int k) { + if (k == 1) return 0L; + + int n = weights.Length; + List splits = new List(); + + for (int i = 0; i < n - 1; i++) { + splits.Add(weights[i] + weights[i + 1]); + } + + splits.Sort(); + int iVal = k - 1; + long minScore = 0, maxScore = 0; + + for (int j = 0; j < iVal; j++) minScore += splits[j]; + for (int j = splits.Count - iVal; j < splits.Count; j++) { + maxScore += splits[j]; + } + + return maxScore - minScore; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ + +> Where $n$ is the number of marbles, and $k$ is the number of bags. + +--- + +## 3. Heap + +::tabs-start + +```python +class Solution: + def putMarbles(self, weights: List[int], k: int) -> int: + if k == 1: + return 0 + + max_heap = [] + min_heap = [] + + for i in range(len(weights) - 1): + split = weights[i] + weights[i + 1] + + if len(max_heap) < k - 1: + heapq.heappush(max_heap, split) + else: + heapq.heappushpop(max_heap, split) + + if len(min_heap) < k - 1: + heapq.heappush(min_heap, -split) + else: + heapq.heappushpop(min_heap, -split) + + max_score = sum(max_heap) + min_score = -sum(min_heap) + return max_score - min_score +``` + +```java +public class Solution { + public long putMarbles(int[] weights, int k) { + if (k == 1) return 0L; + + PriorityQueue maxHeap = new PriorityQueue<>(); + PriorityQueue minHeap = new PriorityQueue<>(Collections.reverseOrder()); + + for (int i = 0; i < weights.length - 1; i++) { + int split = weights[i] + weights[i + 1]; + + if (maxHeap.size() < k - 1) maxHeap.offer(split); + else if (split > maxHeap.peek()) { + maxHeap.poll(); + maxHeap.offer(split); + } + + if (minHeap.size() < k - 1) minHeap.offer(split); + else if (split < minHeap.peek()) { + minHeap.poll(); + minHeap.offer(split); + } + } + + long maxScore = 0, minScore = 0; + for (int val : maxHeap) maxScore += val; + for (int val : minHeap) minScore += val; + + return maxScore - minScore; + } +} +``` + +```cpp +class Solution { +public: + long long putMarbles(vector& weights, int k) { + if (k == 1) return 0LL; + + priority_queue, greater> maxHeap; + priority_queue minHeap; + + for (int i = 0; i < weights.size() - 1; ++i) { + int split = weights[i] + weights[i + 1]; + + if ((int)maxHeap.size() < k - 1) maxHeap.push(split); + else if (split > maxHeap.top()) { + maxHeap.pop(); + maxHeap.push(split); + } + + if ((int)minHeap.size() < k - 1) minHeap.push(split); + else if (split < minHeap.top()) { + minHeap.pop(); + minHeap.push(split); + } + } + + long long maxScore = 0, minScore = 0; + while (!maxHeap.empty()) { + maxScore += maxHeap.top(); + maxHeap.pop(); + } + while (!minHeap.empty()) { + minScore += minHeap.top(); + minHeap.pop(); + } + + return maxScore - minScore; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} weights + * @param {number} k + * @return {number} + */ + putMarbles(weights, k) { + if (k === 1) return 0; + + const minHeap = new MinPriorityQueue(); + const maxHeap = new MaxPriorityQueue(); + + for (let i = 0; i < weights.length - 1; i++) { + const sum = weights[i] + weights[i + 1]; + + minHeap.enqueue(sum); + if (minHeap.size() > k - 1) minHeap.dequeue(); + + maxHeap.enqueue(sum); + if (maxHeap.size() > k - 1) maxHeap.dequeue(); + } + + let maxScore = 0, minScore = 0; + while (!minHeap.isEmpty()) maxScore += minHeap.dequeue(); + while (!maxHeap.isEmpty()) minScore += maxHeap.dequeue(); + + return maxScore - minScore; + } +} +``` + +```csharp +public class Solution { + public long PutMarbles(int[] weights, int k) { + if (k == 1) return 0L; + + var maxHeap = new PriorityQueue(); + var minHeap = new PriorityQueue( + Comparer.Create((a, b) => b.CompareTo(a)) + ); + + for (int i = 0; i < weights.Length - 1; i++) { + int split = weights[i] + weights[i + 1]; + + maxHeap.Enqueue(split, split); + if (maxHeap.Count > k - 1) maxHeap.Dequeue(); + + minHeap.Enqueue(split, split); + if (minHeap.Count > k - 1) minHeap.Dequeue(); + } + + long maxScore = 0, minScore = 0; + while (maxHeap.Count > 0) maxScore += maxHeap.Dequeue(); + while (minHeap.Count > 0) minScore += minHeap.Dequeue(); + + return maxScore - minScore; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log k)$ +* Space complexity: $O(k)$ + +> Where $n$ is the number of marbles, and $k$ is the number of bags. \ No newline at end of file diff --git a/articles/reorganize-string.md b/articles/reorganize-string.md index 57d672327..868c78dca 100644 --- a/articles/reorganize-string.md +++ b/articles/reorganize-string.md @@ -183,6 +183,42 @@ class Solution { } ``` +```csharp +public class Solution { + public string ReorganizeString(string s) { + int[] freq = new int[26]; + foreach (char c in s) { + freq[c - 'a']++; + } + + int maxFreq = freq.Max(); + if (maxFreq > (s.Length + 1) / 2) { + return ""; + } + + List res = new List(); + while (res.Count < s.Length) { + int maxIdx = Array.IndexOf(freq, freq.Max()); + char ch = (char)(maxIdx + 'a'); + res.Add(ch); + freq[maxIdx]--; + + if (freq[maxIdx] == 0) continue; + + int tmp = freq[maxIdx]; + freq[maxIdx] = int.MinValue; + int nextMaxIdx = Array.IndexOf(freq, freq.Max()); + char nextCh = (char)(nextMaxIdx + 'a'); + res.Add(nextCh); + freq[maxIdx] = tmp; + freq[nextMaxIdx]--; + } + + return new string(res.ToArray()); + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -352,6 +388,45 @@ class Solution { } ``` +```csharp +public class Solution { + public string ReorganizeString(string s) { + Dictionary count = new(); + foreach (char c in s) { + if (!count.ContainsKey(c)) count[c] = 0; + count[c]++; + } + + PriorityQueue maxHeap = new(); + foreach (var kvp in count) { + maxHeap.Enqueue(new int[] { kvp.Value, kvp.Key }, -kvp.Value); + } + + string res = ""; + int[] prev = null; + + while (maxHeap.Count > 0 || prev != null) { + if (prev != null && maxHeap.Count == 0) return ""; + + int[] curr = maxHeap.Dequeue(); + res += (char)curr[1]; + curr[0]--; + + if (prev != null) { + maxHeap.Enqueue(prev, -prev[0]); + prev = null; + } + + if (curr[0] > 0) { + prev = curr; + } + } + + return res; + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -536,6 +611,48 @@ class Solution { } ``` +```csharp +public class Solution { + public string ReorganizeString(string s) { + int[] freq = new int[26]; + foreach (char c in s) { + freq[c - 'a']++; + } + + int maxIdx = 0; + for (int i = 1; i < 26; i++) { + if (freq[i] > freq[maxIdx]) { + maxIdx = i; + } + } + + int maxFreq = freq[maxIdx]; + if (maxFreq > (s.Length + 1) / 2) return ""; + + char[] res = new char[s.Length]; + int idx = 0; + char maxChar = (char)(maxIdx + 'a'); + + while (freq[maxIdx] > 0) { + res[idx] = maxChar; + idx += 2; + freq[maxIdx]--; + } + + for (int i = 0; i < 26; i++) { + while (freq[i] > 0) { + if (idx >= s.Length) idx = 1; + res[idx] = (char)(i + 'a'); + idx += 2; + freq[i]--; + } + } + + return new string(res); + } +} +``` + ::tabs-end ### Time & Space Complexity From 1c50915ffda8db92d3b40a8e0292718903f44837 Mon Sep 17 00:00:00 2001 From: Sri Hari <94112314+Srihari2222@users.noreply.github.com> Date: Mon, 28 Apr 2025 22:58:15 +0530 Subject: [PATCH 42/45] Sri Hari: Batch-6/Neetcode-All/Added-articles (#4074) * Batch-6/Neetcode-ALL/Added-articles * Batch-6/Neetcode-ALL/Added-articles --- .../best-time-to-buy-and-sell-stock-ii.md | 109 +++++++ articles/boats-to-save-people.md | 59 ++++ articles/concatenation-of-array.md | 30 +- articles/first-missing-positive.md | 131 +++++++++ articles/longest-common-prefix.md | 120 +++++++- articles/majority-element-ii.md | 149 ++++++++++ articles/majority-element.md | 112 ++++++++ articles/merge-sorted-array.md | 74 +++++ articles/merge-strings-alternately.md | 64 +++++ articles/remove-element.md | 49 ++++ articles/rotate-array.md | 96 +++++++ articles/sort-an-array.md | 267 ++++++++++++++++++ articles/sort-colors.md | 93 ++++++ articles/subarray-sum-equals-k.md | 44 +++ 14 files changed, 1395 insertions(+), 2 deletions(-) diff --git a/articles/best-time-to-buy-and-sell-stock-ii.md b/articles/best-time-to-buy-and-sell-stock-ii.md index e6abb861a..6562e3544 100644 --- a/articles/best-time-to-buy-and-sell-stock-ii.md +++ b/articles/best-time-to-buy-and-sell-stock-ii.md @@ -85,6 +85,30 @@ class Solution { } ``` +```csharp +public class Solution { + public int MaxProfit(int[] prices) { + return Rec(prices, 0, false); + } + + private int Rec(int[] prices, int i, bool bought) { + if (i == prices.Length) { + return 0; + } + + int res = Rec(prices, i + 1, bought); + + if (bought) { + res = Math.Max(res, prices[i] + Rec(prices, i + 1, false)); + } else { + res = Math.Max(res, -prices[i] + Rec(prices, i + 1, true)); + } + + return res; + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -211,6 +235,43 @@ class Solution { } ``` +```csharp +public class Solution { + public int MaxProfit(int[] prices) { + int n = prices.Length; + int[,] dp = new int[n, 2]; + + for (int i = 0; i < n; i++) { + dp[i, 0] = -1; + dp[i, 1] = -1; + } + + return Rec(prices, 0, 0, dp); + } + + private int Rec(int[] prices, int i, int bought, int[,] dp) { + if (i == prices.Length) { + return 0; + } + + if (dp[i, bought] != -1) { + return dp[i, bought]; + } + + int res = Rec(prices, i + 1, bought, dp); + + if (bought == 1) { + res = Math.Max(res, prices[i] + Rec(prices, i + 1, 0, dp)); + } else { + res = Math.Max(res, -prices[i] + Rec(prices, i + 1, 1, dp)); + } + + dp[i, bought] = res; + return res; + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -290,6 +351,22 @@ class Solution { } ``` +```csharp +public class Solution { + public int MaxProfit(int[] prices) { + int n = prices.Length; + int[,] dp = new int[n + 1, 2]; + + for (int i = n - 1; i >= 0; i--) { + dp[i, 0] = Math.Max(dp[i + 1, 0], -prices[i] + dp[i + 1, 1]); + dp[i, 1] = Math.Max(dp[i + 1, 1], prices[i] + dp[i + 1, 0]); + } + + return dp[0, 0]; + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -378,6 +455,24 @@ class Solution { } ``` +```csharp +public class Solution { + public int MaxProfit(int[] prices) { + int nextBuy = 0, nextSell = 0; + int curBuy = 0, curSell = 0; + + for (int i = prices.Length - 1; i >= 0; i--) { + curBuy = Math.Max(nextBuy, -prices[i] + nextSell); + curSell = Math.Max(nextSell, prices[i] + nextBuy); + nextBuy = curBuy; + nextSell = curSell; + } + + return curBuy; + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -450,6 +545,20 @@ class Solution { } ``` +```csharp +public class Solution { + public int MaxProfit(int[] prices) { + int profit = 0; + for (int i = 1; i < prices.Length; i++) { + if (prices[i] > prices[i - 1]) { + profit += prices[i] - prices[i - 1]; + } + } + return profit; + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/boats-to-save-people.md b/articles/boats-to-save-people.md index 8abd6a9a7..ce0498e89 100644 --- a/articles/boats-to-save-people.md +++ b/articles/boats-to-save-people.md @@ -73,6 +73,27 @@ class Solution { } ``` +```csharp +public class Solution { + public int NumRescueBoats(int[] people, int limit) { + Array.Sort(people); + int res = 0, l = 0, r = people.Length - 1; + + while (l <= r) { + int remain = limit - people[r]; + r--; + res++; + + if (l <= r && remain >= people[l]) { + l++; + } + } + + return res; + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -211,6 +232,44 @@ class Solution { } ``` +```csharp +public class Solution { + public int NumRescueBoats(int[] people, int limit) { + int m = 0; + foreach (int p in people) { + m = Math.Max(m, p); + } + + int[] count = new int[m + 1]; + foreach (int p in people) { + count[p]++; + } + + int idx = 0, iVal = 1; + while (idx < people.Length) { + while (count[iVal] == 0) { + iVal++; + } + people[idx] = iVal; + count[iVal]--; + idx++; + } + + int res = 0, l = 0, r = people.Length - 1; + while (l <= r) { + int remain = limit - people[r]; + r--; + res++; + if (l <= r && remain >= people[l]) { + l++; + } + } + + return res; + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/concatenation-of-array.md b/articles/concatenation-of-array.md index 3ea2a1e68..f3740ed68 100644 --- a/articles/concatenation-of-array.md +++ b/articles/concatenation-of-array.md @@ -15,7 +15,7 @@ class Solution: ```java public class Solution { public int[] getConcatenation(int[] nums) { - int[] ans=new int[2 * nums.length]; + int[] ans = new int[2 * nums.length]; int idx = 0; for (int i = 0; i < 2; i++) { for (int num : nums) { @@ -60,6 +60,21 @@ class Solution { } ``` +```csharp +public class Solution { + public int[] GetConcatenation(int[] nums) { + int[] ans = new int[2 * nums.Length]; + int idx = 0; + for (int i = 0; i < 2; i++) { + foreach (int num in nums) { + ans[idx++] = num; + } + } + return ans; + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -127,6 +142,19 @@ class Solution { } ``` +```csharp +public class Solution { + public int[] GetConcatenation(int[] nums) { + int n = nums.Length; + int[] ans = new int[2 * n]; + for (int i = 0; i < n; i++) { + ans[i] = ans[i + n] = nums[i]; + } + return ans; + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/first-missing-positive.md b/articles/first-missing-positive.md index f0bef0643..09f5f9678 100644 --- a/articles/first-missing-positive.md +++ b/articles/first-missing-positive.md @@ -80,6 +80,31 @@ class Solution { } ``` +```csharp +public class Solution { + public int FirstMissingPositive(int[] nums) { + int missing = 1; + + while (true) { + bool found = false; + + foreach (int num in nums) { + if (num == missing) { + found = true; + break; + } + } + + if (!found) { + return missing; + } + + missing++; + } + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -183,6 +208,29 @@ class Solution { } ``` +```csharp +public class Solution { + public int FirstMissingPositive(int[] nums) { + int n = nums.Length; + bool[] seen = new bool[n]; + + foreach (int num in nums) { + if (num > 0 && num <= n) { + seen[num - 1] = true; + } + } + + for (int num = 1; num <= n; num++) { + if (!seen[num - 1]) { + return num; + } + } + + return n + 1; + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -257,6 +305,23 @@ class Solution { } ``` +```csharp +public class Solution { + public int FirstMissingPositive(int[] nums) { + Array.Sort(nums); + int missing = 1; + + foreach (int num in nums) { + if (num > 0 && num == missing) { + missing++; + } + } + + return missing; + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -396,6 +461,39 @@ class Solution { } ``` +```csharp +public class Solution { + public int FirstMissingPositive(int[] nums) { + int n = nums.Length; + + for (int i = 0; i < n; i++) { + if (nums[i] < 0) { + nums[i] = 0; + } + } + + for (int i = 0; i < n; i++) { + int val = Math.Abs(nums[i]); + if (val >= 1 && val <= n) { + if (nums[val - 1] > 0) { + nums[val - 1] *= -1; + } else if (nums[val - 1] == 0) { + nums[val - 1] = -(n + 1); + } + } + } + + for (int i = 0; i < n; i++) { + if (nums[i] >= 0) { + return i + 1; + } + } + + return n + 1; + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -528,6 +626,39 @@ class Solution { } ``` +```csharp +public class Solution { + public int FirstMissingPositive(int[] nums) { + int n = nums.Length; + int i = 0; + + while (i < n) { + if (nums[i] <= 0 || nums[i] > n) { + i++; + continue; + } + + int index = nums[i] - 1; + if (nums[i] != nums[index]) { + int temp = nums[i]; + nums[i] = nums[index]; + nums[index] = temp; + } else { + i++; + } + } + + for (i = 0; i < n; i++) { + if (nums[i] != i + 1) { + return i + 1; + } + } + + return n + 1; + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/longest-common-prefix.md b/articles/longest-common-prefix.md index e5941f9a6..936377d1c 100644 --- a/articles/longest-common-prefix.md +++ b/articles/longest-common-prefix.md @@ -78,6 +78,27 @@ class Solution { } ``` +```csharp +public class Solution { + public string LongestCommonPrefix(string[] strs) { + string prefix = strs[0]; + + for (int i = 1; i < strs.Length; i++) { + int j = 0; + while (j < Math.Min(prefix.Length, strs[i].Length)) { + if (prefix[j] != strs[i][j]) { + break; + } + j++; + } + prefix = prefix.Substring(0, j); + } + + return prefix; + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -153,6 +174,21 @@ class Solution { } ``` +```csharp +public class Solution { + public string LongestCommonPrefix(string[] strs) { + for (int i = 0; i < strs[0].Length; i++) { + foreach (string s in strs) { + if (i == s.Length || s[i] != strs[0][i]) { + return s.Substring(0, i); + } + } + } + return strs[0]; + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -242,6 +278,30 @@ class Solution { } ``` +```csharp +public class Solution { + public string LongestCommonPrefix(string[] strs) { + if (strs.Length == 1) { + return strs[0]; + } + + Array.Sort(strs); + string first = strs[0]; + string last = strs[strs.Length - 1]; + + int i = 0; + while (i < Math.Min(first.Length, last.Length)) { + if (first[i] != last[i]) { + return first.Substring(0, i); + } + i++; + } + + return first; + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -493,6 +553,64 @@ class Solution { } ``` +```csharp +public class TrieNode { + public Dictionary Children = new Dictionary(); +} + +public class Trie { + public TrieNode Root; + + public Trie() { + Root = new TrieNode(); + } + + public void Insert(string word) { + TrieNode node = Root; + foreach (char c in word) { + if (!node.Children.ContainsKey(c)) { + node.Children[c] = new TrieNode(); + } + node = node.Children[c]; + } + } + + public int Lcp(string word, int prefixLen) { + TrieNode node = Root; + for (int i = 0; i < Math.Min(word.Length, prefixLen); i++) { + if (!node.Children.ContainsKey(word[i])) { + return i; + } + node = node.Children[word[i]]; + } + return Math.Min(word.Length, prefixLen); + } +} + +public class Solution { + public string LongestCommonPrefix(string[] strs) { + if (strs.Length == 1) return strs[0]; + + int mini = 0; + for (int i = 1; i < strs.Length; i++) { + if (strs[i].Length < strs[mini].Length) { + mini = i; + } + } + + Trie trie = new Trie(); + trie.Insert(strs[mini]); + + int prefixLen = strs[mini].Length; + for (int i = 0; i < strs.Length; i++) { + prefixLen = trie.Lcp(strs[i], prefixLen); + } + + return strs[0].Substring(0, prefixLen); + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -500,4 +618,4 @@ class Solution { * Time complexity: $O(n * m)$ * Space complexity: $O(n)$ -> Where $n$ is the length of the shortest string and $m$ is the number of strings. +> Where $n$ is the length of the shortest string and $m$ is the number of strings. \ No newline at end of file diff --git a/articles/majority-element-ii.md b/articles/majority-element-ii.md index 6ba0f2fd1..aca6e49c3 100644 --- a/articles/majority-element-ii.md +++ b/articles/majority-element-ii.md @@ -72,6 +72,24 @@ class Solution { } ``` +```csharp +public class Solution { + public List MajorityElement(int[] nums) { + HashSet res = new HashSet(); + int n = nums.Length; + + foreach (int num in nums) { + int count = nums.Count(x => x == num); + if (count > n / 3) { + res.Add(num); + } + } + + return res.ToList(); + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -180,6 +198,30 @@ class Solution { } ``` +```csharp +public class Solution { + public List MajorityElement(int[] nums) { + Array.Sort(nums); + List res = new List(); + int n = nums.Length; + + int i = 0; + while (i < n) { + int j = i + 1; + while (j < n && nums[j] == nums[i]) { + j++; + } + if (j - i > n / 3) { + res.Add(nums[i]); + } + i = j; + } + + return res; + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -271,6 +313,31 @@ class Solution { } ``` +```csharp +public class Solution { + public List MajorityElement(int[] nums) { + Dictionary count = new Dictionary(); + List res = new List(); + int n = nums.Length; + + foreach (int num in nums) { + if (!count.ContainsKey(num)) { + count[num] = 0; + } + count[num]++; + } + + foreach (var kvp in count) { + if (kvp.Value > n / 3) { + res.Add(kvp.Key); + } + } + + return res; + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -444,6 +511,45 @@ class Solution { } ``` +```csharp +public class Solution { + public List MajorityElement(int[] nums) { + int n = nums.Length; + int num1 = -1, num2 = -1; + int cnt1 = 0, cnt2 = 0; + + foreach (int num in nums) { + if (num == num1) { + cnt1++; + } else if (num == num2) { + cnt2++; + } else if (cnt1 == 0) { + num1 = num; + cnt1 = 1; + } else if (cnt2 == 0) { + num2 = num; + cnt2 = 1; + } else { + cnt1--; + cnt2--; + } + } + + cnt1 = cnt2 = 0; + foreach (int num in nums) { + if (num == num1) cnt1++; + else if (num == num2) cnt2++; + } + + List res = new List(); + if (cnt1 > n / 3) res.Add(num1); + if (cnt2 > n / 3) res.Add(num2); + + return res; + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -589,6 +695,49 @@ class Solution { } ``` +```csharp +public class Solution { + public List MajorityElement(int[] nums) { + Dictionary count = new Dictionary(); + + foreach (int num in nums) { + if (count.ContainsKey(num)) { + count[num]++; + } else { + count[num] = 1; + } + + if (count.Count <= 2) { + continue; + } + + Dictionary newCount = new Dictionary(); + foreach (var kvp in count) { + if (kvp.Value > 1) { + newCount[kvp.Key] = kvp.Value - 1; + } + } + count = newCount; + } + + List res = new List(); + foreach (int candidate in count.Keys) { + int freq = 0; + foreach (int num in nums) { + if (num == candidate) { + freq++; + } + } + if (freq > nums.Length / 3) { + res.Add(candidate); + } + } + + return res; + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/majority-element.md b/articles/majority-element.md index 9ac32df2c..225347051 100644 --- a/articles/majority-element.md +++ b/articles/majority-element.md @@ -72,6 +72,26 @@ class Solution { } ``` +```csharp +public class Solution { + public int MajorityElement(int[] nums) { + int n = nums.Length; + foreach (int num in nums) { + int count = 0; + foreach (int i in nums) { + if (i == num) { + count++; + } + } + if (count > n / 2) { + return num; + } + } + return -1; + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -158,6 +178,29 @@ class Solution { } ``` +```csharp +public class Solution { + public int MajorityElement(int[] nums) { + Dictionary count = new Dictionary(); + int res = 0, maxCount = 0; + + foreach (int num in nums) { + if (!count.ContainsKey(num)) { + count[num] = 0; + } + count[num]++; + + if (count[num] > maxCount) { + res = num; + maxCount = count[num]; + } + } + + return res; + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -210,6 +253,15 @@ class Solution { } ``` +```csharp +public class Solution { + public int MajorityElement(int[] nums) { + Array.Sort(nums); + return nums[nums.Length / 2]; + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -313,6 +365,30 @@ class Solution { } ``` +```csharp +public class Solution { + public int MajorityElement(int[] nums) { + int n = nums.Length; + int[] bit = new int[32]; + + foreach (int num in nums) { + for (int i = 0; i < 32; i++) { + bit[i] += (num >> i) & 1; + } + } + + int res = 0; + for (int i = 0; i < 32; i++) { + if (bit[i] > n / 2) { + res |= (1 << i); + } + } + + return res; + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -393,6 +469,23 @@ class Solution { } ``` +```csharp +public class Solution { + public int MajorityElement(int[] nums) { + int res = 0, count = 0; + + foreach (int num in nums) { + if (count == 0) { + res = num; + } + count += (num == res) ? 1 : -1; + } + + return res; + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -484,6 +577,25 @@ class Solution { } ``` +```csharp +public class Solution { + private static Random random = new Random(); + + public int MajorityElement(int[] nums) { + int n = nums.Length; + + while (true) { + int candidate = nums[random.Next(n)]; + int count = nums.Count(x => x == candidate); + + if (count > n / 2) { + return candidate; + } + } + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/merge-sorted-array.md b/articles/merge-sorted-array.md index aab35dc73..a0e701ef0 100644 --- a/articles/merge-sorted-array.md +++ b/articles/merge-sorted-array.md @@ -53,6 +53,17 @@ class Solution { } ``` +```csharp +public class Solution { + public void Merge(int[] nums1, int m, int[] nums2, int n) { + for (int i = 0; i < n; i++) { + nums1[i + m] = nums2[i]; + } + Array.Sort(nums1); + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -146,6 +157,25 @@ class Solution { } ``` +```csharp +public class Solution { + public void Merge(int[] nums1, int m, int[] nums2, int n) { + int[] nums1Copy = new int[m]; + Array.Copy(nums1, nums1Copy, m); + + int idx = 0, i = 0, j = 0; + + while (idx < m + n) { + if (j >= n || (i < m && nums1Copy[i] <= nums2[j])) { + nums1[idx++] = nums1Copy[i++]; + } else { + nums1[idx++] = nums2[j++]; + } + } + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -270,6 +300,33 @@ class Solution { } ``` +```csharp +public class Solution { + public void Merge(int[] nums1, int m, int[] nums2, int n) { + int last = m + n - 1; + + // Merge in reverse order + while (m > 0 && n > 0) { + if (nums1[m - 1] > nums2[n - 1]) { + nums1[last] = nums1[m - 1]; + m--; + } else { + nums1[last] = nums2[n - 1]; + n--; + } + last--; + } + + // Fill nums1 with leftover nums2 elements + while (n > 0) { + nums1[last] = nums2[n - 1]; + n--; + last--; + } + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -364,6 +421,23 @@ class Solution { } ``` +```csharp +public class Solution { + public void Merge(int[] nums1, int m, int[] nums2, int n) { + int last = m + n - 1; + int i = m - 1, j = n - 1; + + while (j >= 0) { + if (i >= 0 && nums1[i] > nums2[j]) { + nums1[last--] = nums1[i--]; + } else { + nums1[last--] = nums2[j--]; + } + } + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/merge-strings-alternately.md b/articles/merge-strings-alternately.md index 04d95926d..d61a657cd 100644 --- a/articles/merge-strings-alternately.md +++ b/articles/merge-strings-alternately.md @@ -70,6 +70,27 @@ class Solution { } ``` +```csharp +public class Solution { + public string MergeAlternately(string word1, string word2) { + int i = 0, j = 0; + StringBuilder res = new StringBuilder(); + + while (i < word1.Length && j < word2.Length) { + res.Append(word1[i]); + res.Append(word2[j]); + i++; + j++; + } + + res.Append(word1.Substring(i)); + res.Append(word2.Substring(j)); + + return res.ToString(); + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -152,6 +173,29 @@ class Solution { } ``` +```csharp +public class Solution { + public string MergeAlternately(string word1, string word2) { + int n = word1.Length, m = word2.Length; + int i = 0, j = 0; + StringBuilder res = new StringBuilder(); + + while (i < n || j < m) { + if (i < n) { + res.Append(word1[i]); + } + if (j < m) { + res.Append(word2[j]); + } + i++; + j++; + } + + return res.ToString(); + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -240,6 +284,26 @@ class Solution { } ``` +```csharp +public class Solution { + public string MergeAlternately(string word1, string word2) { + int n = word1.Length, m = word2.Length; + StringBuilder res = new StringBuilder(); + + for (int i = 0; i < Math.Max(n, m); i++) { + if (i < n) { + res.Append(word1[i]); + } + if (i < m) { + res.Append(word2[i]); + } + } + + return res.ToString(); + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/remove-element.md b/articles/remove-element.md index 1c3d722e4..14d2c78b4 100644 --- a/articles/remove-element.md +++ b/articles/remove-element.md @@ -72,6 +72,25 @@ class Solution { } ``` +```csharp +public class Solution { + public int RemoveElement(int[] nums, int val) { + List tmp = new List(); + foreach (int num in nums) { + if (num != val) { + tmp.Add(num); + } + } + + for (int i = 0; i < tmp.Count; i++) { + nums[i] = tmp[i]; + } + + return tmp.Count; + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -144,6 +163,20 @@ class Solution { } ``` +```csharp +public class Solution { + public int RemoveElement(int[] nums, int val) { + int k = 0; + for (int i = 0; i < nums.Length; i++) { + if (nums[i] != val) { + nums[k++] = nums[i]; + } + } + return k; + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -225,6 +258,22 @@ class Solution { } ``` +```csharp +public class Solution { + public int RemoveElement(int[] nums, int val) { + int i = 0, n = nums.Length; + while (i < n) { + if (nums[i] == val) { + nums[i] = nums[--n]; + } else { + i++; + } + } + return n; + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/rotate-array.md b/articles/rotate-array.md index 72dfcb037..790679e20 100644 --- a/articles/rotate-array.md +++ b/articles/rotate-array.md @@ -75,6 +75,24 @@ class Solution { } ``` +```csharp +public class Solution { + public void Rotate(int[] nums, int k) { + int n = nums.Length; + k %= n; + + while (k > 0) { + int tmp = nums[n - 1]; + for (int i = n - 1; i > 0; i--) { + nums[i] = nums[i - 1]; + } + nums[0] = tmp; + k--; + } + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -153,6 +171,23 @@ class Solution { } ``` +```csharp +public class Solution { + public void Rotate(int[] nums, int k) { + int n = nums.Length; + int[] tmp = new int[n]; + + for (int i = 0; i < n; i++) { + tmp[(i + k) % n] = nums[i]; + } + + for (int i = 0; i < n; i++) { + nums[i] = tmp[i]; + } + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -265,6 +300,30 @@ class Solution { } ``` +```csharp +public class Solution { + public void Rotate(int[] nums, int k) { + int n = nums.Length; + k %= n; + int count = 0; + + for (int start = 0; count < n; start++) { + int current = start; + int prev = nums[start]; + + do { + int nextIdx = (current + k) % n; + int temp = nums[nextIdx]; + nums[nextIdx] = prev; + prev = temp; + current = nextIdx; + count++; + } while (start != current); + } + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -369,6 +428,29 @@ class Solution { } ``` +```csharp +public class Solution { + public void Rotate(int[] nums, int k) { + int n = nums.Length; + k %= n; + + Reverse(nums, 0, n - 1); + Reverse(nums, 0, k - 1); + Reverse(nums, k, n - 1); + } + + private void Reverse(int[] nums, int left, int right) { + while (left < right) { + int temp = nums[left]; + nums[left] = nums[right]; + nums[right] = temp; + left++; + right--; + } + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -425,6 +507,20 @@ class Solution { } ``` +```csharp +public class Solution { + public void Rotate(int[] nums, int k) { + int n = nums.Length; + k %= n; + + int[] rotated = new int[n]; + Array.Copy(nums, n - k, rotated, 0, k); + Array.Copy(nums, 0, rotated, k, n - k); + Array.Copy(rotated, nums, n); + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/sort-an-array.md b/articles/sort-an-array.md index ba9edb507..265db6b33 100644 --- a/articles/sort-an-array.md +++ b/articles/sort-an-array.md @@ -205,6 +205,58 @@ class Solution { } ``` +```csharp +public class Solution { + public int[] SortArray(int[] nums) { + QuickSort(nums, 0, nums.Length - 1); + return nums; + } + + private void QuickSort(int[] nums, int left, int right) { + if (right <= left + 1) { + if (right == left + 1 && nums[right] < nums[left]) { + Swap(nums, left, right); + } + return; + } + + int j = Partition(nums, left, right); + QuickSort(nums, left, j - 1); + QuickSort(nums, j + 1, right); + } + + private int Partition(int[] nums, int left, int right) { + int mid = (left + right) >> 1; + Swap(nums, mid, left + 1); + + if (nums[left] > nums[right]) Swap(nums, left, right); + if (nums[left + 1] > nums[right]) Swap(nums, left + 1, right); + if (nums[left] > nums[left + 1]) Swap(nums, left, left + 1); + + int pivot = nums[left + 1]; + int i = left + 1, j = right; + + while (true) { + while (++i <= right && nums[i] < pivot) ; + while (--j >= left && nums[j] > pivot) ; + + if (i > j) break; + + Swap(nums, i, j); + } + + Swap(nums, left + 1, j); + return j; + } + + private void Swap(int[] nums, int i, int j) { + int temp = nums[i]; + nums[i] = nums[j]; + nums[j] = temp; + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -397,6 +449,47 @@ class Solution { } ``` +```csharp +public class Solution { + public int[] SortArray(int[] nums) { + MergeSort(nums, 0, nums.Length - 1); + return nums; + } + + private void MergeSort(int[] arr, int l, int r) { + if (l == r) return; + + int m = (l + r) / 2; + MergeSort(arr, l, m); + MergeSort(arr, m + 1, r); + Merge(arr, l, m, r); + } + + private void Merge(int[] arr, int L, int M, int R) { + int[] left = arr[L..(M + 1)]; + int[] right = arr[(M + 1)..(R + 1)]; + + int i = L, j = 0, k = 0; + + while (j < left.Length && k < right.Length) { + if (left[j] <= right[k]) { + arr[i++] = left[j++]; + } else { + arr[i++] = right[k++]; + } + } + + while (j < left.Length) { + arr[i++] = left[j++]; + } + + while (k < right.Length) { + arr[i++] = right[k++]; + } + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -580,6 +673,53 @@ class Solution { } ``` +```csharp +public class Solution { + public int[] SortArray(int[] nums) { + HeapSort(nums); + return nums; + } + + private void Heapify(int[] arr, int n, int i) { + int l = (i << 1) + 1; + int r = (i << 1) + 2; + int largestNode = i; + + if (l < n && arr[l] > arr[largestNode]) { + largestNode = l; + } + + if (r < n && arr[r] > arr[largestNode]) { + largestNode = r; + } + + if (largestNode != i) { + Swap(arr, i, largestNode); + Heapify(arr, n, largestNode); + } + } + + private void HeapSort(int[] arr) { + int n = arr.Length; + + for (int i = n / 2 - 1; i >= 0; i--) { + Heapify(arr, n, i); + } + + for (int i = n - 1; i > 0; i--) { + Swap(arr, 0, i); + Heapify(arr, i, 0); + } + } + + private void Swap(int[] arr, int i, int j) { + int temp = arr[i]; + arr[i] = arr[j]; + arr[j] = temp; + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -711,6 +851,37 @@ class Solution { } ``` +```csharp +public class Solution { + public int[] SortArray(int[] nums) { + CountingSort(nums); + return nums; + } + + private void CountingSort(int[] nums) { + Dictionary count = new Dictionary(); + int minVal = int.MaxValue, maxVal = int.MinValue; + + foreach (int val in nums) { + if (!count.ContainsKey(val)) { + count[val] = 0; + } + count[val]++; + minVal = Math.Min(minVal, val); + maxVal = Math.Max(maxVal, val); + } + + int index = 0; + for (int val = minVal; val <= maxVal; val++) { + if (!count.ContainsKey(val)) continue; + while (count[val]-- > 0) { + nums[index++] = val; + } + } + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -974,6 +1145,74 @@ class Solution { } ``` +```csharp +public class Solution { + public int[] SortArray(int[] nums) { + List negatives = new List(); + List positives = new List(); + + foreach (int num in nums) { + if (num < 0) negatives.Add(-num); + else positives.Add(num); + } + + if (negatives.Count > 0) { + RadixSort(negatives); + negatives.Reverse(); + for (int i = 0; i < negatives.Count; i++) { + negatives[i] = -negatives[i]; + } + } + + if (positives.Count > 0) { + RadixSort(positives); + } + + List result = new List(); + result.AddRange(negatives); + result.AddRange(positives); + + return result.ToArray(); + } + + private void RadixSort(List arr) { + int n = arr.Count; + int maxElement = 0; + foreach (int num in arr) { + if (num > maxElement) maxElement = num; + } + + int d = 1; + while (maxElement / d > 0) { + CountSort(arr, n, d); + d *= 10; + } + } + + private void CountSort(List arr, int n, int d) { + int[] count = new int[10]; + for (int i = 0; i < n; i++) { + int digit = (arr[i] / d) % 10; + count[digit]++; + } + + for (int i = 1; i < 10; i++) { + count[i] += count[i - 1]; + } + + int[] res = new int[n]; + for (int i = n - 1; i >= 0; i--) { + int digit = (arr[i] / d) % 10; + res[--count[digit]] = arr[i]; + } + + for (int i = 0; i < n; i++) { + arr[i] = res[i]; + } + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -1097,6 +1336,34 @@ class Solution { } ``` +```csharp +public class Solution { + public int[] SortArray(int[] nums) { + int n = nums.Length; + if (n == 1) return nums; + + ShellSort(nums, n); + return nums; + } + + private void ShellSort(int[] nums, int n) { + int gap = n / 2; + while (gap >= 1) { + for (int i = gap; i < n; i++) { + int tmp = nums[i]; + int j = i - gap; + while (j >= 0 && nums[j] > tmp) { + nums[j + gap] = nums[j]; + j -= gap; + } + nums[j + gap] = tmp; + } + gap /= 2; + } + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/sort-colors.md b/articles/sort-colors.md index c94416b39..46bc4a86a 100644 --- a/articles/sort-colors.md +++ b/articles/sort-colors.md @@ -40,6 +40,14 @@ class Solution { } ``` +```csharp +public class Solution { + public void SortColors(int[] nums) { + Array.Sort(nums); + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -130,6 +138,24 @@ class Solution { } ``` +```csharp +public class Solution { + public void SortColors(int[] nums) { + int[] count = new int[3]; + foreach (int num in nums) { + count[num]++; + } + + int index = 0; + for (int i = 0; i < 3; i++) { + while (count[i]-- > 0) { + nums[index++] = i; + } + } + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -236,6 +262,32 @@ class Solution { } ``` +```csharp +public class Solution { + public void SortColors(int[] nums) { + int i = 0, l = 0, r = nums.Length - 1; + + while (i <= r) { + if (nums[i] == 0) { + Swap(nums, l, i); + l++; + } else if (nums[i] == 2) { + Swap(nums, i, r); + r--; + i--; + } + i++; + } + } + + private void Swap(int[] nums, int i, int j) { + int temp = nums[i]; + nums[i] = nums[j]; + nums[j] = temp; + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -339,6 +391,27 @@ class Solution { } ``` +```csharp +public class Solution { + public void SortColors(int[] nums) { + int zero = 0, one = 0, two = 0; + + for (int i = 0; i < nums.Length; i++) { + if (nums[i] == 0) { + nums[two++] = 2; + nums[one++] = 1; + nums[zero++] = 0; + } else if (nums[i] == 1) { + nums[two++] = 2; + nums[one++] = 1; + } else { + nums[two++] = 2; + } + } + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -429,6 +502,26 @@ class Solution { } ``` +```csharp +public class Solution { + public void SortColors(int[] nums) { + int zero = 0, one = 0; + + for (int two = 0; two < nums.Length; two++) { + int tmp = nums[two]; + nums[two] = 2; + + if (tmp < 2) { + nums[one++] = 1; + } + if (tmp < 1) { + nums[zero++] = 0; + } + } + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/subarray-sum-equals-k.md b/articles/subarray-sum-equals-k.md index 5afa95bd3..fa979f529 100644 --- a/articles/subarray-sum-equals-k.md +++ b/articles/subarray-sum-equals-k.md @@ -69,6 +69,24 @@ class Solution { } ``` +```csharp +public class Solution { + public int SubarraySum(int[] nums, int k) { + int res = 0; + for (int i = 0; i < nums.Length; i++) { + int sum = 0; + for (int j = i; j < nums.Length; j++) { + sum += nums[j]; + if (sum == k) { + res++; + } + } + } + return res; + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -161,6 +179,32 @@ class Solution { } ``` +```csharp +public class Solution { + public int SubarraySum(int[] nums, int k) { + int res = 0, curSum = 0; + Dictionary prefixSums = new Dictionary(); + prefixSums[0] = 1; + + foreach (int num in nums) { + curSum += num; + int diff = curSum - k; + + if (prefixSums.ContainsKey(diff)) { + res += prefixSums[diff]; + } + + if (!prefixSums.ContainsKey(curSum)) { + prefixSums[curSum] = 0; + } + prefixSums[curSum]++; + } + + return res; + } +} +``` + ::tabs-end ### Time & Space Complexity From c57cf461bbc4cd27e050a2cada488be4b567202d Mon Sep 17 00:00:00 2001 From: neetcode-gh <77742485+neetcode-gh@users.noreply.github.com> Date: Fri, 2 May 2025 06:41:51 -0700 Subject: [PATCH 43/45] Update combinations-of-a-phone-number.md --- articles/combinations-of-a-phone-number.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/articles/combinations-of-a-phone-number.md b/articles/combinations-of-a-phone-number.md index 6f6bfd6dd..51796e3f7 100644 --- a/articles/combinations-of-a-phone-number.md +++ b/articles/combinations-of-a-phone-number.md @@ -1,4 +1,4 @@ -## 1. Bactracking +## 1. Backtracking ::tabs-start @@ -497,4 +497,4 @@ class Solution { * Time complexity: $O(n * 4 ^ n)$ * Space complexity: * $O(n)$ extra space. - * $O(n * 4 ^ n)$ space for the output list. \ No newline at end of file + * $O(n * 4 ^ n)$ space for the output list. From d2bcd42a22a671dffab507ad76c38baa3838285b Mon Sep 17 00:00:00 2001 From: neetcode-gh <77742485+neetcode-gh@users.noreply.github.com> Date: Sun, 11 May 2025 08:41:57 -0700 Subject: [PATCH 44/45] Update sort-an-array.md --- articles/sort-an-array.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/articles/sort-an-array.md b/articles/sort-an-array.md index 265db6b33..6b34f49d2 100644 --- a/articles/sort-an-array.md +++ b/articles/sort-an-array.md @@ -261,7 +261,7 @@ public class Solution { ### Time & Space Complexity -* Time complexity: $O(n)$ in average case, $O(n ^ 2)$ in worst case. +* Time complexity: $O(n \log n)$ in average case, $O(n ^ 2)$ in worst case. * Space complexity: $O(\log n)$ for recursive stack. --- @@ -1369,4 +1369,4 @@ public class Solution { ### Time & Space Complexity * Time complexity: $O(n \log n)$ in average case, $O(n ^ 2)$ in worst case. -* Space complexity: $O(1)$ \ No newline at end of file +* Space complexity: $O(1)$ From 3d2b2c0429a514c1565add058c23bf8187370276 Mon Sep 17 00:00:00 2001 From: Sri Hari <94112314+Srihari2222@users.noreply.github.com> Date: Tue, 20 May 2025 22:00:48 +0530 Subject: [PATCH 45/45] Sri Hari: Batch-7/Added articles (#4148) * Batch-7/Neetcode-ALL/Added-articles * Batch-7/Neetcode-ALL/Added-articles * Batch-7/Neetcode-ALL/Added-articles * Batch-7/Neetcode-ALL/Added-articles * Batch-7/Neetcode-ALL/Added-articles * Batch-7/Neetcode-ALL/Added-articles * Batch-7/Neetcode-ALL/Added-articles * Batch-7/Neetcode-ALL/Added-articles * Batch-7/Neetcode-ALL/Added-articles * Batch-7/Neetcode-ALL/Added-articles * Batch-7/Neetcode-ALL/Added-articles * Batch-7/Neetcode-ALL/Added-articles * Batch-7/Neetcode-ALL/Added-articles --- articles/4sum.md | 163 +++++++++ articles/accounts-merge.md | 233 ++++++++++++ articles/asteroid-collision.md | 63 ++++ articles/baseball-game.md | 64 +++- articles/binary-tree-inorder-traversal.md | 109 ++++++ articles/binary-tree-postorder-traversal.md | 160 ++++++++ articles/binary-tree-preorder-traversal.md | 111 ++++++ ...capacity-to-ship-packages-within-d-days.md | 61 ++++ articles/car-pooling.md | 122 ++++++- articles/combination-sum-iv.md | 99 +++++ articles/combinations.md | 98 +++++ articles/construct-quad-tree.md | 212 +++++++++++ articles/contains-duplicate-ii.md | 54 +++ articles/course-schedule-iv.md | 195 ++++++++++ articles/decode-string.md | 108 ++++++ articles/delete-leaves-with-a-given-value.md | 142 ++++++++ articles/delete-node-in-a-bst.md | 158 ++++++++ articles/evaluate-division.md | 201 ++++++++++ articles/extra-characters-in-a-string.md | 238 ++++++++++++ articles/find-in-mountain-array.md | 194 +++++++++- articles/find-the-town-judge.md | 46 +++ articles/greatest-common-divisor-traversal.md | 344 +++++++++++++++++- articles/guess-number-higher-or-lower.md | 81 ++++- articles/house-robber-iii.md | 106 ++++++ articles/insert-into-a-binary-search-tree.md | 71 ++++ articles/integer-break.md | 145 ++++++++ articles/ipo.md | 111 +++++- articles/island-perimeter.md | 135 +++++++ articles/longest-happy-string.md | 115 +++++- articles/matchsticks-to-square.md | 112 ++++++ articles/minimum-size-subarray-sum.md | 76 ++++ articles/n-queens-ii.md | 162 +++++++++ articles/n-th-tribonacci-number.md | 59 +++ articles/open-the-lock.md | 119 ++++++ articles/partition-to-k-equal-sum-subsets.md | 173 +++++++++ articles/path-with-minimum-effort.md | 219 ++++++++++- articles/perfect-squares.md | 126 +++++++ articles/permutations-ii.md | 180 +++++++++ .../remove-duplicates-from-sorted-array.md | 45 +++ articles/reverse-linked-list-ii.md | 177 +++++++++ articles/reverse-string.md | 73 ++++ articles/search-in-rotated-sorted-array-ii.md | 39 ++ articles/search-insert-position.md | 87 +++++ articles/simplify-path.md | 49 +++ articles/single-threaded-cpu.md | 138 ++++++- articles/split-array-largest-sum.md | 228 +++++++++++- articles/sqrtx.md | 76 ++++ articles/stone-game-iii.md | 106 ++++++ articles/sum-of-all-subset-xor-totals.md | 73 ++++ articles/verifying-an-alien-dictionary.md | 60 +++ articles/word-break-ii.md | 213 +++++++++++ 51 files changed, 6476 insertions(+), 53 deletions(-) diff --git a/articles/4sum.md b/articles/4sum.md index f99c11ddd..b590cf39e 100644 --- a/articles/4sum.md +++ b/articles/4sum.md @@ -96,6 +96,35 @@ class Solution { } ``` +```csharp +public class Solution { + public List> FourSum(int[] nums, int target) { + int n = nums.Length; + Array.Sort(nums); + HashSet<(int, int, int, int)> res = new HashSet<(int, int, int, int)>(); + + for (int a = 0; a < n; a++) { + for (int b = a + 1; b < n; b++) { + for (int c = b + 1; c < n; c++) { + for (int d = c + 1; d < n; d++) { + long sum = (long)nums[a] + nums[b] + nums[c] + nums[d]; + if (sum == target) { + res.Add((nums[a], nums[b], nums[c], nums[d])); + } + } + } + } + } + + var result = new List>(); + foreach (var quad in res) { + result.Add(new List { quad.Item1, quad.Item2, quad.Item3, quad.Item4 }); + } + return result; + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -287,6 +316,58 @@ class Solution { } ``` +```csharp +public class Solution { + public List> FourSum(int[] nums, int target) { + Array.Sort(nums); + Dictionary count = new Dictionary(); + + foreach (int num in nums) { + if (!count.ContainsKey(num)) { + count[num] = 0; + } + count[num]++; + } + + List> res = new List>(); + + for (int i = 0; i < nums.Length; i++) { + count[nums[i]]--; + if (i > 0 && nums[i] == nums[i - 1]) continue; + + for (int j = i + 1; j < nums.Length; j++) { + count[nums[j]]--; + if (j > i + 1 && nums[j] == nums[j - 1]) continue; + + for (int k = j + 1; k < nums.Length; k++) { + count[nums[k]]--; + if (k > j + 1 && nums[k] == nums[k - 1]) continue; + + long fourth = (long)target - (long)nums[i] - (long)nums[j] - (long)nums[k]; + if (fourth > int.MaxValue || fourth < int.MinValue) { + continue; + } + + if (count.ContainsKey((int)fourth) && count[(int)fourth] > 0) { + res.Add(new List { nums[i], nums[j], nums[k], (int)fourth }); + } + } + + for (int k = j + 1; k < nums.Length; k++) { + count[nums[k]]++; + } + } + + for (int j = i + 1; j < nums.Length; j++) { + count[nums[j]]++; + } + } + + return res; + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -448,6 +529,42 @@ class Solution { } ``` +```csharp +public class Solution { + public List> FourSum(int[] nums, int target) { + Array.Sort(nums); + List> res = new List>(); + int n = nums.Length; + + for (int i = 0; i < n; i++) { + if (i > 0 && nums[i] == nums[i - 1]) continue; + + for (int j = i + 1; j < n; j++) { + if (j > i + 1 && nums[j] == nums[j - 1]) continue; + + int left = j + 1, right = n - 1; + while (left < right) { + long sum = (long)nums[i] + nums[j] + nums[left] + nums[right]; + if (sum == target) { + res.Add(new List { nums[i], nums[j], nums[left], nums[right] }); + left++; + right--; + while (left < right && nums[left] == nums[left - 1]) left++; + while (left < right && nums[right] == nums[right + 1]) right--; + } else if (sum < target) { + left++; + } else { + right--; + } + } + } + } + + return res; + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -637,6 +754,52 @@ class Solution { } ``` +```csharp +public class Solution { + private List> res; + private List quad; + + public List> FourSum(int[] nums, int target) { + Array.Sort(nums); + res = new List>(); + quad = new List(); + KSum(nums, 4, 0, target); + return res; + } + + private void KSum(int[] nums, int k, int start, long target) { + if (k == 2) { + int l = start, r = nums.Length - 1; + while (l < r) { + long sum = (long)nums[l] + nums[r]; + if (sum < target) { + l++; + } else if (sum > target) { + r--; + } else { + List newQuad = new List(quad); + newQuad.Add(nums[l]); + newQuad.Add(nums[r]); + res.Add(newQuad); + l++; + r--; + while (l < r && nums[l] == nums[l - 1]) l++; + while (l < r && nums[r] == nums[r + 1]) r--; + } + } + return; + } + + for (int i = start; i < nums.Length - k + 1; i++) { + if (i > start && nums[i] == nums[i - 1]) continue; + quad.Add(nums[i]); + KSum(nums, k - 1, i + 1, target - nums[i]); + quad.RemoveAt(quad.Count - 1); + } + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/accounts-merge.md b/articles/accounts-merge.md index 665334798..15b8e9881 100644 --- a/articles/accounts-merge.md +++ b/articles/accounts-merge.md @@ -284,6 +284,79 @@ class Solution { } ``` +```csharp +public class Solution { + private Dictionary emailIdx = new Dictionary(); + private List emails = new List(); + private List> adj; + private bool[] visited; + private Dictionary> components = new Dictionary>(); + private Dictionary componentName = new Dictionary(); + + public List> AccountsMerge(List> accounts) { + int m = 0; + + for (int accId = 0; accId < accounts.Count; accId++) { + var account = accounts[accId]; + for (int i = 1; i < account.Count; i++) { + string email = account[i]; + if (!emailIdx.ContainsKey(email)) { + emailIdx[email] = m++; + emails.Add(email); + } + } + } + + adj = new List>(); + for (int i = 0; i < m; i++) adj.Add(new List()); + + foreach (var account in accounts) { + for (int i = 2; i < account.Count; i++) { + int u = emailIdx[account[i - 1]]; + int v = emailIdx[account[i]]; + adj[u].Add(v); + adj[v].Add(u); + } + } + + visited = new bool[m]; + + foreach (var account in accounts) { + string name = account[0]; + foreach (var email in account.Skip(1)) { + int idx = emailIdx[email]; + if (!visited[idx]) { + components[idx] = new List(); + componentName[idx] = name; + Dfs(idx, idx); + } + } + } + + var res = new List>(); + foreach (var kvp in components) { + var group = kvp.Value; + group.Sort(StringComparer.Ordinal); + var merged = new List { componentName[kvp.Key] }; + merged.AddRange(group); + res.Add(merged); + } + + return res; + } + + private void Dfs(int node, int root) { + visited[node] = true; + components[root].Add(emails[node]); + foreach (int nei in adj[node]) { + if (!visited[nei]) { + Dfs(nei, root); + } + } + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -607,6 +680,85 @@ class Solution { } ``` +```csharp +public class Solution { + public List> AccountsMerge(List> accounts) { + int n = accounts.Count; + Dictionary emailIdx = new Dictionary(); + List emails = new List(); + Dictionary emailToAcc = new Dictionary(); + + int m = 0; + for (int accId = 0; accId < n; accId++) { + var account = accounts[accId]; + for (int i = 1; i < account.Count; i++) { + string email = account[i]; + if (!emailIdx.ContainsKey(email)) { + emailIdx[email] = m; + emails.Add(email); + emailToAcc[m] = accId; + m++; + } + } + } + + List> adj = new List>(); + for (int i = 0; i < m; i++) adj.Add(new List()); + + foreach (var account in accounts) { + for (int i = 2; i < account.Count; i++) { + int id1 = emailIdx[account[i]]; + int id2 = emailIdx[account[i - 1]]; + adj[id1].Add(id2); + adj[id2].Add(id1); + } + } + + Dictionary> emailGroup = new Dictionary>(); + bool[] visited = new bool[m]; + + void Bfs(int start, int accId) { + Queue queue = new Queue(); + queue.Enqueue(start); + visited[start] = true; + + if (!emailGroup.ContainsKey(accId)) + emailGroup[accId] = new List(); + + while (queue.Count > 0) { + int node = queue.Dequeue(); + emailGroup[accId].Add(emails[node]); + + foreach (int nei in adj[node]) { + if (!visited[nei]) { + visited[nei] = true; + queue.Enqueue(nei); + } + } + } + } + + for (int i = 0; i < m; i++) { + if (!visited[i]) { + Bfs(i, emailToAcc[i]); + } + } + + List> res = new List>(); + foreach (var kvp in emailGroup) { + int accId = kvp.Key; + string name = accounts[accId][0]; + List merged = new List { name }; + kvp.Value.Sort(StringComparer.Ordinal); + merged.AddRange(kvp.Value); + res.Add(merged); + } + + return res; + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -922,6 +1074,87 @@ class Solution { } ``` +```csharp +public class UnionFind { + private int[] parent; + private int[] rank; + + public UnionFind(int n) { + parent = new int[n]; + rank = new int[n]; + for (int i = 0; i < n; i++) { + parent[i] = i; + rank[i] = 1; + } + } + + public int Find(int x) { + if (x != parent[x]) { + parent[x] = Find(parent[x]); + } + return parent[x]; + } + + public bool Union(int x, int y) { + int rootX = Find(x); + int rootY = Find(y); + + if (rootX == rootY) return false; + + if (rank[rootX] > rank[rootY]) { + parent[rootY] = rootX; + rank[rootX] += rank[rootY]; + } else { + parent[rootX] = rootY; + rank[rootY] += rank[rootX]; + } + + return true; + } +} + +public class Solution { + public List> AccountsMerge(List> accounts) { + int n = accounts.Count; + UnionFind uf = new UnionFind(n); + Dictionary emailToAcc = new Dictionary(); + + for (int i = 0; i < n; i++) { + for (int j = 1; j < accounts[i].Count; j++) { + string email = accounts[i][j]; + if (emailToAcc.ContainsKey(email)) { + uf.Union(i, emailToAcc[email]); + } else { + emailToAcc[email] = i; + } + } + } + + Dictionary> emailGroup = new Dictionary>(); + foreach (var kvp in emailToAcc) { + string email = kvp.Key; + int leader = uf.Find(kvp.Value); + if (!emailGroup.ContainsKey(leader)) { + emailGroup[leader] = new List(); + } + emailGroup[leader].Add(email); + } + + List> res = new List>(); + foreach (var kvp in emailGroup) { + int accId = kvp.Key; + List emails = kvp.Value; + emails.Sort(StringComparer.Ordinal); + List merged = new List { accounts[accId][0] }; + merged.AddRange(emails); + res.Add(merged); + } + + return res; + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/asteroid-collision.md b/articles/asteroid-collision.md index e9634e9e8..94a5993d3 100644 --- a/articles/asteroid-collision.md +++ b/articles/asteroid-collision.md @@ -101,6 +101,35 @@ class Solution { } ``` +```csharp +public class Solution { + public int[] AsteroidCollision(int[] asteroids) { + Stack stack = new Stack(); + + foreach (int a in asteroids) { + int current = a; + while (stack.Count > 0 && current < 0 && stack.Peek() > 0) { + int diff = current + stack.Peek(); + if (diff < 0) { + stack.Pop(); + } else if (diff > 0) { + current = 0; + } else { + current = 0; + stack.Pop(); + } + } + if (current != 0) { + stack.Push(current); + } + } + + int[] result = stack.Reverse().ToArray(); + return result; + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -231,6 +260,40 @@ class Solution { } ``` +```csharp +public class Solution { + public int[] AsteroidCollision(int[] asteroids) { + int n = asteroids.Length; + int j = -1; + + foreach (int a in asteroids) { + int current = a; + + while (j >= 0 && asteroids[j] > 0 && current < 0) { + if (asteroids[j] > Math.Abs(current)) { + current = 0; + break; + } else if (asteroids[j] == Math.Abs(current)) { + j--; + current = 0; + break; + } else { + j--; + } + } + + if (current != 0) { + asteroids[++j] = current; + } + } + + int[] result = new int[j + 1]; + Array.Copy(asteroids, 0, result, 0, j + 1); + return result; + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/baseball-game.md b/articles/baseball-game.md index 2d04f4311..ac8e53feb 100644 --- a/articles/baseball-game.md +++ b/articles/baseball-game.md @@ -1,4 +1,4 @@ -## 1. Stack +## 1. Stack - I ::tabs-start @@ -96,6 +96,36 @@ class Solution { } ``` +```csharp +public class Solution { + public int CalPoints(string[] operations) { + Stack stack = new Stack(); + + foreach (var op in operations) { + if (op == "+") { + int top = stack.Pop(); + int newTop = top + stack.Peek(); + stack.Push(top); + stack.Push(newTop); + } else if (op == "D") { + stack.Push(2 * stack.Peek()); + } else if (op == "C") { + stack.Pop(); + } else { + stack.Push(int.Parse(op)); + } + } + + int total = 0; + foreach (var val in stack) { + total += val; + } + + return total; + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -215,6 +245,38 @@ class Solution { } ``` +```csharp +public class Solution { + public int CalPoints(string[] operations) { + Stack stack = new Stack(); + int res = 0; + + foreach (var op in operations) { + if (op == "+") { + int top = stack.Pop(); + int second = stack.Peek(); + int sum = top + second; + stack.Push(top); + stack.Push(sum); + res += sum; + } else if (op == "D") { + int doubleVal = 2 * stack.Peek(); + stack.Push(doubleVal); + res += doubleVal; + } else if (op == "C") { + res -= stack.Pop(); + } else { + int num = int.Parse(op); + stack.Push(num); + res += num; + } + } + + return res; + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/binary-tree-inorder-traversal.md b/articles/binary-tree-inorder-traversal.md index 4735f8400..84e525d71 100644 --- a/articles/binary-tree-inorder-traversal.md +++ b/articles/binary-tree-inorder-traversal.md @@ -126,6 +126,35 @@ class Solution { } ``` +```csharp +/** + * Definition for a binary tree node. + * public class TreeNode { + * public int val; + * public TreeNode left; + * public TreeNode right; + * public TreeNode(int val=0, TreeNode left=null, TreeNode right=null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public List InorderTraversal(TreeNode root) { + List res = new List(); + void Inorder(TreeNode node) { + if (node == null) return; + Inorder(node.left); + res.Add(node.val); + Inorder(node.right); + } + Inorder(root); + return res; + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -273,6 +302,41 @@ class Solution { } ``` +```csharp +/** + * Definition for a binary tree node. + * public class TreeNode { + * public int val; + * public TreeNode left; + * public TreeNode right; + * public TreeNode(int val=0, TreeNode left=null, TreeNode right=null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public IList InorderTraversal(TreeNode root) { + List res = new List(); + Stack stack = new Stack(); + TreeNode cur = root; + + while (cur != null || stack.Count > 0) { + while (cur != null) { + stack.Push(cur); + cur = cur.left; + } + cur = stack.Pop(); + res.Add(cur.val); + cur = cur.right; + } + + return res; + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -457,6 +521,51 @@ class Solution { } ``` +```csharp +/** + * Definition for a binary tree node. + * public class TreeNode { + * public int val; + * public TreeNode left; + * public TreeNode right; + * public TreeNode(int val=0, TreeNode left=null, TreeNode right=null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public List InorderTraversal(TreeNode root) { + List res = new List(); + TreeNode cur = root; + + while (cur != null) { + if (cur.left == null) { + res.Add(cur.val); + cur = cur.right; + } else { + TreeNode prev = cur.left; + while (prev.right != null && prev.right != cur) { + prev = prev.right; + } + + if (prev.right == null) { + prev.right = cur; + cur = cur.left; + } else { + prev.right = null; + res.Add(cur.val); + cur = cur.right; + } + } + } + + return res; + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/binary-tree-postorder-traversal.md b/articles/binary-tree-postorder-traversal.md index 554c3006a..8853be764 100644 --- a/articles/binary-tree-postorder-traversal.md +++ b/articles/binary-tree-postorder-traversal.md @@ -126,6 +126,37 @@ class Solution { } ``` +```csharp +/** + * Definition for a binary tree node. + * public class TreeNode { + * public int val; + * public TreeNode left; + * public TreeNode right; + * public TreeNode(int val=0, TreeNode left=null, TreeNode right=null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public List PostorderTraversal(TreeNode root) { + List res = new List(); + + void Postorder(TreeNode node) { + if (node == null) return; + Postorder(node.left); + Postorder(node.right); + res.Add(node.val); + } + + Postorder(root); + return res; + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -309,6 +340,52 @@ class Solution { } ``` +```csharp +/** + * Definition for a binary tree node. + * public class TreeNode { + * public int val; + * public TreeNode left; + * public TreeNode right; + * public TreeNode(int val=0, TreeNode left=null, TreeNode right=null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public List PostorderTraversal(TreeNode root) { + var stack = new Stack(); + var visit = new Stack(); + var res = new List(); + + stack.Push(root); + visit.Push(false); + + while (stack.Count > 0) { + var cur = stack.Pop(); + var v = visit.Pop(); + + if (cur != null) { + if (v) { + res.Add(cur.val); + } else { + stack.Push(cur); + visit.Push(true); + stack.Push(cur.right); + visit.Push(false); + stack.Push(cur.left); + visit.Push(false); + } + } + } + + return res; + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -464,6 +541,43 @@ class Solution { } ``` +```csharp +/** + * Definition for a binary tree node. + * public class TreeNode { + * public int val; + * public TreeNode left; + * public TreeNode right; + * public TreeNode(int val=0, TreeNode left=null, TreeNode right=null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public List PostorderTraversal(TreeNode root) { + List res = new List(); + Stack stack = new Stack(); + TreeNode cur = root; + + while (cur != null || stack.Count > 0) { + if (cur != null) { + res.Add(cur.val); + stack.Push(cur); + cur = cur.right; + } else { + cur = stack.Pop(); + cur = cur.left; + } + } + + res.Reverse(); + return res; + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -652,6 +766,52 @@ class Solution { } ``` +```csharp +/** + * Definition for a binary tree node. + * public class TreeNode { + * public int val; + * public TreeNode left; + * public TreeNode right; + * public TreeNode(int val=0, TreeNode left=null, TreeNode right=null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public List PostorderTraversal(TreeNode root) { + List res = new List(); + TreeNode cur = root; + + while (cur != null) { + if (cur.right == null) { + res.Add(cur.val); + cur = cur.left; + } else { + TreeNode prev = cur.right; + while (prev.left != null && prev.left != cur) { + prev = prev.left; + } + + if (prev.left == null) { + res.Add(cur.val); + prev.left = cur; + cur = cur.right; + } else { + prev.left = null; + cur = cur.left; + } + } + } + + res.Reverse(); + return res; + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/binary-tree-preorder-traversal.md b/articles/binary-tree-preorder-traversal.md index fbcc8b4d6..f7f86ffb9 100644 --- a/articles/binary-tree-preorder-traversal.md +++ b/articles/binary-tree-preorder-traversal.md @@ -126,6 +126,37 @@ class Solution { } ``` +```csharp +/** + * Definition for a binary tree node. + * public class TreeNode { + * public int val; + * public TreeNode left; + * public TreeNode right; + * public TreeNode(int val=0, TreeNode left=null, TreeNode right=null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public List PreorderTraversal(TreeNode root) { + List res = new List(); + Preorder(root, res); + return res; + } + + private void Preorder(TreeNode node, List res) { + if (node == null) return; + + res.Add(node.val); + Preorder(node.left, res); + Preorder(node.right, res); + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -274,6 +305,41 @@ class Solution { } ``` +```csharp +/** + * Definition for a binary tree node. + * public class TreeNode { + * public int val; + * public TreeNode left; + * public TreeNode right; + * public TreeNode(int val=0, TreeNode left=null, TreeNode right=null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public List PreorderTraversal(TreeNode root) { + List res = new List(); + Stack stack = new Stack(); + TreeNode cur = root; + + while (cur != null || stack.Count > 0) { + if (cur != null) { + res.Add(cur.val); + stack.Push(cur.right); + cur = cur.left; + } else { + cur = stack.Pop(); + } + } + + return res; + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -458,6 +524,51 @@ class Solution { } ``` +```csharp +/** + * Definition for a binary tree node. + * public class TreeNode { + * public int val; + * public TreeNode left; + * public TreeNode right; + * public TreeNode(int val=0, TreeNode left=null, TreeNode right=null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public List PreorderTraversal(TreeNode root) { + List res = new List(); + TreeNode cur = root; + + while (cur != null) { + if (cur.left == null) { + res.Add(cur.val); + cur = cur.right; + } else { + TreeNode prev = cur.left; + while (prev.right != null && prev.right != cur) { + prev = prev.right; + } + + if (prev.right == null) { + res.Add(cur.val); + prev.right = cur; + cur = cur.left; + } else { + prev.right = null; + cur = cur.right; + } + } + } + + return res; + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/capacity-to-ship-packages-within-d-days.md b/articles/capacity-to-ship-packages-within-d-days.md index 9a5d4e777..dfc4f2146 100644 --- a/articles/capacity-to-ship-packages-within-d-days.md +++ b/articles/capacity-to-ship-packages-within-d-days.md @@ -97,6 +97,29 @@ class Solution { } ``` +```csharp +public class Solution { + public int ShipWithinDays(int[] weights, int days) { + int res = weights.Max(); + while (true) { + int ships = 1; + int cap = res; + foreach (int w in weights) { + if (cap - w < 0) { + ships++; + cap = res; + } + cap -= w; + } + if (ships <= days) { + return res; + } + res++; + } + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -258,6 +281,44 @@ class Solution { } ``` +```csharp +public class Solution { + public int ShipWithinDays(int[] weights, int days) { + int l = weights.Max(); + int r = weights.Sum(); + int res = r; + + bool CanShip(int cap) { + int ships = 1; + int currCap = cap; + + foreach (int w in weights) { + if (currCap - w < 0) { + ships++; + if (ships > days) return false; + currCap = cap; + } + currCap -= w; + } + + return true; + } + + while (l <= r) { + int cap = (l + r) / 2; + if (CanShip(cap)) { + res = Math.Min(res, cap); + r = cap - 1; + } else { + l = cap + 1; + } + } + + return res; + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/car-pooling.md b/articles/car-pooling.md index 3eac0695d..df5745d6d 100644 --- a/articles/car-pooling.md +++ b/articles/car-pooling.md @@ -92,6 +92,28 @@ class Solution { } ``` +```csharp +public class Solution { + public bool CarPooling(int[][] trips, int capacity) { + Array.Sort(trips, (a, b) => a[1].CompareTo(b[1])); + + for (int i = 0; i < trips.Length; i++) { + int curPass = trips[i][0]; + for (int j = 0; j < i; j++) { + if (trips[j][2] > trips[i][1]) { + curPass += trips[j][0]; + } + } + if (curPass > capacity) { + return false; + } + } + + return true; + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -196,12 +218,12 @@ class Solution { carPooling(trips, capacity) { trips.sort((a, b) => a[1] - b[1]); - const minHeap = new MinPriorityQueue({ priority: x => x[0] }); // [end, numPassengers] + const minHeap = new MinPriorityQueue(x => x[0]); // [end, numPassengers] let curPass = 0; for (const [numPass, start, end] of trips) { - while (!minHeap.isEmpty() && minHeap.front().element[0] <= start) { - curPass -= minHeap.dequeue().element[1]; + while (!minHeap.isEmpty() && minHeap.front()[0] <= start) { + curPass -= minHeap.dequeue()[1]; } curPass += numPass; @@ -217,6 +239,36 @@ class Solution { } ``` +```csharp +public class Solution { + public bool CarPooling(int[][] trips, int capacity) { + Array.Sort(trips, (a, b) => a[1].CompareTo(b[1])); + + var minHeap = new PriorityQueue(); + int curPass = 0; + + foreach (var trip in trips) { + int numPass = trip[0]; + int start = trip[1]; + int end = trip[2]; + + while (minHeap.Count > 0 && minHeap.Peek()[0] <= start) { + curPass -= minHeap.Dequeue()[1]; + } + + curPass += numPass; + if (curPass > capacity) { + return false; + } + + minHeap.Enqueue(new int[] { end, numPass }, end); + } + + return true; + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -330,6 +382,36 @@ class Solution { } ``` +```csharp +public class Solution { + public bool CarPooling(int[][] trips, int capacity) { + List points = new List(); + + foreach (var trip in trips) { + int passengers = trip[0]; + int start = trip[1]; + int end = trip[2]; + + points.Add(new int[] { start, passengers }); + points.Add(new int[] { end, -passengers }); + } + + points.Sort((a, b) => { + if (a[0] == b[0]) return a[1] - b[1]; + return a[0] - b[0]; + }); + + int curPass = 0; + foreach (var point in points) { + curPass += point[1]; + if (curPass > capacity) return false; + } + + return true; + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -459,6 +541,40 @@ class Solution { } ``` +```csharp +public class Solution { + public bool CarPooling(int[][] trips, int capacity) { + int L = int.MaxValue, R = int.MinValue; + + foreach (var trip in trips) { + int start = trip[1], end = trip[2]; + L = Math.Min(L, start); + R = Math.Max(R, end); + } + + int N = R - L + 1; + int[] passChange = new int[N + 1]; + + foreach (var trip in trips) { + int passengers = trip[0]; + int start = trip[1]; + int end = trip[2]; + + passChange[start - L] += passengers; + passChange[end - L] -= passengers; + } + + int curPass = 0; + foreach (int change in passChange) { + curPass += change; + if (curPass > capacity) return false; + } + + return true; + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/combination-sum-iv.md b/articles/combination-sum-iv.md index 3a607251c..b2d2c5386 100644 --- a/articles/combination-sum-iv.md +++ b/articles/combination-sum-iv.md @@ -97,6 +97,31 @@ class Solution { } ``` +```csharp +public class Solution { + public int CombinationSum4(int[] nums, int target) { + Array.Sort(nums); + return Dfs(nums, target); + } + + private int Dfs(int[] nums, int total) { + if (total == 0) { + return 1; + } + + int res = 0; + foreach (int num in nums) { + if (total < num) { + break; + } + res += Dfs(nums, total - num); + } + + return res; + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -222,6 +247,36 @@ class Solution { } ``` +```csharp +public class Solution { + private Dictionary memo; + + public int CombinationSum4(int[] nums, int target) { + Array.Sort(nums); + memo = new Dictionary(); + memo[0] = 1; + return Dfs(nums, target); + } + + private int Dfs(int[] nums, int total) { + if (memo.ContainsKey(total)) { + return memo[total]; + } + + int res = 0; + foreach (int num in nums) { + if (total < num) { + break; + } + res += Dfs(nums, total - num); + } + + memo[total] = res; + return res; + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -310,6 +365,26 @@ class Solution { } ``` +```csharp +public class Solution { + public int CombinationSum4(int[] nums, int target) { + Dictionary dp = new Dictionary(); + dp[0] = 1; + + for (int total = 1; total <= target; total++) { + dp[total] = 0; + foreach (int num in nums) { + if (dp.ContainsKey(total - num)) { + dp[total] += dp[total - num]; + } + } + } + + return dp[target]; + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -403,6 +478,30 @@ class Solution { } ``` +```csharp +public class Solution { + public int CombinationSum4(int[] nums, int target) { + Array.Sort(nums); + Dictionary dp = new Dictionary(); + dp[target] = 1; + + for (int total = target; total > 0; total--) { + if (!dp.ContainsKey(total)) continue; + foreach (int num in nums) { + if (total < num) break; + int key = total - num; + if (!dp.ContainsKey(key)) { + dp[key] = 0; + } + dp[key] += dp[total]; + } + } + + return dp.ContainsKey(0) ? dp[0] : 0; + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/combinations.md b/articles/combinations.md index d5e838baf..321ffeab1 100644 --- a/articles/combinations.md +++ b/articles/combinations.md @@ -105,6 +105,31 @@ class Solution { } ``` +```csharp +public class Solution { + public List> Combine(int n, int k) { + List> res = new List>(); + + void Backtrack(int i, List comb) { + if (i > n) { + if (comb.Count == k) { + res.Add(new List(comb)); + } + return; + } + + comb.Add(i); + Backtrack(i + 1, comb); + comb.RemoveAt(comb.Count - 1); + Backtrack(i + 1, comb); + } + + Backtrack(1, new List()); + return res; + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -220,6 +245,30 @@ class Solution { } ``` +```csharp +public class Solution { + public List> Combine(int n, int k) { + List> res = new List>(); + + void Backtrack(int start, List comb) { + if (comb.Count == k) { + res.Add(new List(comb)); + return; + } + + for (int i = start; i <= n; i++) { + comb.Add(i); + Backtrack(i + 1, comb); + comb.RemoveAt(comb.Count - 1); + } + } + + Backtrack(1, new List()); + return res; + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -348,6 +397,33 @@ class Solution { } ``` +```csharp +public class Solution { + public List> Combine(int n, int k) { + List> res = new List>(); + int[] comb = new int[k]; + int i = 0; + + while (i >= 0) { + comb[i]++; + if (comb[i] > n) { + i--; + continue; + } + + if (i == k - 1) { + res.Add(new List(comb)); + } else { + i++; + comb[i] = comb[i - 1]; + } + } + + return res; + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -446,6 +522,28 @@ class Solution { } ``` +```csharp +public class Solution { + public List> Combine(int n, int k) { + List> res = new List>(); + + for (int mask = 0; mask < (1 << n); mask++) { + List comb = new List(); + for (int bit = 0; bit < n; bit++) { + if ((mask & (1 << bit)) != 0) { + comb.Add(bit + 1); + } + } + if (comb.Count == k) { + res.Add(comb); + } + } + + return res; + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/construct-quad-tree.md b/articles/construct-quad-tree.md index 48e117a86..5e2b91594 100644 --- a/articles/construct-quad-tree.md +++ b/articles/construct-quad-tree.md @@ -237,6 +237,76 @@ class Solution { } ``` +```csharp +/* +// Definition for a QuadTree node. +public class Node { + public bool val; + public bool isLeaf; + public Node topLeft; + public Node topRight; + public Node bottomLeft; + public Node bottomRight; + + public Node() { + val = false; + isLeaf = false; + topLeft = null; + topRight = null; + bottomLeft = null; + bottomRight = null; + } + + public Node(bool _val, bool _isLeaf) { + val = _val; + isLeaf = _isLeaf; + topLeft = null; + topRight = null; + bottomLeft = null; + bottomRight = null; + } + + public Node(bool _val,bool _isLeaf,Node _topLeft,Node _topRight,Node _bottomLeft,Node _bottomRight) { + val = _val; + isLeaf = _isLeaf; + topLeft = _topLeft; + topRight = _topRight; + bottomLeft = _bottomLeft; + bottomRight = _bottomRight; + } +} +*/ + +public class Solution { + public Node Construct(int[][] grid) { + return Dfs(grid, grid.Length, 0, 0); + } + + private Node Dfs(int[][] grid, int n, int r, int c) { + bool allSame = true; + for (int i = 0; i < n && allSame; i++) { + for (int j = 0; j < n && allSame; j++) { + if (grid[r][c] != grid[r + i][c + j]) { + allSame = false; + } + } + } + + if (allSame) { + return new Node(grid[r][c] == 1, true); + } + + int half = n / 2; + Node topLeft = Dfs(grid, half, r, c); + Node topRight = Dfs(grid, half, r, c + half); + Node bottomLeft = Dfs(grid, half, r + half, c); + Node bottomRight = Dfs(grid, half, r + half, c + half); + + return new Node(false, false, topLeft, topRight, bottomLeft, bottomRight); + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -475,6 +545,75 @@ class Solution { } ``` +```csharp +/* +// Definition for a QuadTree node. +public class Node { + public bool val; + public bool isLeaf; + public Node topLeft; + public Node topRight; + public Node bottomLeft; + public Node bottomRight; + + public Node() { + val = false; + isLeaf = false; + topLeft = null; + topRight = null; + bottomLeft = null; + bottomRight = null; + } + + public Node(bool _val, bool _isLeaf) { + val = _val; + isLeaf = _isLeaf; + topLeft = null; + topRight = null; + bottomLeft = null; + bottomRight = null; + } + + public Node(bool _val,bool _isLeaf,Node _topLeft,Node _topRight,Node _bottomLeft,Node _bottomRight) { + val = _val; + isLeaf = _isLeaf; + topLeft = _topLeft; + topRight = _topRight; + bottomLeft = _bottomLeft; + bottomRight = _bottomRight; + } +} +*/ + +public class Solution { + public Node Construct(int[][] grid) { + return Dfs(grid, grid.Length, 0, 0); + } + + private Node Dfs(int[][] grid, int n, int r, int c) { + if (n == 1) { + return new Node(grid[r][c] == 1, true); + } + + int mid = n / 2; + Node topLeft = Dfs(grid, mid, r, c); + Node topRight = Dfs(grid, mid, r, c + mid); + Node bottomLeft = Dfs(grid, mid, r + mid, c); + Node bottomRight = Dfs(grid, mid, r + mid, c + mid); + + if (topLeft.isLeaf && topRight.isLeaf && + bottomLeft.isLeaf && bottomRight.isLeaf && + topLeft.val == topRight.val && + topRight.val == bottomLeft.val && + bottomLeft.val == bottomRight.val) { + return new Node(topLeft.val, true); + } + + return new Node(false, false, topLeft, topRight, bottomLeft, bottomRight); + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -725,6 +864,79 @@ class Solution { } ``` +```csharp +/* +// Definition for a QuadTree node. +public class Node { + public bool val; + public bool isLeaf; + public Node topLeft; + public Node topRight; + public Node bottomLeft; + public Node bottomRight; + + public Node() { + val = false; + isLeaf = false; + topLeft = null; + topRight = null; + bottomLeft = null; + bottomRight = null; + } + + public Node(bool _val, bool _isLeaf) { + val = _val; + isLeaf = _isLeaf; + topLeft = null; + topRight = null; + bottomLeft = null; + bottomRight = null; + } + + public Node(bool _val,bool _isLeaf,Node _topLeft,Node _topRight,Node _bottomLeft,Node _bottomRight) { + val = _val; + isLeaf = _isLeaf; + topLeft = _topLeft; + topRight = _topRight; + bottomLeft = _bottomLeft; + bottomRight = _bottomRight; + } +} +*/ + +public class Solution { + private readonly Node falseLeaf = new Node(false, true); + private readonly Node trueLeaf = new Node(true, true); + + public Node Construct(int[][] grid) { + int n = grid.Length; + return Dfs(grid, n, 0, 0); + } + + private Node Dfs(int[][] grid, int n, int r, int c) { + if (n == 1) { + return grid[r][c] == 1 ? trueLeaf : falseLeaf; + } + + int half = n / 2; + Node topLeft = Dfs(grid, half, r, c); + Node topRight = Dfs(grid, half, r, c + half); + Node bottomLeft = Dfs(grid, half, r + half, c); + Node bottomRight = Dfs(grid, half, r + half, c + half); + + if (topLeft.isLeaf && topRight.isLeaf && + bottomLeft.isLeaf && bottomRight.isLeaf && + topLeft.val == topRight.val && + topLeft.val == bottomLeft.val && + topLeft.val == bottomRight.val) { + return topLeft; + } + + return new Node(false, false, topLeft, topRight, bottomLeft, bottomRight); + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/contains-duplicate-ii.md b/articles/contains-duplicate-ii.md index 9e1c9367e..cc98d9da1 100644 --- a/articles/contains-duplicate-ii.md +++ b/articles/contains-duplicate-ii.md @@ -63,6 +63,21 @@ class Solution { } ``` +```csharp +public class Solution { + public bool ContainsNearbyDuplicate(int[] nums, int k) { + for (int L = 0; L < nums.Length; L++) { + for (int R = L + 1; R < Math.Min(nums.Length, L + k + 1); R++) { + if (nums[L] == nums[R]) { + return true; + } + } + } + return false; + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -148,6 +163,23 @@ class Solution { } ``` +```csharp +public class Solution { + public bool ContainsNearbyDuplicate(int[] nums, int k) { + Dictionary mp = new Dictionary(); + + for (int i = 0; i < nums.Length; i++) { + if (mp.ContainsKey(nums[i]) && i - mp[nums[i]] <= k) { + return true; + } + mp[nums[i]] = i; + } + + return false; + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -249,6 +281,28 @@ class Solution { } ``` +```csharp +public class Solution { + public bool ContainsNearbyDuplicate(int[] nums, int k) { + HashSet window = new HashSet(); + int L = 0; + + for (int R = 0; R < nums.Length; R++) { + if (R - L > k) { + window.Remove(nums[L]); + L++; + } + if (window.Contains(nums[R])) { + return true; + } + window.Add(nums[R]); + } + + return false; + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/course-schedule-iv.md b/articles/course-schedule-iv.md index 0768d9038..0738453e4 100644 --- a/articles/course-schedule-iv.md +++ b/articles/course-schedule-iv.md @@ -109,6 +109,35 @@ class Solution { } ``` +```csharp +public class Solution { + public List CheckIfPrerequisite(int numCourses, int[][] prerequisites, int[][] queries) { + List[] adj = new List[numCourses]; + for (int i = 0; i < numCourses; i++) { + adj[i] = new List(); + } + + foreach (var pre in prerequisites) { + adj[pre[0]].Add(pre[1]); + } + + bool Dfs(int node, int target) { + if (node == target) return true; + foreach (var nei in adj[node]) { + if (Dfs(nei, target)) return true; + } + return false; + } + + var res = new List(); + foreach (var q in queries) { + res.Add(Dfs(q[0], q[1])); + } + return res; + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -259,6 +288,46 @@ class Solution { } ``` +```csharp +public class Solution { + public List CheckIfPrerequisite(int numCourses, int[][] prerequisites, int[][] queries) { + Dictionary> adj = new Dictionary>(); + for (int i = 0; i < numCourses; i++) { + adj[i] = new List(); + } + + foreach (var pair in prerequisites) { + int prereq = pair[0], crs = pair[1]; + adj[crs].Add(prereq); + } + + Dictionary> prereqMap = new Dictionary>(); + + HashSet Dfs(int crs) { + if (!prereqMap.ContainsKey(crs)) { + prereqMap[crs] = new HashSet(); + foreach (var prereq in adj[crs]) { + prereqMap[crs].UnionWith(Dfs(prereq)); + } + prereqMap[crs].Add(crs); + } + return prereqMap[crs]; + } + + for (int crs = 0; crs < numCourses; crs++) { + Dfs(crs); + } + + List res = new List(); + foreach (var q in queries) { + res.Add(prereqMap.ContainsKey(q[1]) && prereqMap[q[1]].Contains(q[0])); + } + + return res; + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -416,6 +485,53 @@ class Solution { } ``` +```csharp +public class Solution { + public List CheckIfPrerequisite(int numCourses, int[][] prerequisites, int[][] queries) { + List[] adj = new List[numCourses]; + for (int i = 0; i < numCourses; i++) { + adj[i] = new List(); + } + + int[,] isPrereq = new int[numCourses, numCourses]; + for (int i = 0; i < numCourses; i++) { + for (int j = 0; j < numCourses; j++) { + isPrereq[i, j] = -1; + } + } + + foreach (var pair in prerequisites) { + int prereq = pair[0], crs = pair[1]; + adj[crs].Add(prereq); + isPrereq[crs, prereq] = 1; + } + + bool Dfs(int crs, int prereq) { + if (isPrereq[crs, prereq] != -1) { + return isPrereq[crs, prereq] == 1; + } + + foreach (int pre in adj[crs]) { + if (pre == prereq || Dfs(pre, prereq)) { + isPrereq[crs, prereq] = 1; + return true; + } + } + + isPrereq[crs, prereq] = 0; + return false; + } + + List res = new List(); + foreach (var q in queries) { + res.Add(Dfs(q[1], q[0])); + } + + return res; + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -572,6 +688,56 @@ class Solution { } ``` +```csharp +public class Solution { + public List CheckIfPrerequisite(int numCourses, int[][] prerequisites, int[][] queries) { + List> adj = new List>(); + List> isPrereq = new List>(); + int[] indegree = new int[numCourses]; + + for (int i = 0; i < numCourses; i++) { + adj.Add(new HashSet()); + isPrereq.Add(new HashSet()); + } + + foreach (var pair in prerequisites) { + int pre = pair[0], crs = pair[1]; + adj[pre].Add(crs); + indegree[crs]++; + } + + Queue q = new Queue(); + for (int i = 0; i < numCourses; i++) { + if (indegree[i] == 0) { + q.Enqueue(i); + } + } + + while (q.Count > 0) { + int node = q.Dequeue(); + foreach (int neighbor in adj[node]) { + isPrereq[neighbor].Add(node); + foreach (int p in isPrereq[node]) { + isPrereq[neighbor].Add(p); + } + indegree[neighbor]--; + if (indegree[neighbor] == 0) { + q.Enqueue(neighbor); + } + } + } + + List result = new List(); + foreach (var query in queries) { + int u = query[0], v = query[1]; + result.Add(isPrereq[v].Contains(u)); + } + + return result; + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -695,6 +861,35 @@ class Solution { } ``` +```csharp +public class Solution { + public List CheckIfPrerequisite(int numCourses, int[][] prerequisites, int[][] queries) { + bool[,] adj = new bool[numCourses, numCourses]; + + foreach (var pair in prerequisites) { + int pre = pair[0], crs = pair[1]; + adj[pre, crs] = true; + } + + for (int k = 0; k < numCourses; k++) { + for (int i = 0; i < numCourses; i++) { + for (int j = 0; j < numCourses; j++) { + adj[i, j] = adj[i, j] || (adj[i, k] && adj[k, j]); + } + } + } + + List res = new List(); + foreach (var query in queries) { + int u = query[0], v = query[1]; + res.Add(adj[u, v]); + } + + return res; + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/decode-string.md b/articles/decode-string.md index c5363f71b..c86b9b8f1 100644 --- a/articles/decode-string.md +++ b/articles/decode-string.md @@ -143,6 +143,46 @@ class Solution { } ``` +```csharp +public class Solution { + private int i; + + public string DecodeString(string s) { + i = 0; + return Helper(s); + } + + private string Helper(string s) { + string res = ""; + int k = 0; + + while (i < s.Length) { + char c = s[i]; + + if (char.IsDigit(c)) { + k = k * 10 + (c - '0'); + } else if (c == '[') { + i++; + string inner = Helper(s); + res += new string(' ', 0).PadLeft(0); + for (int j = 0; j < k; j++) { + res += inner; + } + k = 0; + } else if (c == ']') { + return res; + } else { + res += c; + } + + i++; + } + + return res; + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -290,6 +330,41 @@ class Solution { } ``` +```csharp +public class Solution { + public string DecodeString(string s) { + Stack stack = new Stack(); + + for (int i = 0; i < s.Length; i++) { + if (s[i] != ']') { + stack.Push(s[i].ToString()); + } else { + string substr = ""; + while (stack.Peek() != "[") { + substr = stack.Pop() + substr; + } + stack.Pop(); // remove '[' + + string k = ""; + while (stack.Count > 0 && char.IsDigit(stack.Peek()[0])) { + k = stack.Pop() + k; + } + + int repeat = int.Parse(k); + string expanded = new StringBuilder().Insert(0, substr, repeat).ToString(); + stack.Push(expanded); + } + } + + var result = new StringBuilder(); + foreach (string part in stack) { + result.Insert(0, part); + } + return result.ToString(); + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -436,6 +511,39 @@ class Solution { } ``` +```csharp +public class Solution { + public string DecodeString(string s) { + Stack stringStack = new Stack(); + Stack countStack = new Stack(); + string cur = ""; + int k = 0; + + foreach (char c in s) { + if (char.IsDigit(c)) { + k = k * 10 + (c - '0'); + } else if (c == '[') { + stringStack.Push(cur); + countStack.Push(k); + cur = ""; + k = 0; + } else if (c == ']') { + string temp = cur; + cur = stringStack.Pop(); + int count = countStack.Pop(); + for (int i = 0; i < count; i++) { + cur += temp; + } + } else { + cur += c; + } + } + + return cur; + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/delete-leaves-with-a-given-value.md b/articles/delete-leaves-with-a-given-value.md index ccfb4637b..add3a0494 100644 --- a/articles/delete-leaves-with-a-given-value.md +++ b/articles/delete-leaves-with-a-given-value.md @@ -122,6 +122,36 @@ class Solution { } ``` +```csharp +/** + * Definition for a binary tree node. + * public class TreeNode { + * public int val; + * public TreeNode left; + * public TreeNode right; + * public TreeNode(int val=0, TreeNode left=null, TreeNode right=null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public TreeNode RemoveLeafNodes(TreeNode root, int target) { + if (root == null) return null; + + root.left = RemoveLeafNodes(root.left, target); + root.right = RemoveLeafNodes(root.right, target); + + if (root.left == null && root.right == null && root.val == target) { + return null; + } + + return root; + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -343,6 +373,62 @@ class Solution { } ``` +```csharp +/** + * Definition for a binary tree node. + * public class TreeNode { + * public int val; + * public TreeNode left; + * public TreeNode right; + * public TreeNode(int val=0, TreeNode left=null, TreeNode right=null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public TreeNode RemoveLeafNodes(TreeNode root, int target) { + if (root == null) return null; + + Stack stack = new Stack(); + HashSet visited = new HashSet(); + Dictionary parents = new Dictionary(); + + stack.Push(root); + parents[root] = null; + + while (stack.Count > 0) { + TreeNode node = stack.Pop(); + + if (node.left == null && node.right == null) { + if (node.val == target) { + TreeNode parent = parents[node]; + if (parent == null) { + return null; + } + if (parent.left == node) parent.left = null; + if (parent.right == node) parent.right = null; + } + } else if (!visited.Contains(node)) { + visited.Add(node); + stack.Push(node); + if (node.left != null) { + stack.Push(node.left); + parents[node.left] = node; + } + if (node.right != null) { + stack.Push(node.right); + parents[node.right] = node; + } + } + } + + return root; + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -569,6 +655,62 @@ class Solution { } ``` +```csharp +/** + * Definition for a binary tree node. + * public class TreeNode { + * public int val; + * public TreeNode left; + * public TreeNode right; + * public TreeNode(int val=0, TreeNode left=null, TreeNode right=null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public TreeNode RemoveLeafNodes(TreeNode root, int target) { + if (root == null) return null; + + Stack stack = new Stack(); + TreeNode cur = root; + TreeNode visited = null; + + while (stack.Count > 0 || cur != null) { + while (cur != null) { + stack.Push(cur); + cur = cur.left; + } + + cur = stack.Peek(); + if (cur.right != null && cur.right != visited) { + cur = cur.right; + continue; + } + + stack.Pop(); + if (cur.left == null && cur.right == null && cur.val == target) { + if (stack.Count == 0) return null; + + TreeNode parent = stack.Peek(); + if (parent.left == cur) { + parent.left = null; + } else if (parent.right == cur) { + parent.right = null; + } + } else { + visited = cur; + } + + cur = null; + } + + return root; + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/delete-node-in-a-bst.md b/articles/delete-node-in-a-bst.md index 4b086145a..0a9617193 100644 --- a/articles/delete-node-in-a-bst.md +++ b/articles/delete-node-in-a-bst.md @@ -153,6 +153,45 @@ class Solution { } ``` +```csharp +/** + * Definition for a binary tree node. + * public class TreeNode { + * public int val; + * public TreeNode left; + * public TreeNode right; + * public TreeNode(int val=0, TreeNode left=null, TreeNode right=null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public TreeNode DeleteNode(TreeNode root, int key) { + if (root == null) return null; + + if (key > root.val) { + root.right = DeleteNode(root.right, key); + } else if (key < root.val) { + root.left = DeleteNode(root.left, key); + } else { + if (root.left == null) return root.right; + if (root.right == null) return root.left; + + TreeNode cur = root.right; + while (cur.left != null) { + cur = cur.left; + } + root.val = cur.val; + root.right = DeleteNode(root.right, root.val); + } + + return root; + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -327,6 +366,46 @@ class Solution { } ``` +```csharp +/** + * Definition for a binary tree node. + * public class TreeNode { + * public int val; + * public TreeNode left; + * public TreeNode right; + * public TreeNode(int val=0, TreeNode left=null, TreeNode right=null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public TreeNode DeleteNode(TreeNode root, int key) { + if (root == null) return null; + + if (key > root.val) { + root.right = DeleteNode(root.right, key); + } else if (key < root.val) { + root.left = DeleteNode(root.left, key); + } else { + if (root.left == null) return root.right; + if (root.right == null) return root.left; + + TreeNode cur = root.right; + while (cur.left != null) { + cur = cur.left; + } + cur.left = root.left; + TreeNode res = root.right; + return res; + } + + return root; + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -622,6 +701,85 @@ class Solution { } ``` +```csharp +/** + * Definition for a binary tree node. + * public class TreeNode { + * public int val; + * public TreeNode left; + * public TreeNode right; + * public TreeNode(int val=0, TreeNode left=null, TreeNode right=null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public TreeNode DeleteNode(TreeNode root, int key) { + if (root == null) return null; + + TreeNode parent = null; + TreeNode cur = root; + + // Find the node to delete + while (cur != null && cur.val != key) { + parent = cur; + if (key > cur.val) { + cur = cur.right; + } else { + cur = cur.left; + } + } + + if (cur == null) return root; + + // Node with one or no child + if (cur.left == null || cur.right == null) { + TreeNode child = cur.left != null ? cur.left : cur.right; + + if (parent == null) { + return child; + } + + if (parent.left == cur) { + parent.left = child; + } else { + parent.right = child; + } + } else { + // Node with two children + TreeNode par = null; + TreeNode delNode = cur; + cur = cur.right; + while (cur.left != null) { + par = cur; + cur = cur.left; + } + + if (par != null) { + par.left = cur.right; + cur.right = delNode.right; + } + + cur.left = delNode.left; + + if (parent == null) { + return cur; + } + + if (parent.left == delNode) { + parent.left = cur; + } else { + parent.right = cur; + } + } + + return root; + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/evaluate-division.md b/articles/evaluate-division.md index 647a8002b..9be40ad06 100644 --- a/articles/evaluate-division.md +++ b/articles/evaluate-division.md @@ -199,6 +199,56 @@ class Solution { } ``` +```csharp +public class Solution { + public double[] CalcEquation(List> equations, double[] values, List> queries) { + var adj = new Dictionary>(); + + for (int i = 0; i < equations.Count; i++) { + string a = equations[i][0]; + string b = equations[i][1]; + double val = values[i]; + + if (!adj.ContainsKey(a)) adj[a] = new List<(string, double)>(); + if (!adj.ContainsKey(b)) adj[b] = new List<(string, double)>(); + + adj[a].Add((b, val)); + adj[b].Add((a, 1.0 / val)); + } + + double Bfs(string src, string target) { + if (!adj.ContainsKey(src) || !adj.ContainsKey(target)) return -1.0; + var queue = new Queue<(string, double)>(); + var visited = new HashSet(); + + queue.Enqueue((src, 1.0)); + visited.Add(src); + + while (queue.Count > 0) { + var (node, weight) = queue.Dequeue(); + if (node == target) return weight; + + foreach (var (nei, w) in adj[node]) { + if (!visited.Contains(nei)) { + visited.Add(nei); + queue.Enqueue((nei, weight * w)); + } + } + } + + return -1.0; + } + + double[] res = new double[queries.Count]; + for (int i = 0; i < queries.Count; i++) { + res[i] = Bfs(queries[i][0], queries[i][1]); + } + + return res; + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -391,6 +441,51 @@ class Solution { } ``` +```csharp +public class Solution { + public double[] CalcEquation(List> equations, double[] values, List> queries) { + var adj = new Dictionary>(); + + for (int i = 0; i < equations.Count; i++) { + string a = equations[i][0], b = equations[i][1]; + double val = values[i]; + + if (!adj.ContainsKey(a)) adj[a] = new List<(string, double)>(); + if (!adj.ContainsKey(b)) adj[b] = new List<(string, double)>(); + + adj[a].Add((b, val)); + adj[b].Add((a, 1.0 / val)); + } + + double Dfs(string src, string target, HashSet visited) { + if (!adj.ContainsKey(src) || !adj.ContainsKey(target)) return -1.0; + if (src == target) return 1.0; + + visited.Add(src); + + foreach (var (nei, weight) in adj[src]) { + if (!visited.Contains(nei)) { + double result = Dfs(nei, target, visited); + if (result != -1.0) { + return weight * result; + } + } + } + + return -1.0; + } + + double[] res = new double[queries.Count]; + for (int i = 0; i < queries.Count; i++) { + var visited = new HashSet(); + res[i] = Dfs(queries[i][0], queries[i][1], visited); + } + + return res; + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -666,6 +761,69 @@ class Solution { } ``` +```csharp +public class UnionFind { + private Dictionary parent = new Dictionary(); + private Dictionary weight = new Dictionary(); + + public void Add(string x) { + if (!parent.ContainsKey(x)) { + parent[x] = x; + weight[x] = 1.0; + } + } + + public string Find(string x) { + if (parent[x] != x) { + string origParent = parent[x]; + parent[x] = Find(origParent); + weight[x] *= weight[origParent]; + } + return parent[x]; + } + + public void Union(string x, string y, double value) { + Add(x); + Add(y); + string rootX = Find(x); + string rootY = Find(y); + + if (rootX != rootY) { + parent[rootX] = rootY; + weight[rootX] = value * weight[y] / weight[x]; + } + } + + public double GetRatio(string x, string y) { + if (!parent.ContainsKey(x) || !parent.ContainsKey(y) || Find(x) != Find(y)) { + return -1.0; + } + return weight[x] / weight[y]; + } +} + +public class Solution { + public double[] CalcEquation(List> equations, double[] values, List> queries) { + var uf = new UnionFind(); + + for (int i = 0; i < equations.Count; i++) { + string a = equations[i][0]; + string b = equations[i][1]; + uf.Union(a, b, values[i]); + } + + double[] res = new double[queries.Count]; + for (int i = 0; i < queries.Count; i++) { + string a = queries[i][0]; + string b = queries[i][1]; + res[i] = uf.GetRatio(a, b); + } + + return res; + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -826,6 +984,49 @@ class Solution { } ``` +```csharp +public class Solution { + public double[] CalcEquation(List> equations, double[] values, List> queries) { + var graph = new Dictionary>(); + + for (int i = 0; i < equations.Count; i++) { + string a = equations[i][0]; + string b = equations[i][1]; + double val = values[i]; + + if (!graph.ContainsKey(a)) graph[a] = new Dictionary(); + if (!graph.ContainsKey(b)) graph[b] = new Dictionary(); + + graph[a][b] = val; + graph[b][a] = 1.0 / val; + } + + foreach (var k in graph.Keys.ToList()) { + foreach (var i in graph[k].Keys.ToList()) { + foreach (var j in graph[k].Keys.ToList()) { + if (!graph[i].ContainsKey(j)) { + graph[i][j] = graph[i][k] * graph[k][j]; + } + } + } + } + + double[] result = new double[queries.Count]; + for (int i = 0; i < queries.Count; i++) { + string a = queries[i][0]; + string b = queries[i][1]; + if (graph.ContainsKey(a) && graph[a].ContainsKey(b)) { + result[i] = graph[a][b]; + } else { + result[i] = -1.0; + } + } + + return result; + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/extra-characters-in-a-string.md b/articles/extra-characters-in-a-string.md index b41af1f45..6b2052693 100644 --- a/articles/extra-characters-in-a-string.md +++ b/articles/extra-characters-in-a-string.md @@ -104,6 +104,30 @@ class Solution { } ``` +```csharp +public class Solution { + public int MinExtraChar(string s, string[] dictionary) { + HashSet words = new HashSet(dictionary); + return Dfs(0, s, words); + } + + private int Dfs(int i, string s, HashSet words) { + if (i == s.Length) { + return 0; + } + + int res = 1 + Dfs(i + 1, s, words); + for (int j = i; j < s.Length; j++) { + if (words.Contains(s.Substring(i, j - i + 1))) { + res = Math.Min(res, Dfs(j + 1, s, words)); + } + } + + return res; + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -220,6 +244,34 @@ class Solution { } ``` +```csharp +public class Solution { + public int MinExtraChar(string s, string[] dictionary) { + HashSet words = new HashSet(dictionary); + int n = s.Length; + int[] dp = new int[n + 1]; + Array.Fill(dp, -1); + dp[n] = 0; + + return Dfs(0, s, words, dp); + } + + private int Dfs(int i, string s, HashSet words, int[] dp) { + if (dp[i] != -1) return dp[i]; + + int res = 1 + Dfs(i + 1, s, words, dp); + for (int j = i; j < s.Length; j++) { + if (words.Contains(s.Substring(i, j - i + 1))) { + res = Math.Min(res, Dfs(j + 1, s, words, dp)); + } + } + + dp[i] = res; + return res; + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -316,6 +368,27 @@ class Solution { } ``` +```csharp +public class Solution { + public int MinExtraChar(string s, string[] dictionary) { + HashSet words = new HashSet(dictionary); + int n = s.Length; + int[] dp = new int[n + 1]; + + for (int i = n - 1; i >= 0; i--) { + dp[i] = 1 + dp[i + 1]; + for (int j = i; j < n; j++) { + if (words.Contains(s.Substring(i, j - i + 1))) { + dp[i] = Math.Min(dp[i], dp[j + 1]); + } + } + } + + return dp[0]; + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -467,6 +540,43 @@ class Solution { } ``` +```csharp +public class Solution { + public int MinExtraChar(string s, string[] dictionary) { + Dictionary dp = new Dictionary(); + dp[s.Length] = 0; + return Dfs(0, s, dictionary, dp); + } + + private int Dfs(int i, string s, string[] dictionary, Dictionary dp) { + if (dp.ContainsKey(i)) { + return dp[i]; + } + + int res = 1 + Dfs(i + 1, s, dictionary, dp); + + foreach (string word in dictionary) { + if (i + word.Length > s.Length) continue; + + bool flag = true; + for (int j = 0; j < word.Length; j++) { + if (s[i + j] != word[j]) { + flag = false; + break; + } + } + + if (flag) { + res = Math.Min(res, Dfs(i + word.Length, s, dictionary, dp)); + } + } + + dp[i] = res; + return res; + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -563,6 +673,26 @@ class Solution { } ``` +```csharp +public class Solution { + public int MinExtraChar(string s, string[] dictionary) { + int n = s.Length; + int[] dp = new int[n + 1]; + + for (int i = n - 1; i >= 0; i--) { + dp[i] = 1 + dp[i + 1]; + foreach (string word in dictionary) { + if (i + word.Length <= n && s.Substring(i, word.Length) == word) { + dp[i] = Math.Min(dp[i], dp[i + word.Length]); + } + } + } + + return dp[0]; + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -819,6 +949,63 @@ class Solution { } ``` +```csharp +public class TrieNode { + public TrieNode[] Children = new TrieNode[26]; + public bool IsWord = false; +} + +public class Trie { + public TrieNode Root = new TrieNode(); + + public void AddWord(string word) { + TrieNode curr = Root; + foreach (char c in word) { + int idx = c - 'a'; + if (curr.Children[idx] == null) { + curr.Children[idx] = new TrieNode(); + } + curr = curr.Children[idx]; + } + curr.IsWord = true; + } +} + +public class Solution { + public int MinExtraChar(string s, string[] dictionary) { + Trie trie = new Trie(); + foreach (string word in dictionary) { + trie.AddWord(word); + } + + int[] dp = new int[s.Length + 1]; + Array.Fill(dp, -1); + + return Dfs(0, s, trie, dp); + } + + private int Dfs(int i, string s, Trie trie, int[] dp) { + if (i == s.Length) return 0; + if (dp[i] != -1) return dp[i]; + + int res = 1 + Dfs(i + 1, s, trie, dp); + TrieNode curr = trie.Root; + + for (int j = i; j < s.Length; j++) { + int idx = s[j] - 'a'; + if (curr.Children[idx] == null) break; + curr = curr.Children[idx]; + if (curr.IsWord) { + res = Math.Min(res, Dfs(j + 1, s, trie, dp)); + } + } + + dp[i] = res; + return res; + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -1052,6 +1239,57 @@ class Solution { } ``` +```csharp +public class TrieNode { + public TrieNode[] Children = new TrieNode[26]; + public bool IsWord = false; +} + +public class Trie { + public TrieNode Root = new TrieNode(); + + public void AddWord(string word) { + TrieNode curr = Root; + foreach (char c in word) { + int idx = c - 'a'; + if (curr.Children[idx] == null) { + curr.Children[idx] = new TrieNode(); + } + curr = curr.Children[idx]; + } + curr.IsWord = true; + } +} + +public class Solution { + public int MinExtraChar(string s, string[] dictionary) { + Trie trie = new Trie(); + foreach (string word in dictionary) { + trie.AddWord(word); + } + + int n = s.Length; + int[] dp = new int[n + 1]; + + for (int i = n - 1; i >= 0; i--) { + dp[i] = 1 + dp[i + 1]; + TrieNode curr = trie.Root; + + for (int j = i; j < n; j++) { + int idx = s[j] - 'a'; + if (curr.Children[idx] == null) break; + curr = curr.Children[idx]; + if (curr.IsWord) { + dp[i] = Math.Min(dp[i], dp[j + 1]); + } + } + } + + return dp[0]; + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/find-in-mountain-array.md b/articles/find-in-mountain-array.md index f687ba271..c917a7507 100644 --- a/articles/find-in-mountain-array.md +++ b/articles/find-in-mountain-array.md @@ -78,18 +78,18 @@ public: /** * // This is the MountainArray's API interface. * // You should not implement it, or speculate about its implementation - * function MountainArray() { + * class MountainArray { * @param {number} index * @return {number} - * this.get = function(index) { + * get(index) { * ... - * }; + * } * * @return {number} - * this.length = function() { + * length() { * ... - * }; - * }; + * } + * } */ class Solution { @@ -112,6 +112,31 @@ class Solution { } ``` +```csharp +/** + * // This is MountainArray's API interface. + * // You should not implement it, or speculate about its implementation + * class MountainArray { + * public int Get(int index) {} + * public int Length() {} + * } + */ + +class Solution { + public int FindInMountainArray(int target, MountainArray mountainArr) { + int n = mountainArr.Length(); + + for (int i = 0; i < n; i++) { + if (mountainArr.Get(i) == target) { + return i; + } + } + + return -1; + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -316,18 +341,18 @@ public: /** * // This is the MountainArray's API interface. * // You should not implement it, or speculate about its implementation - * function MountainArray() { + * class MountainArray { * @param {number} index * @return {number} - * this.get = function(index) { + * get(index) { * ... - * }; + * } * * @return {number} - * this.length = function() { + * length() { * ... - * }; - * }; + * } + * } */ class Solution { @@ -391,6 +416,72 @@ class Solution { } ``` +```csharp +/** + * // This is MountainArray's API interface. + * // You should not implement it, or speculate about its implementation + * class MountainArray { + * public int Get(int index) {} + * public int Length() {} + * } + */ + +class Solution { + public int FindInMountainArray(int target, MountainArray mountainArr) { + int length = mountainArr.Length(); + + // Find Peak + int l = 1, r = length - 2, peak = 0; + while (l <= r) { + int m = (l + r) / 2; + int left = mountainArr.Get(m - 1); + int mid = mountainArr.Get(m); + int right = mountainArr.Get(m + 1); + if (left < mid && mid < right) { + l = m + 1; + } else if (left > mid && mid > right) { + r = m - 1; + } else { + peak = m; + break; + } + } + + // Search left portion + l = 0; + r = peak - 1; + while (l <= r) { + int m = (l + r) / 2; + int val = mountainArr.Get(m); + if (val < target) { + l = m + 1; + } else if (val > target) { + r = m - 1; + } else { + return m; + } + } + + // Search right portion + l = peak; + r = length - 1; + while (l <= r) { + int m = (l + r) / 2; + int val = mountainArr.Get(m); + if (val > target) { + l = m + 1; + } else if (val < target) { + r = m - 1; + } else { + return m; + } + } + + return -1; + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -600,18 +691,18 @@ public: /** * // This is the MountainArray's API interface. * // You should not implement it, or speculate about its implementation - * function MountainArray() { + * class MountainArray { * @param {number} index * @return {number} - * this.get = function(index) { + * get(index) { * ... - * }; + * } * * @return {number} - * this.length = function() { + * length() { * ... - * }; - * }; + * } + * } */ class Solution { @@ -676,6 +767,73 @@ class Solution { } ``` +```csharp +/** + * // This is MountainArray's API interface. + * // You should not implement it, or speculate about its implementation + * class MountainArray { + * public int Get(int index) {} + * public int Length() {} + * } + */ + +class Solution { + private Dictionary cache = new Dictionary(); + + private int Get(int index, MountainArray mountainArr) { + if (!cache.ContainsKey(index)) { + cache[index] = mountainArr.Get(index); + } + return cache[index]; + } + + private int BinarySearch(int l, int r, bool ascending, MountainArray mountainArr, int target) { + while (l <= r) { + int m = (l + r) >> 1; + int val = Get(m, mountainArr); + if (val == target) { + return m; + } + if ((ascending && val < target) || (!ascending && val > target)) { + l = m + 1; + } else { + r = m - 1; + } + } + return -1; + } + + public int FindInMountainArray(int target, MountainArray mountainArr) { + int length = mountainArr.Length(); + + // Find Peak + int l = 1, r = length - 2, peak = 0; + while (l <= r) { + int m = (l + r) >> 1; + int left = Get(m - 1, mountainArr); + int mid = Get(m, mountainArr); + int right = Get(m + 1, mountainArr); + + if (left < mid && mid < right) { + l = m + 1; + } else if (left > mid && mid > right) { + r = m - 1; + } else { + peak = m; + break; + } + } + + // Search left portion + int res = BinarySearch(0, peak, true, mountainArr, target); + if (res != -1) return res; + + // Search right portion + return BinarySearch(peak, length - 1, false, mountainArr, target); + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/find-the-town-judge.md b/articles/find-the-town-judge.md index 393448cd8..a1343bd35 100644 --- a/articles/find-the-town-judge.md +++ b/articles/find-the-town-judge.md @@ -89,6 +89,30 @@ class Solution { } ``` +```csharp +public class Solution { + public int FindJudge(int n, int[][] trust) { + int[] incoming = new int[n + 1]; + int[] outgoing = new int[n + 1]; + + foreach (int[] t in trust) { + int a = t[0]; + int b = t[1]; + outgoing[a]++; + incoming[b]++; + } + + for (int i = 1; i <= n; i++) { + if (outgoing[i] == 0 && incoming[i] == n - 1) { + return i; + } + } + + return -1; + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -189,6 +213,28 @@ class Solution { } ``` +```csharp +public class Solution { + public int FindJudge(int n, int[][] trust) { + int[] delta = new int[n + 1]; + + foreach (int[] t in trust) { + int a = t[0]; + int b = t[1]; + delta[a]--; + delta[b]++; + } + + for (int i = 1; i <= n; i++) { + if (delta[i] == n - 1) { + return i; + } + } + return -1; + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/greatest-common-divisor-traversal.md b/articles/greatest-common-divisor-traversal.md index 79c11cf70..57628c98a 100644 --- a/articles/greatest-common-divisor-traversal.md +++ b/articles/greatest-common-divisor-traversal.md @@ -144,6 +144,53 @@ class Solution { } ``` +```csharp +public class Solution { + public bool CanTraverseAllPairs(int[] nums) { + int n = nums.Length; + bool[] visited = new bool[n]; + List[] adj = new List[n]; + for (int i = 0; i < n; i++) { + adj[i] = new List(); + } + + for (int i = 0; i < n; i++) { + for (int j = i + 1; j < n; j++) { + if (GCD(nums[i], nums[j]) > 1) { + adj[i].Add(j); + adj[j].Add(i); + } + } + } + + void DFS(int node) { + visited[node] = true; + foreach (int neighbor in adj[node]) { + if (!visited[neighbor]) { + DFS(neighbor); + } + } + } + + DFS(0); + foreach (bool v in visited) { + if (!v) return false; + } + + return true; + } + + private int GCD(int a, int b) { + while (b != 0) { + int temp = b; + b = a % b; + a = temp; + } + return a; + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -189,7 +236,7 @@ class Solution: uf = UnionFind(len(nums)) factor_index = {} # f -> index of value with factor f - for i, num in enumerate(nums): + for i, n in enumerate(nums): f = 2 while f * f <= n: if n % f == 0: @@ -206,7 +253,7 @@ class Solution: else: factor_index[n] = i - return uf.isConnected() + return uf.isConnected() ``` ```java @@ -452,6 +499,87 @@ class Solution { } ``` +```csharp +public class Solution { + public class UnionFind { + public int Count; + private int[] Parent; + private int[] Size; + + public UnionFind(int n) { + Count = n; + Parent = new int[n]; + Size = new int[n]; + for (int i = 0; i < n; i++) { + Parent[i] = i; + Size[i] = 1; + } + } + + public int Find(int x) { + if (Parent[x] != x) { + Parent[x] = Find(Parent[x]); + } + return Parent[x]; + } + + public bool Union(int x, int y) { + int px = Find(x); + int py = Find(y); + if (px == py) return false; + Count--; + if (Size[px] < Size[py]) { + int temp = px; + px = py; + py = temp; + } + Size[px] += Size[py]; + Parent[py] = px; + return true; + } + + public bool IsConnected() { + return Count == 1; + } + } + + public bool CanTraverseAllPairs(int[] nums) { + int n = nums.Length; + if (n == 1) return true; + if (Array.Exists(nums, x => x == 1)) return false; + + UnionFind uf = new UnionFind(n); + Dictionary factorIndex = new Dictionary(); + + for (int i = 0; i < n; i++) { + int num = nums[i]; + int original = num; + for (int f = 2; f * f <= num; f++) { + if (num % f == 0) { + if (factorIndex.ContainsKey(f)) { + uf.Union(i, factorIndex[f]); + } else { + factorIndex[f] = i; + } + while (num % f == 0) { + num /= f; + } + } + } + if (num > 1) { + if (factorIndex.ContainsKey(num)) { + uf.Union(i, factorIndex[num]); + } else { + factorIndex[num] = i; + } + } + } + + return uf.IsConnected(); + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -790,6 +918,86 @@ class Solution { } ``` +```csharp +public class UnionFind { + public int[] Parent; + public int[] Size; + + public UnionFind(int n) { + Parent = new int[n]; + Size = new int[n]; + for (int i = 0; i < n; i++) { + Parent[i] = i; + Size[i] = 1; + } + } + + public int Find(int x) { + if (Parent[x] != x) { + Parent[x] = Find(Parent[x]); + } + return Parent[x]; + } + + public bool Union(int x, int y) { + int px = Find(x); + int py = Find(y); + if (px == py) return false; + if (Size[px] < Size[py]) { + int temp = px; + px = py; + py = temp; + } + Parent[py] = px; + Size[px] += Size[py]; + return true; + } +} + +public class Solution { + public bool CanTraverseAllPairs(int[] nums) { + int n = nums.Length; + if (n == 1) return true; + if (Array.Exists(nums, x => x == 1)) return false; + + int maxVal = nums.Max(); + int[] sieve = new int[maxVal + 1]; + for (int i = 2; i * i <= maxVal; i++) { + if (sieve[i] == 0) { + for (int j = i * i; j <= maxVal; j += i) { + if (sieve[j] == 0) sieve[j] = i; + } + } + } + + UnionFind uf = new UnionFind(n + maxVal + 1); + + for (int i = 0; i < n; i++) { + int num = nums[i]; + if (sieve[num] == 0) { + uf.Union(i, n + num); + continue; + } + + while (num > 1) { + int prime = sieve[num] != 0 ? sieve[num] : num; + uf.Union(i, n + prime); + while (num % prime == 0) { + num /= prime; + } + } + } + + int root = uf.Find(0); + for (int i = 1; i < n; i++) { + if (uf.Find(i) != root) return false; + } + + return true; + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -1029,6 +1237,70 @@ class Solution { } ``` +```csharp +public class Solution { + public bool CanTraverseAllPairs(int[] nums) { + int N = nums.Length; + if (N == 1) return true; + if (Array.Exists(nums, num => num == 1)) return false; + + int MAX = nums.Max(); + int[] sieve = new int[MAX + 1]; + for (int p = 2; p * p <= MAX; p++) { + if (sieve[p] == 0) { + for (int mult = p * p; mult <= MAX; mult += p) { + if (sieve[mult] == 0) { + sieve[mult] = p; + } + } + } + } + + Dictionary> adj = new Dictionary>(); + for (int i = 0; i < N; i++) { + int num = nums[i]; + if (sieve[num] == 0) { + AddEdge(adj, i, N + num); + continue; + } + while (num > 1) { + int prime = sieve[num] != 0 ? sieve[num] : num; + AddEdge(adj, i, N + prime); + while (num % prime == 0) { + num /= prime; + } + } + } + + HashSet visited = new HashSet(); + DFS(0, adj, visited); + + for (int i = 0; i < N; i++) { + if (!visited.Contains(i)) return false; + } + + return true; + } + + private void AddEdge(Dictionary> adj, int u, int v) { + if (!adj.ContainsKey(u)) adj[u] = new List(); + if (!adj.ContainsKey(v)) adj[v] = new List(); + adj[u].Add(v); + adj[v].Add(u); + } + + private void DFS(int node, Dictionary> adj, HashSet visited) { + visited.Add(node); + if (!adj.ContainsKey(node)) return; + foreach (int nei in adj[node]) { + if (!visited.Contains(nei)) { + DFS(nei, adj, visited); + } + } + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -1295,6 +1567,74 @@ class Solution { } ``` +```csharp +public class Solution { + public bool CanTraverseAllPairs(int[] nums) { + int N = nums.Length; + if (N == 1) return true; + if (nums.Contains(1)) return false; + + int MAX = nums.Max(); + int[] sieve = new int[MAX + 1]; + for (int p = 2; p * p <= MAX; p++) { + if (sieve[p] == 0) { + for (int composite = p * p; composite <= MAX; composite += p) { + if (sieve[composite] == 0) { + sieve[composite] = p; + } + } + } + } + + Dictionary> adj = new Dictionary>(); + for (int i = 0; i < N; i++) { + int num = nums[i]; + if (sieve[num] == 0) { + AddEdge(adj, i, N + num); + continue; + } + + while (num > 1) { + int prime = sieve[num] != 0 ? sieve[num] : num; + AddEdge(adj, i, N + prime); + while (num % prime == 0) { + num /= prime; + } + } + } + + HashSet visited = new HashSet(); + Queue queue = new Queue(); + queue.Enqueue(0); + visited.Add(0); + + while (queue.Count > 0) { + int node = queue.Dequeue(); + if (!adj.ContainsKey(node)) continue; + foreach (int nei in adj[node]) { + if (!visited.Contains(nei)) { + visited.Add(nei); + queue.Enqueue(nei); + } + } + } + + for (int i = 0; i < N; i++) { + if (!visited.Contains(i)) return false; + } + + return true; + } + + private void AddEdge(Dictionary> adj, int u, int v) { + if (!adj.ContainsKey(u)) adj[u] = new List(); + if (!adj.ContainsKey(v)) adj[v] = new List(); + adj[u].Add(v); + adj[v].Add(u); + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/guess-number-higher-or-lower.md b/articles/guess-number-higher-or-lower.md index b6053e2e8..4946e4968 100644 --- a/articles/guess-number-higher-or-lower.md +++ b/articles/guess-number-higher-or-lower.md @@ -65,7 +65,7 @@ public: * @return -1 if num is higher than the picked number * 1 if num is lower than the picked number * otherwise return 0 - * var guess = function(num) {} + * function guess(num) {} */ class Solution { @@ -82,6 +82,26 @@ class Solution { } ``` +```csharp +/** + * Forward declaration of guess API. + * @param num your guess + * @return -1 if num is higher than the picked number + * 1 if num is lower than the picked number + * otherwise return 0 + * int guess(int num); + */ + +public class Solution : GuessGame { + public int GuessNumber(int n) { + for (int num = 1; num <= n; num++) { + if (guess(num) == 0) return num; + } + return n; + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -181,7 +201,7 @@ public: * @return -1 if num is higher than the picked number * 1 if num is lower than the picked number * otherwise return 0 - * var guess = function(num) {} + * function guess(num) {} */ class Solution { @@ -206,6 +226,26 @@ class Solution { } ``` +```csharp +/** + * Forward declaration of guess API. + * @param num your guess + * @return -1 if num is higher than the picked number + * 1 if num is lower than the picked number + * otherwise return 0 + * int guess(int num); + */ + +public class Solution : GuessGame { + public int GuessNumber(int n) { + for (int num = 1; num <= n; num++) { + if (guess(num) == 0) return num; + } + return n; + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -316,7 +356,7 @@ public: * @return -1 if num is higher than the picked number * 1 if num is lower than the picked number * otherwise return 0 - * var guess = function(num) {} + * function guess(num) {} */ class Solution { @@ -344,6 +384,41 @@ class Solution { } ``` +```csharp +/** + * Forward declaration of guess API. + * @param num your guess + * @return -1 if num is higher than the picked number + * 1 if num is lower than the picked number + * otherwise return 0 + * int guess(int num); + */ + +public class Solution : GuessGame { + public int GuessNumber(int n) { + int l = 1, r = n; + while (true) { + int m1 = l + (r - l) / 3; + int m2 = r - (r - l) / 3; + + if (guess(m1) == 0) return m1; + if (guess(m2) == 0) return m2; + + if (guess(m1) + guess(m2) == 0) { + l = m1 + 1; + r = m2 - 1; + } + else if (guess(m1) == -1) { + r = m1 - 1; + } + else { + l = m2 + 1; + } + } + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/house-robber-iii.md b/articles/house-robber-iii.md index 08d521c95..a09238c6b 100644 --- a/articles/house-robber-iii.md +++ b/articles/house-robber-iii.md @@ -128,6 +128,38 @@ class Solution { } ``` +```csharp +/** + * Definition for a binary tree node. + * public class TreeNode { + * public int val; + * public TreeNode left; + * public TreeNode right; + * public TreeNode(int val=0, TreeNode left=null, TreeNode right=null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public int Rob(TreeNode root) { + if (root == null) return 0; + + int res = root.val; + if (root.left != null) { + res += Rob(root.left.left) + Rob(root.left.right); + } + if (root.right != null) { + res += Rob(root.right.left) + Rob(root.right.right); + } + + res = Math.Max(res, Rob(root.left) + Rob(root.right)); + return res; + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -299,6 +331,46 @@ class Solution { } ``` +```csharp +/** + * Definition for a binary tree node. + * public class TreeNode { + * public int val; + * public TreeNode left; + * public TreeNode right; + * public TreeNode(int val=0, TreeNode left=null, TreeNode right=null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + private Dictionary cache = new(); + + public int Rob(TreeNode root) { + return Dfs(root); + } + + private int Dfs(TreeNode root) { + if (root == null) return 0; + if (cache.ContainsKey(root)) return cache[root]; + + int res = root.val; + if (root.left != null) { + res += Dfs(root.left.left) + Dfs(root.left.right); + } + if (root.right != null) { + res += Dfs(root.right.left) + Dfs(root.right.right); + } + + res = Math.Max(res, Dfs(root.left) + Dfs(root.right)); + cache[root] = res; + return res; + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -449,6 +521,40 @@ class Solution { } ``` +```csharp +/** + * Definition for a binary tree node. + * public class TreeNode { + * public int val; + * public TreeNode left; + * public TreeNode right; + * public TreeNode(int val=0, TreeNode left=null, TreeNode right=null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public int Rob(TreeNode root) { + var result = Dfs(root); + return Math.Max(result.withRoot, result.withoutRoot); + } + + private (int withRoot, int withoutRoot) Dfs(TreeNode root) { + if (root == null) return (0, 0); + + var left = Dfs(root.left); + var right = Dfs(root.right); + + int withRoot = root.val + left.withoutRoot + right.withoutRoot; + int withoutRoot = Math.Max(left.withRoot, left.withoutRoot) + Math.Max(right.withRoot, right.withoutRoot); + + return (withRoot, withoutRoot); + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/insert-into-a-binary-search-tree.md b/articles/insert-into-a-binary-search-tree.md index d8597610b..fe0604c3a 100644 --- a/articles/insert-into-a-binary-search-tree.md +++ b/articles/insert-into-a-binary-search-tree.md @@ -118,6 +118,37 @@ class Solution { } ``` +```csharp +/** + * Definition for a binary tree node. + * public class TreeNode { + * public int val; + * public TreeNode left; + * public TreeNode right; + * public TreeNode(int val=0, TreeNode left=null, TreeNode right=null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public TreeNode InsertIntoBST(TreeNode root, int val) { + if (root == null) { + return new TreeNode(val); + } + + if (val > root.val) { + root.right = InsertIntoBST(root.right, val); + } else { + root.left = InsertIntoBST(root.left, val); + } + + return root; + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -282,6 +313,46 @@ class Solution { } ``` +```csharp +/** + * Definition for a binary tree node. + * public class TreeNode { + * public int val; + * public TreeNode left; + * public TreeNode right; + * public TreeNode(int val=0, TreeNode left=null, TreeNode right=null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public TreeNode InsertIntoBST(TreeNode root, int val) { + if (root == null) { + return new TreeNode(val); + } + + TreeNode cur = root; + while (true) { + if (val > cur.val) { + if (cur.right == null) { + cur.right = new TreeNode(val); + return root; + } + cur = cur.right; + } else { + if (cur.left == null) { + cur.left = new TreeNode(val); + return root; + } + cur = cur.left; + } + } + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/integer-break.md b/articles/integer-break.md index eadd8856d..2330d8828 100644 --- a/articles/integer-break.md +++ b/articles/integer-break.md @@ -79,6 +79,28 @@ class Solution { } ``` +```csharp +public class Solution { + private int n; + + public int IntegerBreak(int n) { + this.n = n; + + int Dfs(int num) { + if (num == 1) return 1; + int res = num == n ? 0 : num; + for (int i = 1; i < num; i++) { + int val = Dfs(i) * Dfs(num - i); + res = Math.Max(res, val); + } + return res; + } + + return Dfs(n); + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -173,6 +195,26 @@ class Solution { } ``` +```csharp +public class Solution { + public int IntegerBreak(int n) { + int Dfs(int num, int i) { + if (Math.Min(num, i) == 0) { + return 1; + } + + if (i > num) { + return Dfs(num, num); + } + + return Math.Max(i * Dfs(num - i, i), Dfs(num, i - 1)); + } + + return Dfs(n, n - 1); + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -289,6 +331,31 @@ class Solution { } ``` +```csharp +public class Solution { + public int IntegerBreak(int n) { + Dictionary dp = new Dictionary(); + dp[1] = 1; + + int Dfs(int num) { + if (dp.ContainsKey(num)) { + return dp[num]; + } + + dp[num] = (num == n) ? 0 : num; + for (int i = 1; i < num; i++) { + int val = Dfs(i) * Dfs(num - i); + dp[num] = Math.Max(dp[num], val); + } + + return dp[num]; + } + + return Dfs(n); + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -409,6 +476,30 @@ class Solution { } ``` +```csharp +public class Solution { + public int IntegerBreak(int n) { + Dictionary<(int, int), int> dp = new Dictionary<(int, int), int>(); + + int Dfs(int num, int i) { + if (Math.Min(num, i) == 0) return 1; + + if (dp.ContainsKey((num, i))) return dp[(num, i)]; + + if (i > num) { + dp[(num, i)] = Dfs(num, num); + return dp[(num, i)]; + } + + dp[(num, i)] = Math.Max(i * Dfs(num - i, i), Dfs(num, i - 1)); + return dp[(num, i)]; + } + + return Dfs(n, n - 1); + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -495,6 +586,24 @@ class Solution { } ``` +```csharp +public class Solution { + public int IntegerBreak(int n) { + int[] dp = new int[n + 1]; + dp[1] = 1; + + for (int num = 2; num <= n; num++) { + dp[num] = num == n ? 0 : num; + for (int i = 1; i < num; i++) { + dp[num] = Math.Max(dp[num], dp[i] * dp[num - i]); + } + } + + return dp[n]; + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -571,6 +680,24 @@ class Solution { } ``` +```csharp +public class Solution { + public int IntegerBreak(int n) { + if (n <= 3) { + return n - 1; + } + + int res = 1; + while (n > 4) { + res *= 3; + n -= 3; + } + + return res * n; + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -653,6 +780,24 @@ class Solution { } ``` +```csharp +public class Solution { + public int IntegerBreak(int n) { + if (n <= 3) { + return n - 1; + } + + int res = (int)Math.Pow(3, n / 3); + + if (n % 3 == 1) { + return (res / 3) * 4; + } + + return res * Math.Max(1, n % 3); + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/ipo.md b/articles/ipo.md index e71ba4c34..87670000f 100644 --- a/articles/ipo.md +++ b/articles/ipo.md @@ -85,8 +85,8 @@ class Solution { * @return {number} */ findMaximizedCapital(k, w, profits, capital) { - const minCapital = new MinPriorityQueue({ compare: (a, b) => a[0] - b[0] }); // Min heap - const maxProfit = new MaxPriorityQueue({ compare: (a, b) => b - a }); // Max heap + const minCapital = new PriorityQueue((a, b) => a[0] - b[0]); // Min heap + const maxProfit = new PriorityQueue((a, b) => b - a); // Max heap for (let i = 0; i < capital.length; i++) { minCapital.enqueue([capital[i], profits[i]]); @@ -107,6 +107,39 @@ class Solution { } ``` +```csharp +public class Solution { + public int FindMaximizedCapital(int k, int w, int[] profits, int[] capital) { + var minCapital = new List<(int c, int p)>(); + for (int i = 0; i < capital.Length; i++) { + minCapital.Add((capital[i], profits[i])); + } + + // Min-heap by capital + minCapital.Sort((a, b) => a.c.CompareTo(b.c)); + + // Max-heap by profit + var maxProfit = new PriorityQueue(Comparer.Create((a, b) => b.CompareTo(a))); + int iPtr = 0; + + for (int i = 0; i < k; i++) { + while (iPtr < minCapital.Count && minCapital[iPtr].c <= w) { + maxProfit.Enqueue(minCapital[iPtr].p, minCapital[iPtr].p); + iPtr++; + } + + if (maxProfit.Count == 0) { + break; + } + + w += maxProfit.Dequeue(); + } + + return w; + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -215,12 +248,12 @@ class Solution { * @return {number} */ findMaximizedCapital(k, w, profits, capital) { - const minCapital = new MinPriorityQueue({ - compare: (a, b) => capital[a] - capital[b], - }); - const maxProfit = new MaxPriorityQueue({ - compare: (a, b) => profits[b] - profits[a], - }); + const minCapital = new PriorityQueue( + (a, b) => capital[a] - capital[b], + ); + const maxProfit = new PriorityQueue( + (a, b) => profits[b] - profits[a], + ); for (let i = 0; i < capital.length; i++) { minCapital.enqueue(i); @@ -241,6 +274,34 @@ class Solution { } ``` +```csharp +public class Solution { + public int FindMaximizedCapital(int k, int w, int[] profits, int[] capital) { + var minCapital = new PriorityQueue(); // index with capital as priority + var maxProfit = new PriorityQueue(Comparer.Create((a, b) => b.CompareTo(a))); // max heap by profit + + for (int i = 0; i < capital.Length; i++) { + minCapital.Enqueue(i, capital[i]); + } + + for (int i = 0; i < k; i++) { + while (minCapital.Count > 0 && capital[minCapital.Peek()] <= w) { + int idx = minCapital.Dequeue(); + maxProfit.Enqueue(profits[idx], profits[idx]); + } + + if (maxProfit.Count == 0) { + break; + } + + w += maxProfit.Dequeue(); + } + + return w; + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -358,7 +419,7 @@ class Solution { if (maxProfit.isEmpty()) { break; } - w += maxProfit.dequeue().element; + w += maxProfit.dequeue(); } return w; @@ -366,6 +427,38 @@ class Solution { } ``` +```csharp +public class Solution { + public int FindMaximizedCapital(int k, int w, int[] profits, int[] capital) { + int n = profits.Length; + int[] indices = new int[n]; + for (int i = 0; i < n; i++) { + indices[i] = i; + } + + Array.Sort(indices, (a, b) => capital[a].CompareTo(capital[b])); + + var maxProfit = new PriorityQueue(Comparer.Create((a, b) => b.CompareTo(a))); + int idx = 0; + + for (int i = 0; i < k; i++) { + while (idx < n && capital[indices[idx]] <= w) { + maxProfit.Enqueue(profits[indices[idx]], profits[indices[idx]]); + idx++; + } + + if (maxProfit.Count == 0) { + break; + } + + w += maxProfit.Dequeue(); + } + + return w; + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/island-perimeter.md b/articles/island-perimeter.md index c4bb78ca5..bb87939fe 100644 --- a/articles/island-perimeter.md +++ b/articles/island-perimeter.md @@ -138,6 +138,42 @@ class Solution { } ``` +```csharp +public class Solution { + private int rows, cols; + private HashSet<(int, int)> visit; + + public int IslandPerimeter(int[][] grid) { + rows = grid.Length; + cols = grid[0].Length; + visit = new HashSet<(int, int)>(); + + int Dfs(int i, int j) { + if (i < 0 || j < 0 || i >= rows || j >= cols || grid[i][j] == 0) { + return 1; + } + if (visit.Contains((i, j))) { + return 0; + } + + visit.Add((i, j)); + int perim = Dfs(i, j + 1) + Dfs(i + 1, j) + Dfs(i, j - 1) + Dfs(i - 1, j); + return perim; + } + + for (int i = 0; i < rows; i++) { + for (int j = 0; j < cols; j++) { + if (grid[i][j] == 1) { + return Dfs(i, j); + } + } + } + + return 0; + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -309,6 +345,56 @@ class Solution { } ``` +```csharp +public class Solution { + public int IslandPerimeter(int[][] grid) { + int rows = grid.Length; + int cols = grid[0].Length; + var visited = new HashSet<(int, int)>(); + int[][] directions = new int[][] { + new int[] { 0, 1 }, + new int[] { 1, 0 }, + new int[] { 0, -1 }, + new int[] { -1, 0 } + }; + + int Bfs(int r, int c) { + var queue = new Queue<(int, int)>(); + queue.Enqueue((r, c)); + visited.Add((r, c)); + int perimeter = 0; + + while (queue.Count > 0) { + var (x, y) = queue.Dequeue(); + foreach (var dir in directions) { + int nx = x + dir[0]; + int ny = y + dir[1]; + + if (nx < 0 || ny < 0 || nx >= rows || ny >= cols || grid[nx][ny] == 0) { + perimeter++; + } else if (!visited.Contains((nx, ny))) { + visited.Add((nx, ny)); + queue.Enqueue((nx, ny)); + } + } + } + + return perimeter; + } + + for (int i = 0; i < rows; i++) { + for (int j = 0; j < cols; j++) { + if (grid[i][j] == 1) { + return Bfs(i, j); + } + } + } + + return 0; + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -401,6 +487,29 @@ class Solution { } ``` +```csharp +public class Solution { + public int IslandPerimeter(int[][] grid) { + int m = grid.Length; + int n = grid[0].Length; + int res = 0; + + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + if (grid[i][j] == 1) { + if (i + 1 >= m || grid[i + 1][j] == 0) res++; + if (j + 1 >= n || grid[i][j + 1] == 0) res++; + if (i - 1 < 0 || grid[i - 1][j] == 0) res++; + if (j - 1 < 0 || grid[i][j - 1] == 0) res++; + } + } + } + + return res; + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -506,6 +615,32 @@ class Solution { } ``` +```csharp +public class Solution { + public int IslandPerimeter(int[][] grid) { + int m = grid.Length; + int n = grid[0].Length; + int res = 0; + + for (int r = 0; r < m; r++) { + for (int c = 0; c < n; c++) { + if (grid[r][c] == 1) { + res += 4; + if (r > 0 && grid[r - 1][c] == 1) { + res -= 2; + } + if (c > 0 && grid[r][c - 1] == 1) { + res -= 2; + } + } + } + } + + return res; + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/longest-happy-string.md b/articles/longest-happy-string.md index e5f2e0573..3b669e0e4 100644 --- a/articles/longest-happy-string.md +++ b/articles/longest-happy-string.md @@ -166,6 +166,45 @@ class Solution { } ``` +```csharp +public class Solution { + public string LongestDiverseString(int a, int b, int c) { + int[] count = new int[] { a, b, c }; + List res = new List(); + + int GetMax(int repeated) { + int idx = -1; + int maxCnt = 0; + for (int i = 0; i < 3; i++) { + if (i == repeated || count[i] == 0) continue; + if (maxCnt < count[i]) { + maxCnt = count[i]; + idx = i; + } + } + return idx; + } + + int repeated = -1; + while (true) { + int maxChar = GetMax(repeated); + if (maxChar == -1) break; + + res.Add((char)(maxChar + 'a')); + count[maxChar]--; + + if (res.Count > 1 && res[res.Count - 1] == res[res.Count - 2]) { + repeated = maxChar; + } else { + repeated = -1; + } + } + + return new string(res.ToArray()); + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -283,18 +322,18 @@ class Solution { */ longestDiverseString(a, b, c) { const res = []; - const maxHeap = new MaxPriorityQueue({ priority: (x) => x[0] }); + const maxHeap = new MaxPriorityQueue(x => x[0]); if (a > 0) maxHeap.enqueue([a, 'a']); if (b > 0) maxHeap.enqueue([b, 'b']); if (c > 0) maxHeap.enqueue([c, 'c']); while (!maxHeap.isEmpty()) { - const [count, char] = maxHeap.dequeue().element; + const [count, char] = maxHeap.dequeue(); if (res.length > 1 && res[res.length - 1] === char && res[res.length - 2] === char) { if (maxHeap.isEmpty()) break; - const [count2, char2] = maxHeap.dequeue().element; + const [count2, char2] = maxHeap.dequeue(); res.push(char2); if (count2 - 1 > 0) maxHeap.enqueue([count2 - 1, char2]); maxHeap.enqueue([count, char]); @@ -309,6 +348,47 @@ class Solution { } ``` +```csharp +public class Solution { + public string LongestDiverseString(int a, int b, int c) { + string res = ""; + PriorityQueue<(int count, char ch), int> maxHeap = new PriorityQueue<(int, char), int>(); + + void AddToHeap(int count, char ch) { + if (count > 0) { + maxHeap.Enqueue((count, ch), -count); + } + } + + AddToHeap(a, 'a'); + AddToHeap(b, 'b'); + AddToHeap(c, 'c'); + + while (maxHeap.Count > 0) { + var (count1, ch1) = maxHeap.Dequeue(); + if (res.Length >= 2 && res[^1] == ch1 && res[^2] == ch1) { + if (maxHeap.Count == 0) break; + var (count2, ch2) = maxHeap.Dequeue(); + res += ch2; + count2--; + if (count2 > 0) { + maxHeap.Enqueue((count2, ch2), -count2); + } + maxHeap.Enqueue((count1, ch1), -count1); + } else { + res += ch1; + count1--; + if (count1 > 0) { + maxHeap.Enqueue((count1, ch1), -count1); + } + } + } + + return res; + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -448,6 +528,35 @@ class Solution { } ``` +```csharp +public class Solution { + public string LongestDiverseString(int a, int b, int c) { + return string.Join("", Rec(a, b, c, 'a', 'b', 'c')); + } + + private List Rec(int max1, int max2, int max3, char char1, char char2, char char3) { + if (max1 < max2) return Rec(max2, max1, max3, char2, char1, char3); + if (max2 < max3) return Rec(max1, max3, max2, char1, char3, char2); + if (max2 == 0) { + int use = Math.Min(2, max1); + var res = new List(); + for (int i = 0; i < use; i++) res.Add(char1); + return res; + } + + int use1 = Math.Min(2, max1); + int use2 = (max1 - use1 >= max2) ? 1 : 0; + + var result = new List(); + for (int i = 0; i < use1; i++) result.Add(char1); + for (int i = 0; i < use2; i++) result.Add(char2); + + result.AddRange(Rec(max1 - use1, max2 - use2, max3, char1, char2, char3)); + return result; + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/matchsticks-to-square.md b/articles/matchsticks-to-square.md index c05819570..ae760c5ac 100644 --- a/articles/matchsticks-to-square.md +++ b/articles/matchsticks-to-square.md @@ -109,6 +109,39 @@ class Solution { } ``` +```csharp +public class Solution { + public bool Makesquare(int[] matchsticks) { + int total = 0; + foreach (int stick in matchsticks) { + total += stick; + } + if (total % 4 != 0) return false; + + int target = total / 4; + int[] sides = new int[4]; + + bool Dfs(int i) { + if (i == matchsticks.Length) { + return sides[0] == sides[1] && sides[1] == sides[2] && sides[2] == sides[3]; + } + + for (int side = 0; side < 4; side++) { + sides[side] += matchsticks[i]; + if (sides[side] <= target && Dfs(i + 1)) { + return true; + } + sides[side] -= matchsticks[i]; + } + + return false; + } + + return Dfs(0); + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -266,6 +299,45 @@ class Solution { } ``` +```csharp +public class Solution { + public bool Makesquare(int[] matchsticks) { + int totalLength = 0; + foreach (int stick in matchsticks) { + totalLength += stick; + } + + if (totalLength % 4 != 0) { + return false; + } + + int length = totalLength / 4; + int[] sides = new int[4]; + Array.Sort(matchsticks, (a, b) => b.CompareTo(a)); // Sort in descending order + + bool Dfs(int i) { + if (i == matchsticks.Length) { + return true; + } + + for (int side = 0; side < 4; side++) { + if (sides[side] + matchsticks[i] <= length) { + sides[side] += matchsticks[i]; + if (Dfs(i + 1)) return true; + sides[side] -= matchsticks[i]; + } + + if (sides[side] == 0) break; + } + + return false; + } + + return Dfs(0); + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -466,6 +538,46 @@ class Solution { } ``` +```csharp +public class Solution { + public bool Makesquare(int[] matchsticks) { + int totalLength = matchsticks.Sum(); + if (totalLength % 4 != 0) return false; + + int length = totalLength / 4; + if (matchsticks.Max() > length) return false; + + int n = matchsticks.Length; + int[] dp = Enumerable.Repeat(int.MinValue, 1 << n).ToArray(); + Array.Sort(matchsticks, (a, b) => b.CompareTo(a)); // Sort descending + + int Dfs(int mask) { + if (mask == 0) return 0; + if (dp[mask] != int.MinValue) return dp[mask]; + + for (int i = 0; i < n; i++) { + if ((mask & (1 << i)) != 0) { + int res = Dfs(mask ^ (1 << i)); + if (res >= 0 && res + matchsticks[i] <= length) { + dp[mask] = (res + matchsticks[i]) % length; + return dp[mask]; + } + if (mask == (1 << n) - 1) { + dp[mask] = -1; + return -1; + } + } + } + + dp[mask] = -1; + return -1; + } + + return Dfs((1 << n) - 1) != -1; + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/minimum-size-subarray-sum.md b/articles/minimum-size-subarray-sum.md index 74be08d78..683582184 100644 --- a/articles/minimum-size-subarray-sum.md +++ b/articles/minimum-size-subarray-sum.md @@ -94,6 +94,28 @@ class Solution { } ``` +```csharp +public class Solution { + public int MinSubArrayLen(int target, int[] nums) { + int n = nums.Length; + int res = int.MaxValue; + + for (int i = 0; i < n; i++) { + int curSum = 0; + for (int j = i; j < n; j++) { + curSum += nums[j]; + if (curSum >= target) { + res = Math.Min(res, j - i + 1); + break; + } + } + } + + return res == int.MaxValue ? 0 : res; + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -188,6 +210,27 @@ class Solution { } ``` +```csharp +public class Solution { + public int MinSubArrayLen(int target, int[] nums) { + int l = 0, total = 0; + int res = int.MaxValue; + + for (int r = 0; r < nums.Length; r++) { + total += nums[r]; + + while (total >= target) { + res = Math.Min(res, r - l + 1); + total -= nums[l]; + l++; + } + } + + return res == int.MaxValue ? 0 : res; + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -324,6 +367,39 @@ class Solution { } ``` +```csharp +public class Solution { + public int MinSubArrayLen(int target, int[] nums) { + int n = nums.Length; + int[] prefixSum = new int[n + 1]; + + for (int i = 0; i < n; i++) { + prefixSum[i + 1] = prefixSum[i] + nums[i]; + } + + int res = n + 1; + + for (int i = 0; i < n; i++) { + int l = i, r = n; + while (l < r) { + int mid = (l + r) / 2; + int curSum = prefixSum[mid + 1] - prefixSum[i]; + if (curSum >= target) { + r = mid; + } else { + l = mid + 1; + } + } + if (l != n) { + res = Math.Min(res, l - i + 1); + } + } + + return res % (n + 1); + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/n-queens-ii.md b/articles/n-queens-ii.md index 1e123c887..16a94bd75 100644 --- a/articles/n-queens-ii.md +++ b/articles/n-queens-ii.md @@ -178,6 +178,57 @@ class Solution { } ``` +```csharp +public class Solution { + private int res = 0; + + public int TotalNQueens(int n) { + res = 0; + char[][] board = new char[n][]; + for (int i = 0; i < n; i++) { + board[i] = Enumerable.Repeat('.', n).ToArray(); + } + + Backtrack(0, board, n); + return res; + } + + private void Backtrack(int r, char[][] board, int n) { + if (r == n) { + res++; + return; + } + + for (int c = 0; c < n; c++) { + if (IsSafe(r, c, board)) { + board[r][c] = 'Q'; + Backtrack(r + 1, board, n); + board[r][c] = '.'; + } + } + } + + private bool IsSafe(int r, int c, char[][] board) { + // Check column + for (int row = r - 1; row >= 0; row--) { + if (board[row][c] == 'Q') return false; + } + + // Check top-left diagonal + for (int row = r - 1, col = c - 1; row >= 0 && col >= 0; row--, col--) { + if (board[row][col] == 'Q') return false; + } + + // Check top-right diagonal + for (int row = r - 1, col = c + 1; row >= 0 && col < board.Length; row--, col++) { + if (board[row][col] == 'Q') return false; + } + + return true; + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -346,6 +397,43 @@ class Solution { } ``` +```csharp +public class Solution { + private HashSet col = new HashSet(); + private HashSet posDiag = new HashSet(); // r + c + private HashSet negDiag = new HashSet(); // r - c + private int res = 0; + + public int TotalNQueens(int n) { + res = 0; + Backtrack(0, n); + return res; + } + + private void Backtrack(int r, int n) { + if (r == n) { + res++; + return; + } + + for (int c = 0; c < n; c++) { + if (col.Contains(c) || posDiag.Contains(r + c) || negDiag.Contains(r - c)) + continue; + + col.Add(c); + posDiag.Add(r + c); + negDiag.Add(r - c); + + Backtrack(r + 1, n); + + col.Remove(c); + posDiag.Remove(r + c); + negDiag.Remove(r - c); + } + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -509,6 +597,42 @@ class Solution { } ``` +```csharp +public class Solution { + public int TotalNQueens(int n) { + bool[] col = new bool[n]; + bool[] posDiag = new bool[2 * n]; + bool[] negDiag = new bool[2 * n]; + int res = 0; + + void Backtrack(int r) { + if (r == n) { + res++; + return; + } + + for (int c = 0; c < n; c++) { + if (col[c] || posDiag[r + c] || negDiag[r - c + n]) + continue; + + col[c] = true; + posDiag[r + c] = true; + negDiag[r - c + n] = true; + + Backtrack(r + 1); + + col[c] = false; + posDiag[r + c] = false; + negDiag[r - c + n] = false; + } + } + + Backtrack(0); + return res; + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -664,6 +788,44 @@ class Solution { } ``` +```csharp +public class Solution { + public int TotalNQueens(int n) { + int col = 0; + int posDiag = 0; + int negDiag = 0; + int res = 0; + + void Backtrack(int r) { + if (r == n) { + res++; + return; + } + + for (int c = 0; c < n; c++) { + if (((col & (1 << c)) != 0) || + ((posDiag & (1 << (r + c))) != 0) || + ((negDiag & (1 << (r - c + n))) != 0)) + continue; + + col ^= (1 << c); + posDiag ^= (1 << (r + c)); + negDiag ^= (1 << (r - c + n)); + + Backtrack(r + 1); + + col ^= (1 << c); + posDiag ^= (1 << (r + c)); + negDiag ^= (1 << (r - c + n)); + } + } + + Backtrack(0); + return res; + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/n-th-tribonacci-number.md b/articles/n-th-tribonacci-number.md index bf04e98f7..56f6ce37a 100644 --- a/articles/n-th-tribonacci-number.md +++ b/articles/n-th-tribonacci-number.md @@ -48,6 +48,16 @@ class Solution { } ``` +```csharp +public class Solution { + public int Tribonacci(int n) { + if (n == 0) return 0; + if (n <= 2) return 1; + return Tribonacci(n - 1) + Tribonacci(n - 2) + Tribonacci(n - 3); + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -138,6 +148,21 @@ class Solution { } ``` +```csharp +public class Solution { + private Dictionary dp = new Dictionary(); + + public int Tribonacci(int n) { + if (n == 0) return 0; + if (n <= 2) return 1; + if (dp.ContainsKey(n)) return dp[n]; + + dp[n] = Tribonacci(n - 1) + Tribonacci(n - 2) + Tribonacci(n - 3); + return dp[n]; + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -220,6 +245,25 @@ class Solution { } ``` +```csharp +public class Solution { + public int Tribonacci(int n) { + if (n == 0) return 0; + if (n <= 2) return 1; + + int[] dp = new int[n + 1]; + dp[0] = 0; + dp[1] = dp[2] = 1; + + for (int i = 3; i <= n; i++) { + dp[i] = dp[i - 1] + dp[i - 2] + dp[i - 3]; + } + + return dp[n]; + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -293,6 +337,21 @@ class Solution { } ``` +```csharp +public class Solution { + public int Tribonacci(int n) { + int[] t = {0, 1, 1}; + if (n < 3) return t[n]; + + for (int i = 3; i <= n; i++) { + t[i % 3] = t[0] + t[1] + t[2]; + } + + return t[n % 3]; + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/open-the-lock.md b/articles/open-the-lock.md index 6ded4c7ee..910704fd6 100644 --- a/articles/open-the-lock.md +++ b/articles/open-the-lock.md @@ -159,6 +159,44 @@ class Solution { } ``` +```csharp +public class Solution { + public int OpenLock(string[] deadends, string target) { + var dead = new HashSet(deadends); + if (dead.Contains("0000")) return -1; + + List Children(string lockStr) { + var res = new List(); + for (int i = 0; i < 4; i++) { + int digit = lockStr[i] - '0'; + string up = lockStr.Substring(0, i) + ((digit + 1) % 10) + lockStr.Substring(i + 1); + string down = lockStr.Substring(0, i) + ((digit + 9) % 10) + lockStr.Substring(i + 1); + res.Add(up); + res.Add(down); + } + return res; + } + + var q = new Queue<(string, int)>(); + q.Enqueue(("0000", 0)); + var visited = new HashSet(dead); + + while (q.Count > 0) { + var (lockStr, turns) = q.Dequeue(); + if (lockStr == target) return turns; + foreach (var child in Children(lockStr)) { + if (!visited.Contains(child)) { + visited.Add(child); + q.Enqueue((child, turns + 1)); + } + } + } + + return -1; + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -313,6 +351,42 @@ class Solution { } ``` +```csharp +public class Solution { + public int OpenLock(string[] deadends, string target) { + if (target == "0000") return 0; + + var visited = new HashSet(deadends); + if (visited.Contains("0000")) return -1; + + var q = new Queue(); + q.Enqueue("0000"); + visited.Add("0000"); + int steps = 0; + + while (q.Count > 0) { + steps++; + int size = q.Count; + for (int i = 0; i < size; i++) { + string lockStr = q.Dequeue(); + for (int j = 0; j < 4; j++) { + foreach (int move in new int[] {1, -1}) { + int digit = (lockStr[j] - '0' + move + 10) % 10; + string nextLock = lockStr.Substring(0, j) + digit.ToString() + lockStr.Substring(j + 1); + if (visited.Contains(nextLock)) continue; + if (nextLock == target) return steps; + q.Enqueue(nextLock); + visited.Add(nextLock); + } + } + } + } + + return -1; + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -491,6 +565,51 @@ class Solution { } ``` +```csharp +public class Solution { + public int OpenLock(string[] deadends, string target) { + if (target == "0000") return 0; + + var visit = new HashSet(deadends); + if (visit.Contains("0000")) return -1; + + var begin = new HashSet { "0000" }; + var end = new HashSet { target }; + int steps = 0; + + while (begin.Count > 0 && end.Count > 0) { + if (begin.Count > end.Count) { + var tempSet = begin; + begin = end; + end = tempSet; + } + + var temp = new HashSet(); + steps++; + + foreach (var lockStr in begin) { + for (int i = 0; i < 4; i++) { + foreach (int j in new int[] { -1, 1 }) { + int digit = (lockStr[i] - '0' + j + 10) % 10; + string nextLock = lockStr.Substring(0, i) + digit.ToString() + lockStr.Substring(i + 1); + + if (end.Contains(nextLock)) return steps; + if (visit.Contains(nextLock)) continue; + + visit.Add(nextLock); + temp.Add(nextLock); + } + } + } + + begin = temp; + } + + return -1; + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/partition-to-k-equal-sum-subsets.md b/articles/partition-to-k-equal-sum-subsets.md index 260fe7e37..d217736df 100644 --- a/articles/partition-to-k-equal-sum-subsets.md +++ b/articles/partition-to-k-equal-sum-subsets.md @@ -132,6 +132,38 @@ class Solution { } ``` +```csharp +public class Solution { + public bool CanPartitionKSubsets(int[] nums, int k) { + int totalSum = nums.Sum(); + if (totalSum % k != 0) return false; + + int target = totalSum / k; + Array.Sort(nums); + Array.Reverse(nums); + + bool[] used = new bool[nums.Length]; + + bool Backtrack(int i, int kRemaining, int subsetSum) { + if (kRemaining == 0) return true; + if (subsetSum == target) return Backtrack(0, kRemaining - 1, 0); + + for (int j = i; j < nums.Length; j++) { + if (used[j] || subsetSum + nums[j] > target) continue; + + used[j] = true; + if (Backtrack(j + 1, kRemaining, subsetSum + nums[j])) return true; + used[j] = false; + } + + return false; + } + + return Backtrack(0, k, 0); + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -291,6 +323,39 @@ class Solution { } ``` +```csharp +public class Solution { + public bool CanPartitionKSubsets(int[] nums, int k) { + int total = nums.Sum(); + if (total % k != 0) return false; + + Array.Sort(nums); + Array.Reverse(nums); + int target = total / k; + bool[] used = new bool[nums.Length]; + + bool Backtrack(int i, int kRemaining, int subsetSum) { + if (kRemaining == 0) return true; + if (subsetSum == target) return Backtrack(0, kRemaining - 1, 0); + + for (int j = i; j < nums.Length; j++) { + if (used[j] || subsetSum + nums[j] > target) continue; + + used[j] = true; + if (Backtrack(j + 1, kRemaining, subsetSum + nums[j])) return true; + used[j] = false; + + if (subsetSum == 0) return false; // Pruning + } + + return false; + } + + return Backtrack(0, k, 0); + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -442,6 +507,38 @@ class Solution { } ``` +```csharp +public class Solution { + public bool CanPartitionKSubsets(int[] nums, int k) { + int total = nums.Sum(); + if (total % k != 0) return false; + + Array.Sort(nums); + Array.Reverse(nums); + int target = total / k; + int n = nums.Length; + + bool Backtrack(int i, int kRemaining, int subsetSum, int mask) { + if (kRemaining == 0) return true; + if (subsetSum == target) return Backtrack(0, kRemaining - 1, 0, mask); + + for (int j = i; j < n; j++) { + if ((mask & (1 << j)) == 0 || subsetSum + nums[j] > target) continue; + + if (Backtrack(j + 1, kRemaining, subsetSum + nums[j], mask ^ (1 << j))) + return true; + + if (subsetSum == 0) return false; + } + + return false; + } + + return Backtrack(0, k, 0, (1 << n) - 1); + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -641,6 +738,53 @@ class Solution { } ``` +```csharp +public class Solution { + public bool CanPartitionKSubsets(int[] nums, int k) { + int total = nums.Sum(); + if (total % k != 0) return false; + + Array.Sort(nums); + Array.Reverse(nums); + + int target = total / k; + int n = nums.Length; + bool?[] dp = new bool?[1 << n]; + + bool Backtrack(int i, int kRemaining, int subsetSum, int mask) { + if (dp[mask].HasValue) return (bool)dp[mask]; + if (kRemaining == 0) { + dp[mask] = true; + return true; + } + if (subsetSum == target) { + dp[mask] = Backtrack(0, kRemaining - 1, 0, mask); + return (bool)dp[mask]; + } + + for (int j = i; j < n; j++) { + if ((mask & (1 << j)) == 0 || subsetSum + nums[j] > target) continue; + + if (Backtrack(j + 1, kRemaining, subsetSum + nums[j], mask ^ (1 << j))) { + dp[mask] = true; + return true; + } + + if (subsetSum == 0) { + dp[mask] = false; + return false; + } + } + + dp[mask] = false; + return false; + } + + return Backtrack(0, k, 0, (1 << n) - 1); + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -764,6 +908,35 @@ class Solution { } ``` +```csharp +public class Solution { + public bool CanPartitionKSubsets(int[] nums, int k) { + int total = nums.Sum(); + if (total % k != 0) return false; + + int target = total / k; + int n = nums.Length; + int N = 1 << n; + int[] dp = new int[N]; + for (int i = 1; i < N; i++) { + dp[i] = -1; + } + + for (int mask = 0; mask < N; mask++) { + if (dp[mask] == -1) continue; + for (int i = 0; i < n; i++) { + if ((mask & (1 << i)) == 0 && dp[mask] + nums[i] <= target) { + int nextMask = mask | (1 << i); + dp[nextMask] = (dp[mask] + nums[i]) % target; + } + } + } + + return dp[N - 1] == 0; + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/path-with-minimum-effort.md b/articles/path-with-minimum-effort.md index bccc0e7b9..5f151c2c8 100644 --- a/articles/path-with-minimum-effort.md +++ b/articles/path-with-minimum-effort.md @@ -132,7 +132,7 @@ class Solution { ); dist[0][0] = 0; - const minHeap = new MinPriorityQueue({ priority: (a) => a[0] }); + const minHeap = new MinPriorityQueue(a => a[0]); minHeap.enqueue([0, 0, 0]); // [diff, row, col] const directions = [ @@ -141,7 +141,7 @@ class Solution { ]; while (!minHeap.isEmpty()) { - const [diff, r, c] = minHeap.dequeue().element; + const [diff, r, c] = minHeap.dequeue(); if (r === rows - 1 && c === cols - 1) return diff; if (dist[r][c] < diff) continue; @@ -169,6 +169,50 @@ class Solution { } ``` +```csharp +public class Solution { + public int MinimumEffortPath(int[][] heights) { + int rows = heights.Length, cols = heights[0].Length; + var directions = new int[][] { + new int[] { 0, 1 }, + new int[] { 0, -1 }, + new int[] { 1, 0 }, + new int[] { -1, 0 } + }; + + var minHeap = new PriorityQueue<(int diff, int r, int c), int>(); + var visited = new HashSet<(int, int)>(); + minHeap.Enqueue((0, 0, 0), 0); + + while (minHeap.Count > 0) { + var current = minHeap.Dequeue(); + int diff = current.diff, r = current.r, c = current.c; + + if (visited.Contains((r, c))) continue; + visited.Add((r, c)); + + if (r == rows - 1 && c == cols - 1) { + return diff; + } + + foreach (var dir in directions) { + int newR = r + dir[0]; + int newC = c + dir[1]; + + if (newR < 0 || newC < 0 || newR >= rows || newC >= cols || visited.Contains((newR, newC))) { + continue; + } + + int newDiff = Math.Max(diff, Math.Abs(heights[r][c] - heights[newR][newC])); + minHeap.Enqueue((newDiff, newR, newC), newDiff); + } + } + + return 0; + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -390,6 +434,59 @@ class Solution { } ``` +```csharp +public class Solution { + private int[][] directions = new int[][] { + new int[] { 0, 1 }, + new int[] { 0, -1 }, + new int[] { 1, 0 }, + new int[] { -1, 0 } + }; + + public int MinimumEffortPath(int[][] heights) { + int rows = heights.Length; + int cols = heights[0].Length; + + bool Dfs(int r, int c, int limit, HashSet<(int, int)> visited) { + if (r == rows - 1 && c == cols - 1) + return true; + + visited.Add((r, c)); + + foreach (var dir in directions) { + int newR = r + dir[0]; + int newC = c + dir[1]; + + if (newR < 0 || newC < 0 || newR >= rows || newC >= cols || + visited.Contains((newR, newC)) || + Math.Abs(heights[newR][newC] - heights[r][c]) > limit) + continue; + + if (Dfs(newR, newC, limit, visited)) + return true; + } + + return false; + } + + int left = 0, right = 1000000, res = right; + + while (left <= right) { + int mid = (left + right) / 2; + var visited = new HashSet<(int, int)>(); + if (Dfs(0, 0, mid, visited)) { + res = mid; + right = mid - 1; + } else { + left = mid + 1; + } + } + + return res; + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -654,6 +751,77 @@ class Solution { } ``` +```csharp +public class DSU { + private int[] parent; + private int[] size; + + public DSU(int n) { + parent = new int[n + 1]; + size = new int[n + 1]; + for (int i = 0; i <= n; i++) { + parent[i] = i; + size[i] = 1; + } + } + + public int Find(int node) { + if (parent[node] != node) { + parent[node] = Find(parent[node]); + } + return parent[node]; + } + + public bool Union(int u, int v) { + int pu = Find(u); + int pv = Find(v); + if (pu == pv) return false; + if (size[pu] < size[pv]) { + (pu, pv) = (pv, pu); + } + size[pu] += size[pv]; + parent[pv] = pu; + return true; + } +} + +public class Solution { + public int MinimumEffortPath(int[][] heights) { + int rows = heights.Length; + int cols = heights[0].Length; + List<(int, int, int)> edges = new List<(int, int, int)>(); + + for (int r = 0; r < rows; r++) { + for (int c = 0; c < cols; c++) { + int id = r * cols + c; + if (r + 1 < rows) { + int downId = (r + 1) * cols + c; + int diff = Math.Abs(heights[r][c] - heights[r + 1][c]); + edges.Add((diff, id, downId)); + } + if (c + 1 < cols) { + int rightId = r * cols + (c + 1); + int diff = Math.Abs(heights[r][c] - heights[r][c + 1]); + edges.Add((diff, id, rightId)); + } + } + } + + edges.Sort((a, b) => a.Item1.CompareTo(b.Item1)); + DSU dsu = new DSU(rows * cols); + + foreach (var (weight, u, v) in edges) { + dsu.Union(u, v); + if (dsu.Find(0) == dsu.Find(rows * cols - 1)) { + return weight; + } + } + + return 0; + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -839,6 +1007,53 @@ class Solution { } ``` +```csharp +public class Solution { + public int MinimumEffortPath(int[][] heights) { + int rows = heights.Length; + int cols = heights[0].Length; + int[] dist = Enumerable.Repeat(int.MaxValue, rows * cols).ToArray(); + bool[] inQueue = new bool[rows * cols]; + dist[0] = 0; + + int Index(int r, int c) => r * cols + c; + + Queue queue = new Queue(); + queue.Enqueue(0); + inQueue[0] = true; + + int[][] directions = new int[][] { + new int[] {0, 1}, new int[] {0, -1}, + new int[] {1, 0}, new int[] {-1, 0} + }; + + while (queue.Count > 0) { + int u = queue.Dequeue(); + inQueue[u] = false; + int r = u / cols, c = u % cols; + + foreach (var dir in directions) { + int newR = r + dir[0], newC = c + dir[1]; + if (newR >= 0 && newR < rows && newC >= 0 && newC < cols) { + int v = Index(newR, newC); + int weight = Math.Abs(heights[r][c] - heights[newR][newC]); + int newDist = Math.Max(dist[u], weight); + if (newDist < dist[v]) { + dist[v] = newDist; + if (!inQueue[v]) { + queue.Enqueue(v); + inQueue[v] = true; + } + } + } + } + } + + return dist[rows * cols - 1]; + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/perfect-squares.md b/articles/perfect-squares.md index ce067516a..eb92e5450 100644 --- a/articles/perfect-squares.md +++ b/articles/perfect-squares.md @@ -83,6 +83,25 @@ class Solution { } ``` +```csharp +public class Solution { + public int NumSquares(int n) { + return Dfs(n); + } + + private int Dfs(int target) { + if (target == 0) return 0; + + int res = target; + for (int i = 1; i * i <= target; i++) { + res = Math.Min(res, 1 + Dfs(target - i * i)); + } + + return res; + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -193,6 +212,29 @@ class Solution { } ``` +```csharp +public class Solution { + private Dictionary memo = new Dictionary(); + + public int NumSquares(int n) { + return Dfs(n); + } + + private int Dfs(int target) { + if (target == 0) return 0; + if (memo.ContainsKey(target)) return memo[target]; + + int res = target; + for (int i = 1; i * i <= target; i++) { + res = Math.Min(res, 1 + Dfs(target - i * i)); + } + + memo[target] = res; + return res; + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -279,6 +321,25 @@ class Solution { } ``` +```csharp +public class Solution { + public int NumSquares(int n) { + int[] dp = new int[n + 1]; + Array.Fill(dp, n); + dp[0] = 0; + + for (int target = 1; target <= n; target++) { + for (int s = 1; s * s <= target; s++) { + int square = s * s; + dp[target] = Math.Min(dp[target], 1 + dp[target - square]); + } + } + + return dp[n]; + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -403,6 +464,40 @@ class Solution { } ``` +```csharp +public class Solution { + public int NumSquares(int n) { + Queue q = new Queue(); + HashSet seen = new HashSet(); + + int res = 0; + q.Enqueue(0); + + while (q.Count > 0) { + res++; + int size = q.Count; + for (int i = 0; i < size; i++) { + int cur = q.Dequeue(); + int s = 1; + while (s * s + cur <= n) { + int nxt = cur + s * s; + if (nxt == n) { + return res; + } + if (!seen.Contains(nxt)) { + seen.Add(nxt); + q.Enqueue(nxt); + } + s++; + } + } + } + + return res; + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -540,6 +635,37 @@ class Solution { } ``` +```csharp +public class Solution { + public int NumSquares(int n) { + while (n % 4 == 0) { + n /= 4; + } + + if (n % 8 == 7) { + return 4; + } + + bool IsSquareNum(int num) { + int s = (int)Math.Sqrt(num); + return s * s == num; + } + + if (IsSquareNum(n)) { + return 1; + } + + for (int i = 1; i * i <= n; i++) { + if (IsSquareNum(n - i * i)) { + return 2; + } + } + + return 3; + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/permutations-ii.md b/articles/permutations-ii.md index d4c0edd41..83aefec3b 100644 --- a/articles/permutations-ii.md +++ b/articles/permutations-ii.md @@ -121,6 +121,38 @@ class Solution { } ``` +```csharp +public class Solution { + public List> PermuteUnique(int[] nums) { + HashSet resSet = new HashSet(); + List> result = new List>(); + Backtrack(new List(), nums, resSet, result); + return result; + } + + private void Backtrack(List perm, int[] nums, HashSet resSet, List> result) { + if (perm.Count == nums.Length) { + string key = string.Join(",", perm); + if (resSet.Add(key)) { + result.Add(new List(perm)); + } + return; + } + + for (int i = 0; i < nums.Length; i++) { + if (nums[i] != int.MinValue) { + int temp = nums[i]; + perm.Add(temp); + nums[i] = int.MinValue; + Backtrack(perm, nums, resSet, result); + nums[i] = temp; + perm.RemoveAt(perm.Count - 1); + } + } + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -268,6 +300,44 @@ class Solution { } ``` +```csharp +public class Solution { + public List> PermuteUnique(int[] nums) { + var res = new List>(); + var perm = new List(); + var count = new Dictionary(); + + foreach (int num in nums) { + if (!count.ContainsKey(num)) { + count[num] = 0; + } + count[num]++; + } + + void Dfs() { + if (perm.Count == nums.Length) { + res.Add(new List(perm)); + return; + } + + foreach (var kvp in count) { + int num = kvp.Key; + if (count[num] > 0) { + perm.Add(num); + count[num]--; + Dfs(); + count[num]++; + perm.RemoveAt(perm.Count - 1); + } + } + } + + Dfs(); + return res; + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -413,6 +483,39 @@ class Solution { } ``` +```csharp +public class Solution { + public List> PermuteUnique(int[] nums) { + var res = new List>(); + var perm = new List(); + int n = nums.Length; + bool[] visit = new bool[n]; + Array.Sort(nums); + + void Dfs() { + if (perm.Count == n) { + res.Add(new List(perm)); + return; + } + + for (int i = 0; i < n; i++) { + if (visit[i]) continue; + if (i > 0 && nums[i] == nums[i - 1] && !visit[i - 1]) continue; + + visit[i] = true; + perm.Add(nums[i]); + Dfs(); + perm.RemoveAt(perm.Count - 1); + visit[i] = false; + } + } + + Dfs(); + return res; + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -552,6 +655,41 @@ class Solution { } ``` +```csharp +public class Solution { + public List> PermuteUnique(int[] nums) { + var res = new List>(); + Array.Sort(nums); + Dfs(0); + return res; + + void Dfs(int i) { + if (i == nums.Length) { + res.Add(new List(nums)); + return; + } + + for (int j = i; j < nums.Length; j++) { + if (j > i && nums[j] == nums[i]) continue; + + Swap(i, j); + Dfs(i + 1); + } + + for (int j = nums.Length - 1; j > i; j--) { + Swap(i, j); + } + } + + void Swap(int a, int b) { + int temp = nums[a]; + nums[a] = nums[b]; + nums[b] = temp; + } + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -704,6 +842,48 @@ class Solution { } ``` +```csharp +public class Solution { + public List> PermuteUnique(int[] nums) { + int n = nums.Length; + Array.Sort(nums); + var res = new List>(); + res.Add(new List(nums)); + + while (true) { + int i = n - 2; + while (i >= 0 && nums[i] >= nums[i + 1]) { + i--; + } + + if (i < 0) break; + + int j = n - 1; + while (nums[j] <= nums[i]) { + j--; + } + + Swap(nums, i, j); + + int left = i + 1, right = n - 1; + while (left < right) { + Swap(nums, left++, right--); + } + + res.Add(new List(nums)); + } + + return res; + } + + private void Swap(int[] nums, int i, int j) { + int temp = nums[i]; + nums[i] = nums[j]; + nums[j] = temp; + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/remove-duplicates-from-sorted-array.md b/articles/remove-duplicates-from-sorted-array.md index 57bb96cdc..7629a2ff3 100644 --- a/articles/remove-duplicates-from-sorted-array.md +++ b/articles/remove-duplicates-from-sorted-array.md @@ -56,6 +56,16 @@ class Solution { } ``` +```csharp +public class Solution { + public int RemoveDuplicates(int[] nums) { + int[] unique = nums.Distinct().OrderBy(x => x).ToArray(); + Array.Copy(unique, nums, unique.Length); + return unique.Length; + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -135,6 +145,25 @@ class Solution { } ``` +```csharp +public class Solution { + public int RemoveDuplicates(int[] nums) { + int n = nums.Length; + int l = 0, r = 0; + + while (r < n) { + nums[l] = nums[r]; + while (r < n && nums[r] == nums[l]) { + r++; + } + l++; + } + + return l; + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -206,6 +235,22 @@ class Solution { } ``` +```csharp +public class Solution { + public int RemoveDuplicates(int[] nums) { + int l = 1; + for (int r = 1; r < nums.Length; r++) { + if (nums[r] != nums[r - 1]) { + nums[l] = nums[r]; + l++; + } + } + + return l; + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/reverse-linked-list-ii.md b/articles/reverse-linked-list-ii.md index 5a3d69df7..98f00e838 100644 --- a/articles/reverse-linked-list-ii.md +++ b/articles/reverse-linked-list-ii.md @@ -194,6 +194,57 @@ class Solution { } ``` +```csharp +/** + * Definition for singly-linked list. + * public class ListNode { + * public int val; + * public ListNode next; + * public ListNode(int val=0, ListNode next=null) { + * this.val = val; + * this.next = next; + * } + * } + */ +public class Solution { + public ListNode ReverseBetween(ListNode head, int left, int right) { + ListNode dummy = new ListNode(0, head); + ListNode prev = dummy; + + for (int i = 0; i < left - 1; i++) { + prev = prev.next; + } + + ListNode sublistHead = prev.next; + ListNode sublistTail = sublistHead; + for (int i = 0; i < right - left; i++) { + sublistTail = sublistTail.next; + } + + ListNode nextNode = sublistTail.next; + sublistTail.next = null; + + ListNode reversedSublist = ReverseList(sublistHead); + prev.next = reversedSublist; + sublistHead.next = nextNode; + + return dummy.next; + } + + private ListNode ReverseList(ListNode head) { + if (head == null) return null; + + ListNode newHead = head; + if (head.next != null) { + newHead = ReverseList(head.next); + head.next.next = head; + } + head.next = null; + return newHead; + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -334,6 +385,42 @@ class Solution { } ``` +```csharp +/** + * Definition for singly-linked list. + * public class ListNode { + * public int val; + * public ListNode next; + * public ListNode(int val=0, ListNode next=null) { + * this.val = val; + * this.next = next; + * } + * } + */ +public class Solution { + private ListNode successor = null; + + private ListNode ReverseList(ListNode node, int n) { + if (n == 1) { + successor = node.next; + return node; + } + ListNode newHead = ReverseList(node.next, n - 1); + node.next.next = node; + node.next = successor; + return newHead; + } + + public ListNode ReverseBetween(ListNode head, int left, int right) { + if (left == 1) { + return ReverseList(head, right); + } + head.next = ReverseBetween(head.next, left - 1, right - 1); + return head; + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -540,6 +627,58 @@ class Solution { } ``` +```csharp +/** + * Definition for singly-linked list. + * public class ListNode { + * public int val; + * public ListNode next; + * public ListNode(int val=0, ListNode next=null) { + * this.val = val; + * this.next = next; + * } + * } + */ +public class Solution { + public ListNode ReverseBetween(ListNode head, int left, int right) { + ListNode dummy = new ListNode(0); + dummy.next = head; + ListNode prev = dummy; + + for (int i = 0; i < left - 1; i++) { + prev = prev.next; + } + + ListNode sublistHead = prev.next; + ListNode sublistTail = sublistHead; + for (int i = 0; i < right - left; i++) { + sublistTail = sublistTail.next; + } + + ListNode nextNode = sublistTail.next; + sublistTail.next = null; + + ListNode reversedSublist = ReverseList(sublistHead); + prev.next = reversedSublist; + + sublistHead.next = nextNode; + return dummy.next; + } + + private ListNode ReverseList(ListNode head) { + ListNode prev = null; + ListNode curr = head; + while (curr != null) { + ListNode temp = curr.next; + curr.next = prev; + prev = curr; + curr = temp; + } + return prev; + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -699,6 +838,44 @@ class Solution { } ``` +```csharp +/** + * Definition for singly-linked list. + * public class ListNode { + * public int val; + * public ListNode next; + * public ListNode(int val=0, ListNode next=null) { + * this.val = val; + * this.next = next; + * } + * } + */ +public class Solution { + public ListNode ReverseBetween(ListNode head, int left, int right) { + ListNode dummy = new ListNode(0, head); + ListNode leftPrev = dummy, curr = head; + + for (int i = 0; i < left - 1; i++) { + leftPrev = curr; + curr = curr.next; + } + + ListNode prev = null; + for (int i = 0; i < right - left + 1; i++) { + ListNode tmpNext = curr.next; + curr.next = prev; + prev = curr; + curr = tmpNext; + } + + leftPrev.next.next = curr; + leftPrev.next = prev; + + return dummy.next; + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/reverse-string.md b/articles/reverse-string.md index ac60401c5..6c671dd5f 100644 --- a/articles/reverse-string.md +++ b/articles/reverse-string.md @@ -62,6 +62,23 @@ class Solution { } ``` +```csharp +public class Solution { + public void ReverseString(char[] s) { + char[] tmp = new char[s.Length]; + int n = s.Length; + + for (int i = 0; i < n; i++) { + tmp[i] = s[n - 1 - i]; + } + + for (int i = 0; i < n; i++) { + s[i] = tmp[i]; + } + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -141,6 +158,23 @@ class Solution { } ``` +```csharp +public class Solution { + public void ReverseString(char[] s) { + Reverse(s, 0, s.Length - 1); + } + + private void Reverse(char[] s, int left, int right) { + if (left < right) { + Reverse(s, left + 1, right - 1); + char temp = s[left]; + s[left] = s[right]; + s[right] = temp; + } + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -220,6 +254,22 @@ class Solution { } ``` +```csharp +public class Solution { + public void ReverseString(char[] s) { + Stack stack = new Stack(); + + foreach (char c in s) { + stack.Push(c); + } + + for (int i = 0; i < s.Length; i++) { + s[i] = stack.Pop(); + } + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -279,6 +329,14 @@ class Solution { } ``` +```csharp +public class Solution { + public void ReverseString(char[] s) { + Array.Reverse(s); + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -349,6 +407,21 @@ class Solution { } ``` +```csharp +public class Solution { + public void ReverseString(char[] s) { + int l = 0, r = s.Length - 1; + while (l < r) { + char temp = s[l]; + s[l] = s[r]; + s[r] = temp; + l++; + r--; + } + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/search-in-rotated-sorted-array-ii.md b/articles/search-in-rotated-sorted-array-ii.md index d77b4c923..04af02db0 100644 --- a/articles/search-in-rotated-sorted-array-ii.md +++ b/articles/search-in-rotated-sorted-array-ii.md @@ -53,6 +53,14 @@ class Solution { } ``` +```csharp +public class Solution { + public bool Search(int[] nums, int target) { + return nums.Contains(target); + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -199,6 +207,37 @@ class Solution { } ``` +```csharp +public class Solution { + public bool Search(int[] nums, int target) { + int l = 0, r = nums.Length - 1; + while (l <= r) { + int m = l + (r - l) / 2; + if (nums[m] == target) { + return true; + } + + if (nums[l] < nums[m]) { + if (nums[l] <= target && target < nums[m]) { + r = m - 1; + } else { + l = m + 1; + } + } else if (nums[l] > nums[m]) { + if (nums[m] < target && target <= nums[r]) { + l = m + 1; + } else { + r = m - 1; + } + } else { + l++; + } + } + return false; + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/search-insert-position.md b/articles/search-insert-position.md index 225a7e0a1..7e6cbd60e 100644 --- a/articles/search-insert-position.md +++ b/articles/search-insert-position.md @@ -56,6 +56,19 @@ class Solution { } ``` +```csharp +public class Solution { + public int SearchInsert(int[] nums, int target) { + for (int i = 0; i < nums.Length; i++) { + if (nums[i] >= target) { + return i; + } + } + return nums.Length; + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -158,6 +171,30 @@ class Solution { } ``` +```csharp +public class Solution { + public int SearchInsert(int[] nums, int target) { + int res = nums.Length; + int l = 0, r = nums.Length - 1; + + while (l <= r) { + int mid = (l + r) / 2; + if (nums[mid] == target) { + return mid; + } + if (nums[mid] > target) { + res = mid; + r = mid - 1; + } else { + l = mid + 1; + } + } + + return res; + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -252,6 +289,28 @@ class Solution { } ``` +```csharp +public class Solution { + public int SearchInsert(int[] nums, int target) { + int l = 0, r = nums.Length - 1; + + while (l <= r) { + int mid = (l + r) / 2; + if (nums[mid] == target) { + return mid; + } + if (nums[mid] > target) { + r = mid - 1; + } else { + l = mid + 1; + } + } + + return l; + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -335,6 +394,25 @@ class Solution { } ``` +```csharp +public class Solution { + public int SearchInsert(int[] nums, int target) { + int l = 0, r = nums.Length; + + while (l < r) { + int m = l + (r - l) / 2; + if (nums[m] >= target) { + r = m; + } else { + l = m + 1; + } + } + + return l; + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -388,6 +466,15 @@ class Solution { } ``` +```csharp +public class Solution { + public int SearchInsert(int[] nums, int target) { + int idx = Array.BinarySearch(nums, target); + return idx >= 0 ? idx : ~idx; + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/simplify-path.md b/articles/simplify-path.md index 23fe29c96..ae0ecb3ee 100644 --- a/articles/simplify-path.md +++ b/articles/simplify-path.md @@ -105,6 +105,32 @@ class Solution { } ``` +```csharp +public class Solution { + public string SimplifyPath(string path) { + Stack stack = new Stack(); + string cur = ""; + + foreach (char c in path + "/") { + if (c == '/') { + if (cur == "..") { + if (stack.Count > 0) stack.Pop(); + } else if (cur != "" && cur != ".") { + stack.Push(cur); + } + cur = ""; + } else { + cur += c; + } + } + + var result = new List(stack); + result.Reverse(); + return "/" + string.Join("/", result); + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -205,6 +231,29 @@ class Solution { } ``` +```csharp +public class Solution { + public string SimplifyPath(string path) { + Stack stack = new Stack(); + string[] parts = path.Split('/'); + + foreach (string part in parts) { + if (part == "..") { + if (stack.Count > 0) { + stack.Pop(); + } + } else if (part != "" && part != ".") { + stack.Push(part); + } + } + + var result = new List(stack); + result.Reverse(); + return "/" + string.Join("/", result); + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/single-threaded-cpu.md b/articles/single-threaded-cpu.md index 5b7005989..16f487b94 100644 --- a/articles/single-threaded-cpu.md +++ b/articles/single-threaded-cpu.md @@ -109,12 +109,12 @@ class Solution { * @return {number[]} */ getOrder(tasks) { - const available = new MinPriorityQueue({ - compare: (a, b) => a[0] === b[0] ? a[1] - b[1] : a[0] - b[0] - }); - const pending = new MinPriorityQueue({ - compare: (a, b) => a[0] - b[0] - }); + const available = new PriorityQueue( + (a, b) => a[0] === b[0] ? a[1] - b[1] : a[0] - b[0] + ); + const pending = new PriorityQueue( + (a, b) => a[0] - b[0] + ); tasks.forEach(([enqueueTime, processTime], i) => { pending.enqueue([enqueueTime, processTime, i]); @@ -143,6 +143,42 @@ class Solution { } ``` +```csharp +public class Solution { + public int[] GetOrder(int[][] tasks) { + var available = new PriorityQueue<(int procTime, int index), (int procTime, int index)>(); + var pending = new PriorityQueue<(int startTime, int procTime, int index), int>(); + + int n = tasks.Length; + for (int i = 0; i < n; i++) { + pending.Enqueue((tasks[i][0], tasks[i][1], i), tasks[i][0]); + } + + long time = 0; + int[] res = new int[n]; + int idx = 0; + + while (pending.Count > 0 || available.Count > 0) { + while (pending.Count > 0 && pending.Peek().startTime <= time) { + var task = pending.Dequeue(); + available.Enqueue((task.procTime, task.index), (task.procTime, task.index)); + } + + if (available.Count == 0) { + time = pending.Peek().startTime; + continue; + } + + var next = available.Dequeue(); + time += next.procTime; + res[idx++] = next.index; + } + + return res; + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -259,9 +295,9 @@ class Solution { tasks.sort((a, b) => a[0] - b[0]); const res = []; - const minHeap = new MinPriorityQueue({ compare: (a, b) => + const minHeap = new PriorityQueue((a, b) => a[0] === b[0] ? a[1] - b[1] : a[0] - b[0] - }); + ); let i = 0, time = tasks[0][0]; while (minHeap.size() || i < n) { @@ -282,6 +318,44 @@ class Solution { } ``` +```csharp +public class Solution { + public int[] GetOrder(int[][] tasks) { + int n = tasks.Length; + int i = 0; + for (; i < n; i++) { + tasks[i] = new int[] { tasks[i][0], tasks[i][1], i }; // {enqueueTime, processingTime, index} + } + + Array.Sort(tasks, (a, b) => a[0].CompareTo(b[0])); // sort by enqueueTime + + int[] res = new int[n]; + var minHeap = new PriorityQueue<(int procTime, int index), (int procTime, int index)>(); + + int idx = 0; + i = 0; + long time = tasks[0][0]; + + while (minHeap.Count > 0 || i < n) { + while (i < n && tasks[i][0] <= time) { + minHeap.Enqueue((tasks[i][1], tasks[i][2]), (tasks[i][1], tasks[i][2])); + i++; + } + + if (minHeap.Count == 0) { + time = tasks[i][0]; + } else { + var task = minHeap.Dequeue(); + time += task.procTime; + res[idx++] = task.index; + } + } + + return res; + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -430,14 +504,14 @@ class Solution { return a - b; }); - const minHeap = new MinPriorityQueue({ - compare: (a, b) => { + const minHeap = new PriorityQueue( + (a, b) => { if (tasks[a][1] !== tasks[b][1]) { return tasks[a][1] - tasks[b][1]; } return a - b; } - }); + ); const res = []; let time = 0; @@ -463,6 +537,48 @@ class Solution { } ``` +```csharp +public class Solution { + public int[] GetOrder(int[][] tasks) { + int n = tasks.Length; + int[] indices = new int[n]; + int i = 0; + for (; i < n; i++) { + indices[i] = i; + } + + Array.Sort(indices, (a, b) => + tasks[a][0] != tasks[b][0] ? tasks[a][0].CompareTo(tasks[b][0]) : a.CompareTo(b) + ); + + var minHeap = new PriorityQueue(); + + int[] result = new int[n]; + long time = 0; + int resIndex = 0; + i = 0; + + while (minHeap.Count > 0 || i < n) { + while (i < n && tasks[indices[i]][0] <= time) { + int idx = indices[i]; + minHeap.Enqueue(idx, (tasks[idx][1], idx)); + i++; + } + + if (minHeap.Count == 0) { + time = tasks[indices[i]][0]; + } else { + int nextIndex = minHeap.Dequeue(); + time += tasks[nextIndex][1]; + result[resIndex++] = nextIndex; + } + } + + return result; + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/split-array-largest-sum.md b/articles/split-array-largest-sum.md index dccf649c5..617691f9b 100644 --- a/articles/split-array-largest-sum.md +++ b/articles/split-array-largest-sum.md @@ -113,6 +113,36 @@ class Solution { } ``` +```csharp +public class Solution { + public int SplitArray(int[] nums, int k) { + int n = nums.Length; + return Dfs(nums, 0, k, n); + } + + private int Dfs(int[] nums, int i, int m, int n) { + if (i == n) { + return m == 0 ? 0 : int.MaxValue; + } + if (m == 0) { + return int.MaxValue; + } + + int res = int.MaxValue; + int curSum = 0; + for (int j = i; j <= n - m; j++) { + curSum += nums[j]; + int next = Dfs(nums, j + 1, m - 1, n); + if (next != int.MaxValue) { + res = Math.Min(res, Math.Max(curSum, next)); + } + } + + return res; + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -231,7 +261,7 @@ class Solution { */ splitArray(nums, k) { const n = nums.length; - const dp = Array.from({ length: n }, () => Array(m + 1).fill(-1)); + const dp = Array.from({ length: n }, () => Array(k + 1).fill(-1)); const dfs = (i, m) => { if (i === n) { @@ -259,6 +289,50 @@ class Solution { } ``` +```csharp +public class Solution { + private int[,] dp; + + public int SplitArray(int[] nums, int k) { + int n = nums.Length; + dp = new int[n, k + 1]; + + for (int i = 0; i < n; i++) { + for (int j = 0; j <= k; j++) { + dp[i, j] = -1; + } + } + + return Dfs(nums, 0, k, n); + } + + private int Dfs(int[] nums, int i, int m, int n) { + if (i == n) { + return m == 0 ? 0 : int.MaxValue; + } + if (m == 0) { + return int.MaxValue; + } + if (dp[i, m] != -1) { + return dp[i, m]; + } + + int res = int.MaxValue; + int curSum = 0; + for (int j = i; j <= n - m; j++) { + curSum += nums[j]; + int next = Dfs(nums, j + 1, m - 1, n); + if (next != int.MaxValue) { + res = Math.Min(res, Math.Max(curSum, next)); + } + } + + dp[i, m] = res; + return res; + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -368,6 +442,37 @@ class Solution { } ``` +```csharp +public class Solution { + public int SplitArray(int[] nums, int k) { + int n = nums.Length; + int[,] dp = new int[n + 1, k + 1]; + + for (int i = 0; i <= n; i++) { + for (int j = 0; j <= k; j++) { + dp[i, j] = int.MaxValue; + } + } + + dp[n, 0] = 0; + + for (int m = 1; m <= k; m++) { + for (int i = n - 1; i >= 0; i--) { + int curSum = 0; + for (int j = i; j < n - m + 1; j++) { + curSum += nums[j]; + if (dp[j + 1, m - 1] != int.MaxValue) { + dp[i, m] = Math.Min(dp[i, m], Math.Max(curSum, dp[j + 1, m - 1])); + } + } + } + } + + return dp[0, k]; + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -485,6 +590,36 @@ class Solution { } ``` +```csharp +public class Solution { + public int SplitArray(int[] nums, int k) { + int n = nums.Length; + int[] dp = new int[n + 1]; + int[] nextDp = new int[n + 1]; + Array.Fill(dp, int.MaxValue); + dp[n] = 0; + + for (int m = 1; m <= k; m++) { + Array.Fill(nextDp, int.MaxValue); + for (int i = n - 1; i >= 0; i--) { + int curSum = 0; + for (int j = i; j < n - m + 1; j++) { + curSum += nums[j]; + if (dp[j + 1] != int.MaxValue) { + nextDp[i] = Math.Min(nextDp[i], Math.Max(curSum, dp[j + 1])); + } + } + } + var temp = dp; + dp = nextDp; + nextDp = temp; + } + + return dp[0]; + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -639,6 +774,43 @@ class Solution { } ``` +```csharp +public class Solution { + public int SplitArray(int[] nums, int k) { + int l = 0, r = 0, res = 0; + foreach (int num in nums) { + l = Math.Max(l, num); + r += num; + } + res = r; + + while (l <= r) { + int mid = l + (r - l) / 2; + if (CanSplit(nums, k, mid)) { + res = mid; + r = mid - 1; + } else { + l = mid + 1; + } + } + return res; + } + + private bool CanSplit(int[] nums, int k, int largest) { + int subarray = 1, curSum = 0; + foreach (int num in nums) { + curSum += num; + if (curSum > largest) { + subarray++; + if (subarray > k) return false; + curSum = num; + } + } + return true; + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -852,6 +1024,60 @@ class Solution { } ``` +```csharp +public class Solution { + private int[] prefix; + private int n; + + public int SplitArray(int[] nums, int k) { + n = nums.Length; + prefix = new int[n + 1]; + for (int i = 0; i < n; i++) { + prefix[i + 1] = prefix[i] + nums[i]; + } + + int l = int.MinValue, r = 0; + foreach (int num in nums) { + l = Math.Max(l, num); + r += num; + } + + int res = r; + while (l <= r) { + int mid = l + (r - l) / 2; + if (CanSplit(mid, k)) { + res = mid; + r = mid - 1; + } else { + l = mid + 1; + } + } + return res; + } + + private bool CanSplit(int largest, int k) { + int subarrays = 0, i = 0; + while (i < n) { + int l = i + 1, r = n; + while (l <= r) { + int mid = l + (r - l) / 2; + if (prefix[mid] - prefix[i] <= largest) { + l = mid + 1; + } else { + r = mid - 1; + } + } + subarrays++; + i = r; + if (subarrays > k) { + return false; + } + } + return true; + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/sqrtx.md b/articles/sqrtx.md index 13d854b33..bc2e10fa7 100644 --- a/articles/sqrtx.md +++ b/articles/sqrtx.md @@ -82,6 +82,23 @@ class Solution { } ``` +```csharp +public class Solution { + public int MySqrt(int x) { + if (x == 0) return 0; + + int res = 1; + for (int i = 1; i <= x; i++) { + if ((long)i * i > x) { + return res; + } + res = i; + } + return res; + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -130,6 +147,14 @@ class Solution { } ``` +```csharp +public class Solution { + public int MySqrt(int x) { + return (int)Math.Sqrt(x); + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -236,6 +261,31 @@ class Solution { } ``` +```csharp +public class Solution { + public int MySqrt(int x) { + int l = 0, r = x; + int res = 0; + + while (l <= r) { + int m = l + (r - l) / 2; + long sq = (long)m * m; + + if (sq > x) { + r = m - 1; + } else if (sq < x) { + l = m + 1; + res = m; + } else { + return m; + } + } + + return res; + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -307,6 +357,20 @@ class Solution { } ``` +```csharp +public class Solution { + public int MySqrt(int x) { + if (x < 2) { + return x; + } + + int l = MySqrt(x >> 2) << 1; + int r = l + 1; + return (long)r * r > x ? l : r; + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -370,6 +434,18 @@ class Solution { } ``` +```csharp +public class Solution { + public int MySqrt(int x) { + long r = x; + while (r * r > x) { + r = (r + x / r) >> 1; + } + return (int)r; + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/stone-game-iii.md b/articles/stone-game-iii.md index e95a8e508..781024036 100644 --- a/articles/stone-game-iii.md +++ b/articles/stone-game-iii.md @@ -144,6 +144,40 @@ class Solution { } ``` +```csharp +public class Solution { + public string StoneGameIII(int[] stoneValue) { + int n = stoneValue.Length; + int?[,] dp = new int?[n, 2]; + + int Dfs(int i, int alice) { + if (i >= n) return 0; + if (dp[i, alice].HasValue) return dp[i, alice].Value; + + int res = alice == 1 ? int.MinValue : int.MaxValue; + int score = 0; + + for (int j = i; j < Math.Min(i + 3, n); j++) { + if (alice == 1) { + score += stoneValue[j]; + res = Math.Max(res, score + Dfs(j + 1, 0)); + } else { + score -= stoneValue[j]; + res = Math.Min(res, score + Dfs(j + 1, 1)); + } + } + + dp[i, alice] = res; + return res; + } + + int result = Dfs(0, 1); + if (result == 0) return "Tie"; + return result > 0 ? "Alice" : "Bob"; + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -277,6 +311,33 @@ class Solution { } ``` +```csharp +public class Solution { + public string StoneGameIII(int[] stoneValue) { + int n = stoneValue.Length; + Dictionary dp = new Dictionary(); + + int Dfs(int i) { + if (i >= n) return 0; + if (dp.ContainsKey(i)) return dp[i]; + + int res = int.MinValue, total = 0; + for (int j = i; j < Math.Min(i + 3, n); j++) { + total += stoneValue[j]; + res = Math.Max(res, total - Dfs(j + 1)); + } + + dp[i] = res; + return res; + } + + int result = Dfs(0); + if (result == 0) return "Tie"; + return result > 0 ? "Alice" : "Bob"; + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -384,6 +445,29 @@ class Solution { } ``` +```csharp +public class Solution { + public string StoneGameIII(int[] stoneValue) { + int n = stoneValue.Length; + int[] dp = new int[n + 1]; + for (int i = 0; i <= n; i++) dp[i] = int.MinValue; + dp[n] = 0; + + for (int i = n - 1; i >= 0; i--) { + int total = 0; + for (int j = i; j < Math.Min(i + 3, n); j++) { + total += stoneValue[j]; + dp[i] = Math.Max(dp[i], total - dp[j + 1]); + } + } + + int result = dp[0]; + if (result == 0) return "Tie"; + return result > 0 ? "Alice" : "Bob"; + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -483,6 +567,28 @@ class Solution { } ``` +```csharp +public class Solution { + public string StoneGameIII(int[] stoneValue) { + int n = stoneValue.Length; + int[] dp = new int[4]; + + for (int i = n - 1; i >= 0; i--) { + int total = 0; + dp[i % 4] = int.MinValue; + + for (int j = i; j < Math.Min(i + 3, n); j++) { + total += stoneValue[j]; + dp[i % 4] = Math.Max(dp[i % 4], total - dp[(j + 1) % 4]); + } + } + + if (dp[0] == 0) return "Tie"; + return dp[0] > 0 ? "Alice" : "Bob"; + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/sum-of-all-subset-xor-totals.md b/articles/sum-of-all-subset-xor-totals.md index b1f8dc9ae..b35c24e37 100644 --- a/articles/sum-of-all-subset-xor-totals.md +++ b/articles/sum-of-all-subset-xor-totals.md @@ -98,6 +98,31 @@ class Solution { } ``` +```csharp +public class Solution { + private int res = 0; + + public int SubsetXORSum(int[] nums) { + Backtrack(0, new List(), nums); + return res; + } + + private void Backtrack(int i, List subset, int[] nums) { + int xorr = 0; + foreach (int num in subset) { + xorr ^= num; + } + res += xorr; + + for (int j = i; j < nums.Length; j++) { + subset.Add(nums[j]); + Backtrack(j + 1, subset, nums); + subset.RemoveAt(subset.Count - 1); + } + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -173,6 +198,21 @@ class Solution { } ``` +```csharp +public class Solution { + public int SubsetXORSum(int[] nums) { + return Dfs(0, 0, nums); + } + + private int Dfs(int i, int total, int[] nums) { + if (i == nums.Length) { + return total; + } + return Dfs(i + 1, total ^ nums[i], nums) + Dfs(i + 1, total, nums); + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -270,6 +310,27 @@ class Solution { } ``` +```csharp +public class Solution { + public int SubsetXORSum(int[] nums) { + int n = nums.Length; + int res = 0; + + for (int mask = 0; mask < (1 << n); mask++) { + int xorr = 0; + for (int i = 0; i < n; i++) { + if ((mask & (1 << i)) != 0) { + xorr ^= nums[i]; + } + } + res += xorr; + } + + return res; + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -333,6 +394,18 @@ class Solution { } ``` +```csharp +public class Solution { + public int SubsetXORSum(int[] nums) { + int res = 0; + foreach (int num in nums) { + res |= num; + } + return res << (nums.Length - 1); + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/verifying-an-alien-dictionary.md b/articles/verifying-an-alien-dictionary.md index 7ab6ac58d..86694cf0a 100644 --- a/articles/verifying-an-alien-dictionary.md +++ b/articles/verifying-an-alien-dictionary.md @@ -84,6 +84,35 @@ class Solution { } ``` +```csharp +public class Solution { + public bool IsAlienSorted(string[] words, string order) { + int[] orderIndex = new int[26]; + for (int i = 0; i < order.Length; i++) { + orderIndex[order[i] - 'a'] = i; + } + + string[] sortedWords = (string[])words.Clone(); + Array.Sort(sortedWords, (w1, w2) => { + for (int i = 0; i < Math.Min(w1.Length, w2.Length); i++) { + if (w1[i] != w2[i]) { + return orderIndex[w1[i] - 'a'] - orderIndex[w2[i] - 'a']; + } + } + return w1.Length - w2.Length; + }); + + for (int i = 0; i < words.Length; i++) { + if (!words[i].Equals(sortedWords[i])) { + return false; + } + } + + return true; + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -201,6 +230,37 @@ class Solution { } ``` +```csharp +public class Solution { + public bool IsAlienSorted(string[] words, string order) { + Dictionary orderIndex = new Dictionary(); + for (int i = 0; i < order.Length; i++) { + orderIndex[order[i]] = i; + } + + for (int i = 0; i < words.Length - 1; i++) { + string w1 = words[i]; + string w2 = words[i + 1]; + + for (int j = 0; j < w1.Length; j++) { + if (j == w2.Length) { + return false; + } + + if (w1[j] != w2[j]) { + if (orderIndex[w1[j]] > orderIndex[w2[j]]) { + return false; + } + break; + } + } + } + + return true; + } +} +``` + ::tabs-end ### Time & Space Complexity diff --git a/articles/word-break-ii.md b/articles/word-break-ii.md index c075d2b67..45660e5f9 100644 --- a/articles/word-break-ii.md +++ b/articles/word-break-ii.md @@ -131,6 +131,35 @@ class Solution { } ``` +```csharp +public class Solution { + public List WordBreak(string s, List wordDict) { + HashSet wordSet = new HashSet(wordDict); + List res = new List(); + List cur = new List(); + + void Backtrack(int i) { + if (i == s.Length) { + res.Add(string.Join(" ", cur)); + return; + } + + for (int j = i; j < s.Length; j++) { + string word = s.Substring(i, j - i + 1); + if (wordSet.Contains(word)) { + cur.Add(word); + Backtrack(j + 1); + cur.RemoveAt(cur.Count - 1); + } + } + } + + Backtrack(0); + return res; + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -401,6 +430,66 @@ class Solution { } ``` +```csharp +public class TrieNode { + public Dictionary children = new Dictionary(); + public bool isWord = false; +} + +public class Trie { + public TrieNode root = new TrieNode(); + + public void AddWord(string word) { + TrieNode curr = root; + foreach (char c in word) { + if (!curr.children.ContainsKey(c)) { + curr.children[c] = new TrieNode(); + } + curr = curr.children[c]; + } + curr.isWord = true; + } +} + +public class Solution { + public List WordBreak(string s, List wordDict) { + Trie trie = new Trie(); + foreach (string word in wordDict) { + trie.AddWord(word); + } + + List res = new List(); + + void Backtrack(int index, List path) { + if (index == s.Length) { + res.Add(string.Join(" ", path)); + return; + } + + TrieNode node = trie.root; + StringBuilder word = new StringBuilder(); + + for (int i = index; i < s.Length; i++) { + char c = s[i]; + if (!node.children.ContainsKey(c)) break; + + word.Append(c); + node = node.children[c]; + + if (node.isWord) { + path.Add(word.ToString()); + Backtrack(i + 1, path); + path.RemoveAt(path.Count - 1); + } + } + } + + Backtrack(0, new List()); + return res; + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -561,6 +650,40 @@ class Solution { } ``` +```csharp +public class Solution { + public List WordBreak(string s, List wordDictList) { + HashSet wordDict = new HashSet(wordDictList); + Dictionary> cache = new Dictionary>(); + + List Backtrack(int i) { + if (i == s.Length) return new List { "" }; + if (cache.ContainsKey(i)) return cache[i]; + + List res = new List(); + for (int j = i; j < s.Length; j++) { + string w = s.Substring(i, j - i + 1); + if (!wordDict.Contains(w)) continue; + + List substrings = Backtrack(j + 1); + foreach (string substr in substrings) { + string sentence = w; + if (!string.IsNullOrEmpty(substr)) { + sentence += " " + substr; + } + res.Add(sentence); + } + } + + cache[i] = res; + return res; + } + + return Backtrack(0); + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -676,6 +799,34 @@ class Solution { } ``` +```csharp +public class Solution { + public List WordBreak(string s, List wordDictList) { + HashSet wordSet = new HashSet(wordDictList); + int n = s.Length; + List[] dp = new List[n + 1]; + for (int i = 0; i <= n; i++) { + dp[i] = new List(); + } + dp[0].Add(""); + + for (int i = 1; i <= n; i++) { + for (int j = 0; j < i; j++) { + string word = s.Substring(j, i - j); + if (wordSet.Contains(word)) { + foreach (string sentence in dp[j]) { + string space = string.IsNullOrEmpty(sentence) ? "" : " "; + dp[i].Add(sentence + space + word); + } + } + } + } + + return dp[n]; + } +} +``` + ::tabs-end ### Time & Space Complexity @@ -965,6 +1116,68 @@ class Solution { } ``` +```csharp +public class TrieNode { + public Dictionary Children = new Dictionary(); + public bool IsWord = false; +} + +public class Trie { + public TrieNode Root = new TrieNode(); + + public void AddWord(string word) { + TrieNode curr = Root; + foreach (char c in word) { + if (!curr.Children.ContainsKey(c)) { + curr.Children[c] = new TrieNode(); + } + curr = curr.Children[c]; + } + curr.IsWord = true; + } +} + +public class Solution { + private Dictionary> cache = new Dictionary>(); + + public List WordBreak(string s, List wordDict) { + Trie trie = new Trie(); + foreach (string word in wordDict) { + trie.AddWord(word); + } + return Backtrack(0, s, trie.Root, trie); + } + + private List Backtrack(int index, string s, TrieNode root, Trie trie) { + if (index == s.Length) return new List { "" }; + if (cache.ContainsKey(index)) return cache[index]; + + List res = new List(); + TrieNode curr = root; + + for (int i = index; i < s.Length; i++) { + char c = s[i]; + if (!curr.Children.ContainsKey(c)) break; + + curr = curr.Children[c]; + if (curr.IsWord) { + List suffixes = Backtrack(i + 1, s, root, trie); + foreach (string suffix in suffixes) { + if (suffix == "") { + res.Add(s.Substring(index, i - index + 1)); + } else { + res.Add(s.Substring(index, i - index + 1) + " " + suffix); + } + } + } + } + + cache[index] = res; + return res; + } +} +``` + ::tabs-end ### Time & Space Complexity