Skip to content

Commit dd9d325

Browse files
committed
feat: add searching-ii algorithm
1 parent bb39a5c commit dd9d325

File tree

8 files changed

+229
-12
lines changed

8 files changed

+229
-12
lines changed

README.md

+1
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
### 查找算法
4545

4646
1. [二分查找](/basic/searching/BinarySearch/README.md)
47+
1. [二分查找 II](/basic/searching/BinarySearch-II/README.md)
4748

4849
## 面试高频考题
4950

README_EN.md

+1
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ Complete solutions to [LeetCode](https://leetcode-cn.com/problemset/all/), [LCOF
4444
### Searching
4545

4646
1. [Binary Search](/basic/searching/BinarySearch/README.md)
47+
1. [Binary Search II](/basic/searching/BinarySearch-II/README.md)
4748

4849
## High Frequency Interview Questions
4950

basic/README.md

+2-1
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,5 @@
1010

1111
## 查找算法
1212

13-
- [Binary Search](./searching/BinarySearch/README.md)
13+
- [二分查找](./searching/BinarySearch/README.md)
14+
- [二分查找 II](./searching/BinarySearch-II/README.md)

basic/README_EN.md

+1
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,4 @@
1111
## Searching
1212

1313
- [Binary Search](./searching/BinarySearch/README.md)
14+
- [Binary Search II](./searching/BinarySearch-II/README.md)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
public class BinarySearch {
2+
3+
public static int search1(int[] nums, int val) {
4+
int n = nums.length;
5+
int low = 0, high = n - 1;
6+
while (low <= high) {
7+
int mid = low + ((high - low) >> 1);
8+
if (nums[mid] < val) {
9+
low = mid + 1;
10+
} else if (nums[mid] > val) {
11+
high = mid - 1;
12+
} else {
13+
// 如果nums[mid]是第一个元素,或者nums[mid-1]不等于val
14+
// 说明nums[mid]就是第一个值为给定值的元素
15+
if (mid == 0 || nums[mid - 1] != val) {
16+
return mid;
17+
}
18+
high = mid - 1;
19+
}
20+
}
21+
return -1;
22+
}
23+
24+
public static int search2(int[] nums, int val) {
25+
int n = nums.length;
26+
int low = 0, high = n - 1;
27+
while (low <= high) {
28+
int mid = low + ((high - low) >> 1);
29+
if (nums[mid] < val) {
30+
low = mid + 1;
31+
} else if (nums[mid] > val) {
32+
high = mid - 1;
33+
} else {
34+
// 如果nums[mid]是最后一个元素,或者nums[mid+1]不等于val
35+
// 说明nums[mid]就是最后一个值为给定值的元素
36+
if (mid == n - 1 || nums[mid + 1] != val) {
37+
return mid;
38+
}
39+
low = mid + 1;
40+
}
41+
}
42+
return -1;
43+
}
44+
45+
public static int search3(int[] nums, int val) {
46+
int low = 0, high = nums.length - 1;
47+
while (low <= high) {
48+
int mid = low + ((high - low) >> 1);
49+
if (nums[mid] < val) {
50+
low = mid + 1;
51+
} else {
52+
// 如果nums[mid]是第一个元素,或者nums[mid-1]小于val
53+
// 说明nums[mid]就是第一个大于等于给定值的元素
54+
if (mid == 0 || nums[mid - 1] < val) {
55+
return mid;
56+
}
57+
high = mid - 1;
58+
}
59+
}
60+
return -1;
61+
}
62+
63+
public static int search4(int[] nums, int val) {
64+
int n = nums.length;
65+
int low = 0, high = n - 1;
66+
while (low <= high) {
67+
int mid = low + ((high - low) >> 1);
68+
if (nums[mid] > val) {
69+
high = mid - 1;
70+
} else {
71+
// 如果nums[mid]是最后一个元素,或者nums[mid+1]大于val
72+
// 说明nums[mid]就是最后一个小于等于给定值的元素
73+
if (mid == n - 1 || nums[mid + 1] > val) {
74+
return mid;
75+
}
76+
low = mid + 1;
77+
}
78+
}
79+
return -1;
80+
}
81+
82+
public static void main(String[] args) {
83+
int[] nums = {1, 2, 2, 2, 2, 8, 9};
84+
System.out.println(search1(nums, 2)); // 1
85+
System.out.println(search2(nums, 2)); // 4
86+
System.out.println(search3(nums, 2)); // 1
87+
System.out.println(search4(nums, 2)); // 4
88+
}
89+
}
+129
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
# 二分查找 II
2+
3+
前面讲的二分查找算法,是最为简单的一种,在不存在重复元素的有序数组中,查找值等于给定值的元素。
4+
5+
接下来,我们来看看二分查找算法四种常见的变形问题,分别是:
6+
7+
1. 查找第一个值等于给定值的元素
8+
1. 查找最后一个值等于给定值的元素
9+
1. 查找第一个大于等于给定值的元素
10+
1. 查找最后一个小于等于给定值的元素
11+
12+
## 1. 查找第一个值等于给定值的元素
13+
14+
<!-- tabs:start -->
15+
16+
### **Java**
17+
18+
```java
19+
public static int search(int[] nums, int val) {
20+
int n = nums.length;
21+
int low = 0, high = n - 1;
22+
while (low <= high) {
23+
int mid = low + ((high - low) >> 1);
24+
if (nums[mid] < val) {
25+
low = mid + 1;
26+
} else if (nums[mid] > val) {
27+
high = mid - 1;
28+
} else {
29+
// 如果nums[mid]是第一个元素,或者nums[mid-1]不等于val
30+
// 说明nums[mid]就是第一个值为给定值的元素
31+
if (mid == 0 || nums[mid - 1] != val) {
32+
return mid;
33+
}
34+
high = mid - 1;
35+
}
36+
}
37+
return -1;
38+
}
39+
```
40+
41+
<!-- tabs:end -->
42+
43+
## 2. 查找最后一个值等于给定值的元素
44+
45+
<!-- tabs:start -->
46+
47+
### **Java**
48+
49+
```java
50+
public static int search(int[] nums, int val) {
51+
int n = nums.length;
52+
int low = 0, high = n - 1;
53+
while (low <= high) {
54+
int mid = low + ((high - low) >> 1);
55+
if (nums[mid] < val) {
56+
low = mid + 1;
57+
} else if (nums[mid] > val) {
58+
high = mid - 1;
59+
} else {
60+
// 如果nums[mid]是最后一个元素,或者nums[mid+1]不等于val
61+
// 说明nums[mid]就是最后一个值为给定值的元素
62+
if (mid == n - 1 || nums[mid + 1] != val) {
63+
return mid;
64+
}
65+
low = mid + 1;
66+
}
67+
}
68+
return -1;
69+
}
70+
```
71+
72+
<!-- tabs:end -->
73+
74+
## 3. 查找第一个大于等于给定值的元素
75+
76+
<!-- tabs:start -->
77+
78+
### **Java**
79+
80+
```java
81+
public static int search(int[] nums, int val) {
82+
int low = 0, high = nums.length - 1;
83+
while (low <= high) {
84+
int mid = low + ((high - low) >> 1);
85+
if (nums[mid] < val) {
86+
low = mid + 1;
87+
} else {
88+
// 如果nums[mid]是第一个元素,或者nums[mid-1]小于val
89+
// 说明nums[mid]就是第一个大于等于给定值的元素
90+
if (mid == 0 || nums[mid - 1] < val) {
91+
return mid;
92+
}
93+
high = mid - 1;
94+
}
95+
}
96+
return -1;
97+
}
98+
```
99+
100+
<!-- tabs:end -->
101+
102+
## 4. 查找最后一个小于等于给定值的元素
103+
104+
<!-- tabs:start -->
105+
106+
### **Java**
107+
108+
```java
109+
public static int search(int[] nums, int val) {
110+
int n = nums.length;
111+
int low = 0, high = n - 1;
112+
while (low <= high) {
113+
int mid = low + ((high - low) >> 1);
114+
if (nums[mid] > val) {
115+
high = mid - 1;
116+
} else {
117+
// 如果nums[mid]是最后一个元素,或者nums[mid+1]大于val
118+
// 说明nums[mid]就是最后一个小于等于给定值的元素
119+
if (mid == n - 1 || nums[mid + 1] > val) {
120+
return mid;
121+
}
122+
low = mid + 1;
123+
}
124+
}
125+
return -1;
126+
}
127+
```
128+
129+
<!-- tabs:end -->

basic/searching/BinarySearch/README.md

+5-11
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,15 @@
22

33
二分查找是一种非常高效的查找算法,高效到什么程度呢?我们来分析一下它的时间复杂度。
44

5-
我们假设数据大小是 n,每次查找后数据都会缩小为原来的一半,也就是会除以 2。最坏情况下,直到查找区间被缩小为空,才停止。被查找区间的大小变化:
5+
假设数据大小是 n,每次查找后数据都会缩小为原来的一半,也就是会除以 2。最坏情况下,直到查找区间被缩小为空,才停止。
6+
7+
被查找区间的大小变化为:
68

79
```
810
n, n/2, n/4, n/8, ..., n/(2^k)
911
```
1012

11-
可以看出来,这是一个等比数列。其中 n/(2^k)=1 时,k 的值就是总共缩小的次数。而每一次缩小操作只涉及两个数据的大小比较,所以,经过了 k 次区间缩小操作,时间复杂度就是 O(k)。通过 n/(2^k)=1,我们可以求得 k=log2n,所以时间复杂度就是 O(logn)。
13+
可以看出来,这是一个等比数列。其中 `n/(2^k)=1` 时,k 的值就是总共缩小的次数。而每一次缩小操作只涉及两个数据的大小比较,所以,经过了 k 次区间缩小操作,时间复杂度就是 O(k)。通过 `n/(2^k)=1`,我们可以求得 `k=log2n`,所以时间复杂度就是 O(logn)。
1214

1315
## 代码示例
1416

@@ -58,10 +60,6 @@ public class BinarySearch {
5860
// 非递归查找
5961
int r1 = search(nums, 7);
6062
System.out.println(r1);
61-
62-
// 递归查找
63-
int r2 = search(nums, 7);
64-
System.out.println(r2);
6563
}
6664
}
6765
```
@@ -99,12 +97,8 @@ public class BinarySearch {
9997
public static void main(String[] args) {
10098
int[] nums = {1, 2, 5, 7, 8, 9};
10199

102-
// 非递归查找
103-
int r1 = search(nums, 7);
104-
System.out.println(r1);
105-
106100
// 递归查找
107-
int r2 = search(nums, 7);
101+
int r2 = searchRecursive(nums, 7);
108102
System.out.println(r2);
109103
}
110104
}

basic/summary.md

+1
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,4 @@
77
- [快速排序](/basic/sorting/QuickSort/README.md)
88
- 查找算法
99
- [二分查找](/basic/searching/BinarySearch/README.md)
10+
- [二分查找 II](/basic/searching/BinarySearch-II/README.md)

0 commit comments

Comments
 (0)