Skip to content

Commit 6e40826

Browse files
committed
auto commit
1 parent aa407d4 commit 6e40826

File tree

1 file changed

+157
-26
lines changed

1 file changed

+157
-26
lines changed

notes/Leetcode 题解.md

Lines changed: 157 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -54,25 +54,39 @@
5454
## 二分查找
5555

5656
```java
57-
public int search(int key, int[] array) {
58-
int l = 0, h = array.length - 1;
57+
public int binarySearch(int key, int[] nums) {
58+
int l = 0, h = nums.length - 1;
5959
while (l <= h) {
6060
int mid = l + (h - l) / 2;
61-
if (key == array[mid]) return mid;
62-
if (key < array[mid]) h = mid - 1;
61+
if (key == nums[mid]) return mid;
62+
if (key < nums[mid]) h = mid - 1;
6363
else l = mid + 1;
6464
}
6565
return -1;
6666
}
6767
```
6868

69-
实现时需要注意以下细节:
69+
**时间复杂度**
70+
71+
O(logN)
72+
73+
**计算 mid**
74+
75+
在计算 mid 时不能使用 mid = (l + h) / 2 这种方式,因为 l + h 可能会导致加法溢出,应该使用 mid = l + (h - l) / 2。
76+
77+
**计算 h**
7078

71-
- 在计算 mid 时不能使用 mid = (l + h) / 2 这种方式,因为 l + h 可能会导致加法溢出,应该使用 mid = l + (h - l) / 2
79+
当循环条件为 l <= h,则 h = mid - 1。因为如果 h = mid,会出现循环无法退出的情况,例如 l = 1,h = 1,此时 mid 也等于 1,如果此时继续执行 h = mid,那么就会无限循环
7280

73-
- 对 h 的赋值和循环条件有关,当循环条件为 l <= h 时,h = mid - 1;当循环条件为 l < h 时,h = mid。解释如下:在循环条件为 l <= h 时,如果 h = mid,会出现循环无法退出的情况,例如 l = 1,h = 1,此时 mid 也等于 1,如果此时继续执行 h = mid,那么就会无限循环;在循环条件为 l < h,如果 h = mid - 1,会错误跳过查找的数,例如对于数组 [1,2,3],要查找 1,最开始 l = 0,h = 2,mid = 1,判断 key < arr[mid] 执行 h = mid - 1 = 0,此时循环退出,直接把查找的数跳过了。
81+
当循环条件为 l < h,则 h = mid。因为如果 h = mid - 1,会错误跳过查找的数,例如对于数组 [1,2,3],要查找 1,最开始 l = 0,h = 2,mid = 1,判断 key < arr[mid] 执行 h = mid - 1 = 0,此时循环退出,直接把查找的数跳过了。
7482

75-
- l 的赋值一般都为 l = mid + 1。
83+
**返回值**
84+
85+
在循环条件为 l <= h 的情况下,循环退出时 l 总是比 h 大 1,并且 l 是将 key 插入 nums 中的正确位置。例如对于 nums = {0,1,2,3},key = 4,循环退出时 l = 4,将 key 插入到 nums 中的第 4 个位置就能保持 nums 有序的特点。
86+
87+
在循环条件为 l < h 的情况下,循环退出时 l 和 h 相等。
88+
89+
如果只是想知道 key 存不存在,在循环退出之后可以直接返回 -1 表示 key 不存在于 nums 中。
7690

7791
**求开方**
7892

@@ -87,17 +101,19 @@ Output: 2
87101
Explanation: The square root of 8 is 2.82842..., and since we want to return an integer, the decimal part will be truncated.
88102
```
89103

90-
一个数 x 的开方 sqrt 一定在 0 \~ x 之间,并且满足 sqrt == x / sqrt 。可以利用二分查找在 0 \~ x 之间查找 sqrt。
104+
一个数 x 的开方 sqrt 一定在 0 \~ x 之间,并且满足 sqrt == x / sqrt。可以利用二分查找在 0 \~ x 之间查找 sqrt。
105+
106+
对于 x = 8,它的开方是 2.82842...,最后应该返回 2 而不是 3。在循环条件为 l <= h 并且循环退出时,h 总是比 l 小 1,也就是说 h = 2,l = 3,因此最后的返回值应该为 h 而不是 l。
91107

92108
```java
93109
public int mySqrt(int x) {
94-
if(x <= 1) return x;
110+
if (x <= 1) return x;
95111
int l = 1, h = x;
96-
while(l <= h){
112+
while (l <= h) {
97113
int mid = l + (h - l) / 2;
98114
int sqrt = x / mid;
99-
if(sqrt == mid) return mid;
100-
else if(sqrt < mid) h = mid - 1;
115+
if (sqrt == mid) return mid;
116+
if (sqrt < mid) h = mid - 1;
101117
else l = mid + 1;
102118
}
103119
return h;
@@ -120,23 +136,25 @@ Because the 4th row is incomplete, we return 3.
120136

121137
题目描述:第 i 行摆 i 个,统计能够摆的行数。
122138

123-
返回 h 而不是 l,因为摆的硬币最后一行不能算进去。
139+
n 个硬币能够摆的行数 row 在 0 \~ n 之间,并且满足 n == row * (row + 1) / 2,因此可以利用二分查找在 0 \~ n 之间查找 row。
140+
141+
对于 n = 8,它能摆的行数 row = 3,这是因为最后没有摆满的那一行不能算进去,因此在循环退出时应该返回 h。
124142

125143
```java
126144
public int arrangeCoins(int n) {
127145
int l = 0, h = n;
128-
while(l <= h){
129-
int m = l + (h - l) / 2;
130-
long x = m * (m + 1) / 2;
131-
if(x == n) return m;
132-
else if(x < n) l = m + 1;
133-
else h = m - 1;
146+
while (l <= h) {
147+
int mid = l + (h - l) / 2;
148+
long x = mid * (mid + 1) / 2;
149+
if (x == n) return mid;
150+
else if (x < n) l = mid + 1;
151+
else h = mid - 1;
134152
}
135153
return h;
136154
}
137155
```
138156

139-
可以不用二分查找,更直观的解法如下:
157+
本题可以不用二分查找,更直观的解法如下:
140158

141159
```java
142160
public int arrangeCoins(int n) {
@@ -149,6 +167,39 @@ public int arrangeCoins(int n) {
149167
}
150168
```
151169

170+
**大于给定元素的最小元素**
171+
172+
[Leetcode : 744. Find Smallest Letter Greater Than Target (Easy)](https://leetcode.com/problems/find-smallest-letter-greater-than-target/description/)
173+
174+
```html
175+
Input:
176+
letters = ["c", "f", "j"]
177+
target = "d"
178+
Output: "f"
179+
180+
Input:
181+
letters = ["c", "f", "j"]
182+
target = "k"
183+
Output: "c"
184+
```
185+
186+
题目描述:给定一个有序的字符数组 letters 和一个字符 target,要求找出 letters 中大于 target 的最小字符。letters 字符数组是循环数组。
187+
188+
应该注意最后返回的是 l 位置的字符。
189+
190+
```java
191+
public char nextGreatestLetter(char[] letters, char target) {
192+
int n = letters.length;
193+
int l = 0, h = n - 1;
194+
while (l <= h) {
195+
int m = l + (h - l) / 2;
196+
if (letters[m] <= target) l = m + 1;
197+
else h = m - 1;
198+
}
199+
return l < n ? letters[l] : letters[0];
200+
}
201+
```
202+
152203
**有序数组的 Single Element**
153204

154205
[Leetcode : 540. Single Element in a Sorted Array (Medium)](https://leetcode.com/problems/single-element-in-a-sorted-array/description/)
@@ -158,21 +209,101 @@ Input: [1,1,2,3,3,4,4,8,8]
158209
Output: 2
159210
```
160211

161-
题目描述:一个有序数组只有一个数不出现两次,找出这个数。
212+
题目描述:一个有序数组只有一个数不出现两次,找出这个数。要求以 O(logN) 时间复杂度进行求解。
213+
214+
令 key 为 Single Element 在数组中的位置。如果 m 为偶数,并且 m < key,那么 nums[m] == nums[m + 1];m >= key,那么 nums[m] != nums[m + 1]
215+
216+
从上面的规律可以知道,如果 nums[m] == nums[m + 1],那么 key 所在的数组位置为 m + 2 \~ n - 1,此时令 l = m + 2;如果 nums[m] != nums[m + 1],那么 key 所在的数组位置为 0 \~ m,此时令 h = m。
217+
218+
因为 h 的赋值表达式为 h = m,那么循环条件也就只能使用 l < h 这种形式。
162219

163220
```java
164221
public int singleNonDuplicate(int[] nums) {
165222
int l = 0, h = nums.length - 1;
166-
while(l < h) {
223+
while (l < h) {
167224
int m = l + (h - l) / 2;
168-
if(m % 2 == 1) m--; // 保证 l/h/m 都在偶数位,使得查找区间大小一直都是奇数
169-
if(nums[m] == nums[m + 1]) l = m + 2;
225+
if (m % 2 == 1) m--; // 保证 l/h/m 都在偶数位,使得查找区间大小一直都是奇数
226+
if (nums[m] == nums[m + 1]) l = m + 2;
170227
else h = m;
171228
}
172229
return nums[l];
173230
}
174231
```
175232

233+
**第一个错误的版本**
234+
235+
[Leetcode : 278. First Bad Version (Easy)](https://leetcode.com/problems/first-bad-version/description/)
236+
237+
题目描述:给定一个元素 n 代表有 [1, 2, ..., n] 版本,可以调用 isBadVersion(int x) 知道某个版本是否错误,要求找到第一个错误的版本。
238+
239+
如果第 m 个版本出错,则表示第一个错误的版本在 1 \~ m 之前,令 h = m;否则第一个错误的版本在 m + 1 \~ n 之间,令 l = m + 1。
240+
241+
因为 h 的赋值表达式为 h = m,因此循环条件为 l < h。
242+
243+
```java
244+
public int firstBadVersion(int n) {
245+
int l = 1, h = n;
246+
while (l < h) {
247+
int m = l + (h - l) / 2;
248+
if (isBadVersion(m)) h = m;
249+
else l = m + 1;
250+
}
251+
return l;
252+
}
253+
```
254+
255+
**旋转数组的最小数字**
256+
257+
[Leetcode : 153. Find Minimum in Rotated Sorted Array (Medium)](https://leetcode.com/problems/find-minimum-in-rotated-sorted-array/description/)
258+
259+
```html
260+
Input: [3,4,5,1,2],
261+
Output: 1
262+
```
263+
264+
```java
265+
public int findMin(int[] nums) {
266+
int l = 0, h = nums.length;
267+
while (l < h) {
268+
int m = l + (h - l) / 2;
269+
if (nums[m] <= nums[h]) h = m;
270+
else l = m + 1;
271+
}
272+
return nums[l];
273+
}
274+
```
275+
276+
**查找区间**
277+
278+
[Leetcode : 34. Search for a Range (Medium)](https://leetcode.com/problems/search-for-a-range/description/)
279+
280+
```html
281+
Input: nums = [5,7,7,8,8,10], target = 8
282+
Output: [3,4]
283+
284+
Input: nums = [5,7,7,8,8,10], target = 6
285+
Output: [-1,-1]
286+
```
287+
288+
```java
289+
public int[] searchRange(int[] nums, int target) {
290+
int first = binarySearch(nums, target);
291+
int last = binarySearch(nums, target + 1) - 1;
292+
if (first == nums.length || nums[first] != target) return new int[]{-1, -1};
293+
return new int[]{first, Math.max(first, last)};
294+
}
295+
296+
private int binarySearch(int[] nums, int target) {
297+
int l = 0, h = nums.length; // 注意 h 的初始值
298+
while (l < h) {
299+
int m = l + (h - l) / 2;
300+
if (nums[m] >= target) h = m;
301+
else l = m + 1;
302+
}
303+
return l;
304+
}
305+
```
306+
176307
## 贪心思想
177308

178309
贪心思想保证每次操作都是局部最优的,并且最后得到的结果是全局最优的。
@@ -185,7 +316,7 @@ public int singleNonDuplicate(int[] nums) {
185316
Input: [1,2], [1,2,3]
186317
Output: 2
187318

188-
Explanation: You have 2 children and 3 cookies. The greed factors of 2 children are 1, 2.
319+
Explanation: You have 2 children and 3 cookies. The greed factors of 2 children are 1, 2.
189320
You have 3 cookies and their sizes are big enough to gratify all of the children,
190321
You need to output 2.
191322
```

0 commit comments

Comments
 (0)