Skip to content

Commit ae700c2

Browse files
committed
add python solutions for two pointers pattern
1 parent b19f688 commit ae700c2

File tree

5 files changed

+700
-0
lines changed

5 files changed

+700
-0
lines changed

python/3sum.md

Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
# 3Sum
2+
Given an integer array nums, return all the triplets [nums[i], nums[j], nums[k]] such that i != j, i != k, and j != k, and nums[i] + nums[j] + nums[k] == 0.
3+
4+
Notice that the solution set must not contain duplicate triplets.
5+
6+
### Constraints:
7+
0 <= nums.length <= 3000
8+
-10^5 <= nums[i] <= 10^5
9+
10+
### Examples
11+
```javascript
12+
Input: nums = [-1,0,1,2,-1,-4]
13+
Output: [[-1,-1,2],[-1,0,1]]
14+
Explanation:
15+
nums[0] + nums[1] + nums[2] = -1 + 0 + 1 = 0
16+
nums[1] + nums[2] + nums[3] = -1 + 0 + 1 = 0
17+
There are two unique triplets that sum to zero.
18+
19+
Input: nums = []
20+
Output: []
21+
22+
Input: nums = [0]
23+
Output: []
24+
```
25+
26+
## Approaches to Solve the Problem
27+
### Approach 1: Brute Force (Inefficient)
28+
##### Intuition:
29+
The brute-force solution involves checking every possible triplet in the array. We would use three nested loops to pick all combinations of three numbers and check if their sum equals zero.
30+
31+
Steps:
32+
1. Loop through the array with three nested loops to check all possible combinations.
33+
2. For each triplet, check if the sum equals zero.
34+
3. Use a set to keep track of unique triplets to avoid duplicates.
35+
##### Time Complexity:
36+
O(n³), where n is the length of the array. This approach checks all possible triplets.
37+
##### Space Complexity:
38+
O(n) for storing unique triplets in a set.
39+
##### Python Code:
40+
```python
41+
def threeSum(nums: List[int]) -> List[List[int]]:
42+
res = set()
43+
n = len(nums)
44+
45+
for i in range(n):
46+
for j in range(i + 1, n):
47+
for k in range(j + 1, n):
48+
if nums[i] + nums[j] + nums[k] == 0:
49+
res.add(tuple(sorted([nums[i], nums[j], nums[k]])))
50+
51+
return list(res)
52+
```
53+
### Approach 2: Sorting and Two Pointers (Optimal Solution)
54+
##### Intuition:
55+
By sorting the array first, we can reduce the problem to the two-sum problem with a twist. The idea is to fix one number (nums[i]), and then use two pointers to find the other two numbers (nums[left] and nums[right]) whose sum is -nums[i].
56+
57+
Steps:
58+
1. Sort the array: Sorting helps in skipping duplicate triplets and allows the two-pointer technique to work efficiently.
59+
2. Iterate through the array: For each number nums[i], treat it as a fixed number.
60+
3. Two-pointer approach: After fixing nums[i], use two pointers (left and right) to find two other numbers such that their sum equals -nums[i].
61+
4. Skip duplicates: While iterating, skip duplicate elements to avoid repeating the same triplets.
62+
##### Visualization:
63+
```perl
64+
For nums = [-1, 0, 1, 2, -1, -4], after sorting the array:
65+
66+
Sorted array: [-4, -1, -1, 0, 1, 2]
67+
68+
Iteration 1:
69+
Fix nums[0] = -4
70+
left = 1, right = 5
71+
Sum = nums[1] + nums[5] = -1 + 2 = 1 > 0 → move right left
72+
73+
Iteration 2:
74+
Fix nums[1] = -1
75+
left = 2, right = 5
76+
Sum = nums[2] + nums[5] = -1 + 2 = 1 > 0 → move right left
77+
Sum = nums[2] + nums[4] = -1 + 1 = 0 → triplet found: [-1, -1, 2]
78+
79+
Iteration 3:
80+
Fix nums[2] = -1 (skip since it’s a duplicate of nums[1])
81+
...
82+
83+
Continue until all triplets are checked.
84+
```
85+
##### Time Complexity:
86+
O(n²), where n is the length of the array. Sorting the array takes O(n log n), and the two-pointer scan for each element takes O(n).
87+
##### Space Complexity:
88+
O(n) for the output list of triplets.
89+
##### Python Code:
90+
```python
91+
def threeSum(nums: List[int]) -> List[List[int]]:
92+
res = []
93+
nums.sort() # Step 1: Sort the array
94+
95+
for i in range(len(nums) - 2):
96+
# Step 2: Skip duplicates for the current number
97+
if i > 0 and nums[i] == nums[i - 1]:
98+
continue
99+
100+
# Step 3: Two-pointer approach
101+
left, right = i + 1, len(nums) - 1
102+
while left < right:
103+
total = nums[i] + nums[left] + nums[right]
104+
105+
if total == 0:
106+
res.append([nums[i], nums[left], nums[right]])
107+
left += 1
108+
right -= 1
109+
# Skip duplicates for the left and right pointers
110+
while left < right and nums[left] == nums[left - 1]:
111+
left += 1
112+
while left < right and nums[right] == nums[right + 1]:
113+
right -= 1
114+
elif total < 0:
115+
left += 1 # Increase the sum by moving left pointer right
116+
else:
117+
right -= 1 # Decrease the sum by moving right pointer left
118+
119+
return res
120+
```
121+
### Approach 3: Hashing (Alternative Approach)
122+
##### Intuition:
123+
Another approach is to use a hash set to store the values we've already seen. For each pair of numbers, calculate the third number needed to make the sum zero and check if it exists in the hash set.
124+
125+
Steps:
126+
1. Sort the array: Sorting helps in identifying and skipping duplicates.
127+
2. Iterate through the array: For each number, use a hash set to check if the complement of the sum exists.
128+
3. Check for duplicates: Ensure the result contains only unique triplets by checking for duplicates.
129+
##### Time Complexity:
130+
O(n²), as we iterate over the array and use hashing for each element.
131+
##### Space Complexity:
132+
O(n), for storing the hash set.
133+
##### Python Code:
134+
```python
135+
def threeSum(nums: List[int]) -> List[List[int]]:
136+
nums.sort()
137+
res = []
138+
139+
for i in range(len(nums)):
140+
if i > 0 and nums[i] == nums[i - 1]:
141+
continue
142+
143+
seen = set()
144+
j = i + 1
145+
while j < len(nums):
146+
complement = -nums[i] - nums[j]
147+
if complement in seen:
148+
res.append([nums[i], nums[j], complement])
149+
while j + 1 < len(nums) and nums[j] == nums[j + 1]:
150+
j += 1
151+
seen.add(nums[j])
152+
j += 1
153+
154+
return res
155+
```
156+
| Approach | Time Complexity | Space Complexity |
157+
|-----------------------------------|-----------------|------------------|
158+
| Brute Force | O(n²) | O(n) |
159+
| Sorting + Two Pointers (Optimal) | O(n²) | O(n) |
160+
| Hashing with Set | O(n²) | O(n) |
161+
162+
The Sorting + Two Pointers approach is the most efficient and optimal solution. It leverages sorting and the two-pointer technique to solve the problem in O(n²) time and handles duplicate triplets efficiently.
Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
# Container With Most Water
2+
You are given an integer array height of length n. There are n vertical lines drawn such that the two endpoints of the i-th line are (i, 0) and (i, height[i]).
3+
4+
Find two lines that together with the x-axis form a container, such that the container contains the most water.
5+
6+
Return the maximum amount of water a container can store.
7+
8+
### Constraints:
9+
- n == height.length
10+
- 2 <= n <= 10^5
11+
- 0 <= height[i] <= 10^4
12+
13+
### Examples
14+
```javascript
15+
Input: height = [1,8,6,2,5,4,8,3,7]
16+
Output: 49
17+
Explanation: The vertical lines are drawn at indices 0, 1, 2, ..., 8.
18+
The two lines that form the container are at indices 1 and 8. The area of the container is (8 - 1) * min(8, 7) = 49.
19+
20+
Input: height = [1,1]
21+
Output: 1
22+
```
23+
24+
## Approaches to Solve the Problem
25+
### Approach 1: Brute Force (Inefficient)
26+
##### Intuition:
27+
A brute-force solution involves checking the area formed by every pair of lines. The area between two lines at positions i and j is given by:
28+
```Area = (j - i) * min(height[i], height[j])```
29+
30+
Steps:
31+
1. Use two nested loops to consider every possible pair of lines.
32+
2. For each pair of lines, calculate the area of water the container can hold.
33+
3. Track the maximum area found.
34+
##### Time Complexity:
35+
O(n²), where n is the length of the array. This approach checks all possible pairs of lines.
36+
##### Space Complexity:
37+
O(1), no extra space is used beyond a few variables.
38+
##### Python Code:
39+
```python
40+
def maxArea(height: List[int]) -> int:
41+
max_area = 0
42+
n = len(height)
43+
44+
for i in range(n):
45+
for j in range(i + 1, n):
46+
area = (j - i) * min(height[i], height[j])
47+
max_area = max(max_area, area)
48+
49+
return max_area
50+
```
51+
### Approach 2: Two Pointers (Optimal Solution)
52+
##### Intuition:
53+
To improve efficiency, we can use the two-pointer technique. The idea is to place one pointer at the beginning (left) and another at the end (right) of the array. Then, we calculate the area and move the pointer that has the smaller height because the width is already maximized, and we want to find a taller line that may increase the area.
54+
55+
1. The area between two lines is determined by the shorter of the two lines and the distance between them.
56+
2. By moving the shorter line inward, we have a chance of finding a taller line that might increase the area.
57+
58+
Steps:
59+
1. Initialize two pointers, left at the beginning and right at the end of the array.
60+
2. While left is less than right, calculate the area between the two lines.
61+
3. Move the pointer pointing to the shorter line inward, because only moving the shorter line could potentially increase the area.
62+
4. Track the maximum area found.
63+
##### Visualization:
64+
```perl
65+
For height = [1,8,6,2,5,4,8,3,7]:
66+
67+
Initial pointers:
68+
left = 0 (height[0] = 1)
69+
right = 8 (height[8] = 7)
70+
71+
Iteration 1:
72+
Area = (8 - 0) * min(1, 7) = 8
73+
Move the left pointer to the right since height[0] < height[8].
74+
75+
Iteration 2:
76+
left = 1 (height[1] = 8), right = 8 (height[8] = 7)
77+
Area = (8 - 1) * min(8, 7) = 49
78+
Move the right pointer to the left since height[8] < height[1].
79+
80+
Continue until the pointers meet.
81+
...
82+
83+
Continue until all triplets are checked.
84+
```
85+
##### Time Complexity:
86+
O(n), where n is the length of the array. We traverse the array once.
87+
##### Space Complexity:
88+
O(1), as we use only two pointers and a few variables.
89+
##### Python Code:
90+
```python
91+
def maxArea(height: List[int]) -> int:
92+
left, right = 0, len(height) - 1
93+
max_area = 0
94+
95+
while left < right:
96+
# Calculate the area between the lines at left and right
97+
width = right - left
98+
current_height = min(height[left], height[right])
99+
area = width * current_height
100+
max_area = max(max_area, area)
101+
102+
# Move the pointer pointing to the shorter line
103+
if height[left] < height[right]:
104+
left += 1
105+
else:
106+
right -= 1
107+
108+
return max_area
109+
```
110+
##### Visualization:
111+
```perl
112+
For height = [1,8,6,2,5,4,8,3,7]:
113+
114+
Step 1:
115+
left = 0, right = 8
116+
Calculate Area = (8 - 0) * min(1, 7) = 8
117+
118+
Step 2:
119+
Move the left pointer to the right, left = 1
120+
Calculate Area = (8 - 1) * min(8, 7) = 49 (New Max)
121+
122+
Step 3:
123+
Move the right pointer to the left, right = 7
124+
Continue calculating and moving pointers until left = right.
125+
126+
Final max area: 49
127+
```
128+
##### Edge Cases
129+
1. Two elements: If the array has only two elements, the solution should handle this case directly, as the only area possible is the product of the two heights and the distance between them.
130+
2. All elements are the same height: If all elements in the array have the same height, the algorithm should return the area as the product of the first and last element's height and their distance.
131+
3. Array with zero heights: The array may contain zeros, and the solution should correctly handle these cases as no water can be trapped between two zero heights.
132+
133+
| Approach | Time Complexity | Space Complexity |
134+
|-----------------------------------|-----------------|------------------|
135+
| Brute Force | O(n²) | O(1) |
136+
| Two Pointers | O(n) | O(1) |
137+
138+
The Two Pointers approach is the most optimal solution as it efficiently reduces the problem to a linear scan, avoiding the need for checking every pair of lines, and uses constant space.

0 commit comments

Comments
 (0)