Skip to content

Commit 7319e1c

Browse files
authored
Improved tasks 198-208
1 parent ea61d76 commit 7319e1c

File tree

5 files changed

+289
-5
lines changed

5 files changed

+289
-5
lines changed

src/main/python/g0101_0200/s0198_house_robber/readme.md

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,4 +31,40 @@ Given an integer array `nums` representing the amount of money of each house, re
3131
**Constraints:**
3232

3333
* `1 <= nums.length <= 100`
34-
* `0 <= nums[i] <= 400`
34+
* `0 <= nums[i] <= 400`
35+
36+
To solve the House Robber problem, we can utilize dynamic programming to find the maximum amount of money we can rob without alerting the police. Here's how we can approach this problem:
37+
38+
1. **Initialize Variables**:
39+
- Initialize two variables, `prev_max` and `curr_max`, to keep track of the maximum amount of money robbed from previous houses and the current house, respectively.
40+
41+
2. **Iterate Through Houses**:
42+
- Iterate through the array of house values `nums`.
43+
44+
3. **Calculate Maximum Amount of Money Robbed**:
45+
- For each house, update `curr_max` to the maximum value between the sum of the value of the current house and `prev_max`, and `prev_max`.
46+
47+
4. **Return Result**:
48+
- After iterating through all houses, return `curr_max`, which represents the maximum amount of money that can be robbed without alerting the police.
49+
50+
Let's implement this approach:
51+
52+
```python
53+
class Solution:
54+
def rob(self, nums: List[int]) -> int:
55+
if not nums:
56+
return 0
57+
if len(nums) == 1:
58+
return nums[0]
59+
60+
prev_max = curr_max = 0
61+
62+
for num in nums:
63+
temp = curr_max
64+
curr_max = max(prev_max + num, curr_max)
65+
prev_max = temp
66+
67+
return curr_max
68+
```
69+
70+
This solution ensures that we calculate the maximum amount of money that can be robbed without alerting the police in linear time complexity O(n) and constant space complexity O(1), meeting the problem constraints.

src/main/python/g0101_0200/s0200_number_of_islands/readme.md

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,4 +37,53 @@ An **island** is surrounded by water and is formed by connecting adjacent lands
3737
* `m == grid.length`
3838
* `n == grid[i].length`
3939
* `1 <= m, n <= 300`
40-
* `grid[i][j]` is `'0'` or `'1'`.
40+
* `grid[i][j]` is `'0'` or `'1'`.
41+
42+
To solve the Number of Islands problem, we can utilize Depth-First Search (DFS) to traverse the grid and identify the islands. Here's a step-by-step approach to solve this problem:
43+
44+
1. **Define DFS Function**:
45+
- Define a DFS function to traverse the grid recursively.
46+
- This function will mark visited cells as `'0'` to avoid revisiting them.
47+
- It will explore adjacent cells (up, down, left, right) and continue DFS traversal if the adjacent cell is land (`'1'`).
48+
49+
2. **Iterate Through Grid**:
50+
- Iterate through each cell in the grid.
51+
52+
3. **Count Islands**:
53+
- For each cell with land (`'1'`), call the DFS function to explore the island.
54+
- Increment the count of islands by 1 after each DFS traversal.
55+
56+
4. **Return Count**:
57+
- After traversing the entire grid, return the count of islands.
58+
59+
Let's implement this approach:
60+
61+
```python
62+
class Solution:
63+
def numIslands(self, grid: List[List[str]]) -> int:
64+
def dfs(grid, i, j):
65+
if i < 0 or i >= len(grid) or j < 0 or j >= len(grid[0]) or grid[i][j] == '0':
66+
return
67+
grid[i][j] = '0' # Mark the current cell as visited
68+
# Explore adjacent cells
69+
dfs(grid, i+1, j)
70+
dfs(grid, i-1, j)
71+
dfs(grid, i, j+1)
72+
dfs(grid, i, j-1)
73+
74+
if not grid:
75+
return 0
76+
77+
num_islands = 0
78+
rows, cols = len(grid), len(grid[0])
79+
80+
for i in range(rows):
81+
for j in range(cols):
82+
if grid[i][j] == '1':
83+
num_islands += 1
84+
dfs(grid, i, j)
85+
86+
return num_islands
87+
```
88+
89+
This solution efficiently counts the number of islands in the given grid by performing DFS traversal on each unvisited land cell. It has a time complexity of O(M * N), where M is the number of rows and N is the number of columns in the grid.

src/main/python/g0201_0300/s0206_reverse_linked_list/readme.md

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,4 +31,48 @@ Given the `head` of a singly linked list, reverse the list, and return _the reve
3131
* The number of nodes in the list is the range `[0, 5000]`.
3232
* `-5000 <= Node.val <= 5000`
3333

34-
**Follow up:** A linked list can be reversed either iteratively or recursively. Could you implement both?
34+
**Follow up:** A linked list can be reversed either iteratively or recursively. Could you implement both?
35+
36+
To solve the Reverse Linked List problem, we can either use an iterative approach or a recursive approach. Here are the steps for both approaches:
37+
38+
### Iterative Approach:
39+
1. **Initialize Pointers**: Initialize three pointers, `prev`, `curr`, and `next`.
40+
2. **Iterate Through List**: Iterate through the list until `curr` is not `None`.
41+
- Update `next` to `curr.next`.
42+
- Reverse the `curr` node's pointer to point to `prev` instead of `next`.
43+
- Move `prev` to `curr` and `curr` to `next`.
44+
3. **Return Head**: Return `prev` as the new head of the reversed list.
45+
46+
### Recursive Approach:
47+
1. **Define Recursive Function**: Define a recursive function that takes a node as input.
48+
2. **Base Case**: If the input node or its next node is `None`, return the node itself.
49+
3. **Recursive Call**: Recursively call the function on the next node.
50+
4. **Reverse Pointers**: Reverse the pointers of the current node and its next node.
51+
5. **Return Head**: Return the new head of the reversed list.
52+
53+
Let's implement both approaches:
54+
55+
```python
56+
class Solution:
57+
def reverseListIterative(self, head: ListNode) -> ListNode:
58+
prev, curr = None, head
59+
while curr:
60+
next_node = curr.next
61+
curr.next = prev
62+
prev = curr
63+
curr = next_node
64+
return prev
65+
66+
def reverseListRecursive(self, head: ListNode) -> ListNode:
67+
def reverse(node):
68+
if not node or not node.next:
69+
return node
70+
new_head = reverse(node.next)
71+
node.next.next = node
72+
node.next = None
73+
return new_head
74+
75+
return reverse(head)
76+
```
77+
78+
These solutions will efficiently reverse the linked list either iteratively or recursively, meeting the problem constraints. The time complexity for both approaches is O(n), where n is the number of nodes in the linked list.

src/main/python/g0201_0300/s0207_course_schedule/readme.md

Lines changed: 78 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,4 +30,81 @@ Return `true` if you can finish all courses. Otherwise, return `false`.
3030
* `0 <= prerequisites.length <= 5000`
3131
* `prerequisites[i].length == 2`
3232
* <code>0 <= a<sub>i</sub>, b<sub>i</sub> < numCourses</code>
33-
* All the pairs prerequisites[i] are **unique**.
33+
* All the pairs prerequisites[i] are **unique**.
34+
35+
To solve the Course Schedule problem, we can use a graph-based approach with topological sorting. We'll represent the courses and their prerequisites as a directed graph, and then perform a topological sort to determine if there exists any cycle in the graph. If there is a cycle, it means there is a dependency loop, and it won't be possible to complete all courses.
36+
37+
### Steps:
38+
39+
1. **Build the Graph**:
40+
- Create an adjacency list to represent the directed graph.
41+
- Iterate through the `prerequisites` array and add edges to the graph.
42+
43+
2. **Perform Topological Sorting**:
44+
- Implement a function for topological sorting, which can be done using Depth-First Search (DFS) or Breadth-First Search (BFS).
45+
- In each approach, keep track of the visited nodes and the current path.
46+
- If during DFS, we encounter a node that is already in the current path, it indicates a cycle, and we return False.
47+
- If the sorting completes without finding a cycle, return True.
48+
49+
3. **Check for Cycle**:
50+
- If any node has a cycle in its path, return False.
51+
52+
4. **Return Result**:
53+
- If no cycle is found, return True, indicating it's possible to finish all courses.
54+
55+
### Implementation:
56+
57+
```python
58+
from collections import defaultdict
59+
60+
class Solution:
61+
def canFinish(self, numCourses: int, prerequisites: List[List[int]]) -> bool:
62+
# Build the graph
63+
graph = defaultdict(list)
64+
for course, prereq in prerequisites:
65+
graph[course].append(prereq)
66+
67+
# Function for topological sorting using DFS
68+
def dfs(course, visited, path):
69+
if course in path:
70+
return False # Cycle detected
71+
if visited[course]:
72+
return True # Already visited
73+
visited[course] = True
74+
path.add(course)
75+
for neighbor in graph[course]:
76+
if not dfs(neighbor, visited, path):
77+
return False
78+
path.remove(course)
79+
return True
80+
81+
# Perform topological sorting for each course
82+
for course in range(numCourses):
83+
visited = [False] * numCourses
84+
path = set()
85+
if not dfs(course, visited, path):
86+
return False
87+
88+
return True
89+
```
90+
91+
### Explanation:
92+
93+
1. **Build the Graph**:
94+
- We use a defaultdict to create an adjacency list representation of the directed graph.
95+
- We iterate through the `prerequisites` array and add edges to the graph.
96+
97+
2. **Perform Topological Sorting**:
98+
- We implement a function `dfs` for topological sorting using Depth-First Search (DFS).
99+
- We keep track of visited nodes and the current path to detect cycles.
100+
- If we encounter a node that is already in the current path, it indicates a cycle, and we return False.
101+
- Otherwise, if DFS completes without finding a cycle, we return True.
102+
103+
3. **Check for Cycle**:
104+
- We iterate through each course and perform topological sorting.
105+
- If any node has a cycle in its path, we return False.
106+
107+
4. **Return Result**:
108+
- If no cycle is found, we return True, indicating it's possible to finish all courses.
109+
110+
This solution has a time complexity of O(V + E), where V is the number of courses and E is the number of prerequisites. The space complexity is O(V + E) for storing the graph.

