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

Lines changed: 1 addition & 0 deletions
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

Lines changed: 1 addition & 0 deletions
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

Lines changed: 2 additions & 1 deletion
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

Lines changed: 1 addition & 0 deletions
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)
Lines changed: 89 additions & 0 deletions
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+
}
Lines changed: 129 additions & 0 deletions
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

Lines changed: 5 additions & 11 deletions
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

Lines changed: 1 addition & 0 deletions
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)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy