Skip to content

Commit 9631577

Browse files
committed
2 parents bdf5443 + a58ff84 commit 9631577

File tree

4 files changed

+544
-0
lines changed

4 files changed

+544
-0
lines changed

docs/NextJs/image.png

-42.3 KB
Loading
Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
---
2+
id: find-first-and-last-position-of-element-in-sorted-array
3+
title: Find First and Last Position of Element in Sorted Array(LeetCode)
4+
sidebar_label: 0034-Find First and Last Position of Element in Sorted Array
5+
tags:
6+
- Array
7+
- Binary Search
8+
description: Given an array of integers nums sorted in non-decreasing order, find the starting and ending position of a given target value.
9+
sidebar_position: 34
10+
---
11+
12+
## Problem Statement
13+
14+
Given an array of integers `nums` sorted in non-decreasing order, find the starting and ending position of a given `target` value.
15+
16+
If `target` is not found in the array, return [-1, -1].
17+
18+
You must write an algorithm with O(log n) runtime complexity.
19+
20+
### Examples
21+
22+
**Example 1:**
23+
24+
```plaintext
25+
Input: nums = [5,7,7,8,8,10], target = 8
26+
Output: [3,4]
27+
```
28+
29+
**Example 2:**
30+
31+
```plaintext
32+
Input: nums = [5,7,7,8,8,10], target = 6
33+
Output: [-1,-1]
34+
```
35+
36+
**Example 3:**
37+
38+
```plaintext
39+
Input: nums = [], target = 0
40+
Output: [-1,-1]
41+
```
42+
43+
### Constraints
44+
45+
- `0 <= nums.length <= 105`
46+
- `109 <= nums[i] <= 109`
47+
- `nums` is a non-decreasing array.
48+
- `109 <= target <= 109`
49+
50+
## Solution
51+
52+
When solving the problem of finding the starting and ending position of a target value in a sorted array, we can use
53+
two main approaches: Linear Search (Brute Force) and Binary Search (Optimized). Below, both approaches are explained along with their time and space complexities.
54+
55+
### Approach 1: Linear Search (Brute Force)
56+
57+
#### Explanation:
58+
59+
1. Traverse the array from the beginning to find the first occurrence of the target.
60+
2. Traverse the array from the end to find the last occurrence of the target.
61+
3. Return the indices of the first and last occurrences.
62+
63+
#### Algorithm
64+
65+
1. Initialize `startingPosition` and `endingPosition` to -1.
66+
2. Loop through the array from the beginning to find the first occurrence of the target and set `startingPosition`.
67+
3. Loop through the array from the end to find the last occurrence of the target and set `endingPosition`.
68+
4. Return `{startingPosition, endingPosition}`.
69+
70+
#### Implementation
71+
72+
```C++
73+
class Solution {
74+
public:
75+
vector<int> searchRange(vector<int>& nums, int target) {
76+
int startingPosition = -1, endingPosition = -1;
77+
int n = nums.size();
78+
for(int i = 0; i < n; i++) {
79+
if(nums[i] == target) {
80+
startingPosition = i;
81+
break;
82+
}
83+
}
84+
for(int i = n - 1; i >= 0; i--) {
85+
if(nums[i] == target) {
86+
endingPosition = i;
87+
break;
88+
}
89+
}
90+
return {startingPosition, endingPosition};
91+
}
92+
};
93+
```
94+
95+
### Complexity Analysis
96+
97+
- **Time complexity**: O(N), where N is the size of the array. In the worst case, we might traverse all elements of the array.
98+
- **Space complexity**: O(1), as we are using a constant amount of extra space.
99+
100+
### Approach 2: Binary Search (Optimized)
101+
102+
#### Explanation:
103+
104+
1. Use binary search to find the lower bound (first occurrence) of the target.
105+
2. Use binary search to find the upper bound (last occurrence) of the target by searching for the target + 1 and subtracting 1 from the result.
106+
3. Check if the target exists in the array and return the indices of the first and last occurrences.
107+
108+
#### Algorithm
109+
110+
1. Define a helper function `lower_bound` to find the first position where the target can be inserted.
111+
2. Use `lower_bound` to find the starting position of the target.
112+
3. Use `lower_bound` to find the position where `target + 1` can be inserted, then subtract 1 to get the ending position.
113+
4. Check if `startingPosition` is within bounds and equals the target.
114+
5. Return `{startingPosition, endingPosition}` if the target is found, otherwise return `{-1, -1}`.
115+
116+
#### Implementation (First Version)
117+
118+
```C++
119+
class Solution {
120+
private:
121+
int lower_bound(vector<int>& nums, int low, int high, int target) {
122+
while(low <= high) {
123+
int mid = (low + high) >> 1;
124+
if(nums[mid] < target) {
125+
low = mid + 1;
126+
} else {
127+
high = mid - 1;
128+
}
129+
}
130+
return low;
131+
}
132+
public:
133+
vector<int> searchRange(vector<int>& nums, int target) {
134+
int low = 0, high = nums.size() - 1;
135+
int startingPosition = lower_bound(nums, low, high, target);
136+
int endingPosition = lower_bound(nums, low, high, target + 1) - 1;
137+
if(startingPosition < nums.size() && nums[startingPosition] == target) {
138+
return {startingPosition, endingPosition};
139+
}
140+
return {-1, -1};
141+
}
142+
};
143+
```
144+
#### Implementation (Second Version)
145+
146+
```C++
147+
class Solution {
148+
public:
149+
vector<int> searchRange(vector<int>& nums, int target) {
150+
int startingPosition = lower_bound(nums.begin(), nums.end(), target) - nums.begin();
151+
int endingPosition = lower_bound(nums.begin(), nums.end(), target + 1) - nums.begin() - 1;
152+
if(startingPosition < nums.size() && nums[startingPosition] == target) {
153+
return {startingPosition, endingPosition};
154+
}
155+
return {-1, -1};
156+
}
157+
};
158+
```
159+
160+
### Complexity Analysis
161+
162+
- **Time complexity**: O(log N), where N is the size of the array. We perform binary search, which has a logarithmic time complexity.
163+
- **Space complexity**: O(1), as we are using a constant amount of extra space.
164+
165+
### Conclusion
166+
167+
1. Linear Search is straightforward but less efficient with a time complexity of O(N).
168+
2. Binary Search is more efficient with a time complexity of O(log N), making it suitable for large datasets.
169+
Both approaches provide a clear way to find the starting and ending positions of a target value in a sorted array, with the Binary Search approach being the
170+
optimized solution.
Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,194 @@
1+
---
2+
id: combination-sum
3+
title: Combination Sum(LeetCode)
4+
sidebar_label: 0039-Combination Sum
5+
tags:
6+
- Array
7+
- Backtracking
8+
description: Given an array of distinct integers candidates and a target integer target, return a list of all unique combinations of candidates where the chosen numbers sum to target.
9+
sidebar_position: 39
10+
---
11+
12+
## Problem Statement
13+
14+
Given an array of distinct integers `candidates` and a target integer `target`, return a list of all unique combinations of `candidates` where the chosen numbers
15+
sum to `target`. You may return the combinations in any order.
16+
17+
The same number may be chosen from `candidates` an unlimited number of times. Two combinations are unique if the
18+
frequency
19+
of at least one of the chosen numbers is different.
20+
21+
The test cases are generated such that the number of unique combinations that sum up to `target` is less than `150` combinations for the given input.
22+
23+
### Examples
24+
25+
**Example 1:**
26+
27+
```plaintext
28+
Input: candidates = [2,3,6,7], target = 7
29+
Output: [[2,2,3],[7]]
30+
Explanation:
31+
2 and 3 are candidates, and 2 + 2 + 3 = 7. Note that 2 can be used multiple times.
32+
7 is a candidate, and 7 = 7.
33+
These are the only two combinations.
34+
```
35+
36+
**Example 2:**
37+
38+
```plaintext
39+
Input: candidates = [2,3,5], target = 8
40+
Output: [[2,2,2,2],[2,3,3],[3,5]]
41+
```
42+
43+
**Example 3:**
44+
45+
```plaintext
46+
Input: candidates = [2], target = 1
47+
Output: []
48+
```
49+
50+
### Constraints
51+
52+
- `1 <= candidates.length <= 30`
53+
- `2 <= candidates[i] <= 40`
54+
- All elements of `candidates` are distinct.
55+
- `1 <= target <= 40`
56+
57+
## Solution
58+
59+
The Combination Sum problem involves finding all unique combinations in a list of candidates where the candidate numbers sum to a given target. Each number in the list may be used an
60+
unlimited number of times. Below, we discuss three approaches to solve this problem: DFS (Backtracking), Dynamic Programming (Slow), and Dynamic Programming (Fast).
61+
62+
### Approach 1: DFS (Backtracking)
63+
64+
#### Explanation:
65+
66+
1. Use a recursive function to explore all possible combinations.
67+
2. Track the current combination (`cur`) and its sum (`cur_sum`).
68+
3. If `cur_sum` exceeds the target, backtrack.
69+
4. If `cur_sum` equals the target, add the current combination to the result list.
70+
71+
#### Algorithm
72+
73+
1. Initialize an empty list `ans` to store the results.
74+
2. Define a recursive function `dfs(cur, cur_sum, idx)` to explore combinations.
75+
3. In `dfs`, if `cur_sum` exceeds the target, return.
76+
4. If `cur_sum` equals the target, add `cur` to `ans` and return.
77+
5. Loop through candidates starting from `idx`, and recursively call `dfs` with updated `cur` and `cur_sum`.
78+
6. Start the DFS with an empty combination, a sum of 0, and starting index 0.
79+
7. Return the list `ans`.
80+
81+
#### Implementation
82+
83+
```python
84+
class Solution:
85+
def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]:
86+
ans = []
87+
n = len(candidates)
88+
def dfs(cur, cur_sum, idx):
89+
if cur_sum > target: return
90+
if cur_sum == target:
91+
ans.append(cur)
92+
return
93+
for i in range(idx, n):
94+
dfs(cur + [candidates[i]], cur_sum + candidates[i], i)
95+
dfs([], 0, 0)
96+
return ans
97+
```
98+
99+
### Complexity Analysis
100+
101+
- **Time complexity**: O(N^(M/min_cand + 1)), where N is the number of candidates, M is the target, and min_cand is the
102+
smallest candidate.
103+
- **Space complexity**: O(M/min_cand), as the recursion depth can go up to the target divided by the smallest
104+
candidate.
105+
106+
### Approach 2: Dynamic Programming (Slow)
107+
108+
#### Explanation:
109+
110+
1. Use a list `dp` where `dp[i]` contains combinations that sum up to `i`.
111+
2. Build the `dp` list by iterating through all possible sums up to the target.
112+
3. For each sum, update the combinations by considering each candidate.
113+
114+
#### Algorithm
115+
116+
1. Create a dictionary `idx_d` to map each candidate to its index.
117+
2. Initialize a list `dp` with empty lists for all sums from 0 to the target.
118+
3. For each sum `i` from 1 to the target:
119+
* Iterate through all previous sums `j` from 0 to `i-1`.
120+
* For each combination in `dp[j]`, update combinations in `dp[i]`.
121+
* Add combinations directly from candidates if they equal `i`.
122+
4. Return the list `dp[-1]`.
123+
124+
#### Implementation
125+
126+
```python
127+
class Solution:
128+
def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]:
129+
idx_d = {val: idx for idx, val in enumerate(candidates)}
130+
n = len(candidates)
131+
dp = [[] for _ in range(target + 1)]
132+
for i in range(1, target + 1):
133+
for j in range(i):
134+
for comb in dp[j]:
135+
start_idx = idx_d[comb[-1]]
136+
for val in candidates[start_idx:]:
137+
if val + j == i:
138+
dp[i].append(comb + [val])
139+
for candidate in candidates:
140+
if candidate == i:
141+
dp[i].append([candidate])
142+
return dp[-1]
143+
```
144+
145+
### Complexity Analysis
146+
147+
- **Time complexity**: O(MMM*N), where N is the number of candidates and M is the target.
148+
- **Space complexity**: O(M*M), due to the storage of combinations for each sum up to the target.
149+
150+
### Approach 3: Dynamic Programming (Fast)
151+
152+
#### Explanation:
153+
154+
1. Use a list `dp` where `dp[i]` contains combinations that sum up to `i`.
155+
2. For each candidate, update the combinations for all possible sums up to the target.
156+
157+
#### Algorithm
158+
159+
1. Initialize a list `dp` with empty lists for all sums from 0 to the target.
160+
2. For each candidate `c`:
161+
3. For each sum `i` from `c` to the target:
162+
* If `i` equals `c`, add `[c]` to `dp[i]`.
163+
* For each combination in `dp[i - c]`, add `comb + [c]` to `dp[i]`.
164+
4. Return the list `dp[-1]`.
165+
166+
#### Implementation
167+
168+
```python
169+
class Solution:
170+
def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]:
171+
dp = [[] for _ in range(target + 1)]
172+
for c in candidates:
173+
for i in range(c, target + 1):
174+
if i == c:
175+
dp[i].append([c])
176+
for comb in dp[i - c]:
177+
dp[i].append(comb + [c])
178+
return dp[-1]
179+
```
180+
181+
### Complexity Analysis
182+
183+
- **Time complexity**: O(MMN), where N is the number of candidates and M is the target.
184+
- **Space complexity**: O(M*M), due to the storage of combinations for each sum up to the target.
185+
186+
### Conclusion
187+
188+
In solving the Combination Sum problem:
189+
190+
1. DFS (Backtracking) is simple and intuitive but can be slow for large inputs due to its exponential time complexity.
191+
2. Dynamic Programming (Slow) uses a structured approach but has higher time complexity (O(MMM*N)) and memory usage.
192+
3. Dynamic Programming (Fast) is more efficient (O(MMN)), making it suitable for larger inputs, though it still uses significant memory.
193+
194+
Choose DFS (Backtracking) for smaller datasets, DP (Slow) for a structured approach within manageable limits, and DP (Fast) for larger datasets where efficiency is critical.

0 commit comments

Comments
 (0)