src/main/python/g0201_0300/s0208_implement_trie_prefix_tree/readme.md

Lines changed: 79 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,4 +33,82 @@ Implement the Trie class:
3333

3434
* `1 <= word.length, prefix.length <= 2000`
3535
* `word` and `prefix` consist only of lowercase English letters.
36-
* At most <code>3 * 10<sup>4</sup></code> calls **in total** will be made to `insert`, `search`, and `startsWith`.
36+
* At most <code>3 * 10<sup>4</sup></code> calls **in total** will be made to `insert`, `search`, and `startsWith`.
37+
38+
To solve the task, here are the steps for implementing the Trie (Prefix Tree) problem:
39+
40+
1. **Define the TrieNode Class**:
41+
- Define a class named `TrieNode` to represent each node in the trie.
42+
- Each node will have a dictionary to store child nodes, indicating possible characters that can follow the current node.
43+
- Additionally, each node will have a boolean flag to mark whether it represents the end of a word.
44+
45+
2. **Initialize the Trie Class**:
46+
- Implement the `__init__` method for the Trie class to initialize an empty trie with a root node.
47+
48+
3. **Implement the Insert Method**:
49+
- Implement the `insert` method to insert a word into the trie.
50+
- Start from the root node and iterate through each character in the word.
51+
- Check if the character exists as a child of the current node. If not, create a new node for it.
52+
- Move to the child node and repeat the process for the next character.
53+
- After processing all characters, mark the last node as the end of the word.
54+
55+
4. **Implement the Search Method**:
56+
- Implement the `search` method to check if a word exists in the trie.
57+
- Start from the root node and iterate through each character in the word.
58+
- If at any point, a character is not found as a child of the current node, return False.
59+
- If all characters are found and the last node is marked as the end of a word, return True.
60+
61+
5. **Implement the StartsWith Method**:
62+
- Implement the `startsWith` method to check if there is any word in the trie that starts with a given prefix.
63+
- Follow a similar process as the search method, but return True as soon as all characters of the prefix are found, regardless of whether it forms a complete word.
64+
65+
6. **Return Results**:
66+
- After implementing all methods, create an instance of the Trie class and perform the required operations according to the given input.
67+
68+
Here's a Python implementation:
69+
70+
```python
71+
class TrieNode:
72+
def __init__(self):
73+
self.children = {}
74+
self.is_end_of_word = False
75+
76+
class Trie:
77+
def __init__(self):
78+
self.root = TrieNode()
79+
80+
def insert(self, word: str) -> None:
81+
node = self.root
82+
for char in word:
83+
if char not in node.children:
84+
node.children[char] = TrieNode()
85+
node = node.children[char]
86+
node.is_end_of_word = True
87+
88+
def search(self, word: str) -> bool:
89+
node = self.root
90+
for char in word:
91+
if char not in node.children:
92+
return False
93+
node = node.children[char]
94+
return node.is_end_of_word
95+
96+
def startsWith(self, prefix: str) -> bool:
97+
node = self.root
98+
for char in prefix:
99+
if char not in node.children:
100+
return False
101+
node = node.children[char]
102+
return True
103+
104+
# Example Usage
105+
trie = Trie()
106+
trie.insert("apple")
107+
print(trie.search("apple")) # Output: True
108+
print(trie.search("app")) # Output: False
109+
print(trie.startsWith("app")) # Output: True
110+
trie.insert("app")
111+
print(trie.search("app")) # Output: True
112+
```
113+
114+
This solution efficiently implements the Trie data structure and satisfies the requirements of the problem.

0 commit comments

Comments
 (0)