Skip to content

Commit a6c25ea

Browse files
committed
81.搜索旋转排序数组II,二分查找
1 parent 630aa85 commit a6c25ea

File tree

4 files changed

+62
-19
lines changed

4 files changed

+62
-19
lines changed

leetcode_Java/DoneTitle.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
77. 组合
4747
78. 子集
4848
79. 单词搜索
49+
81. 搜索旋转排序数组 II
4950
82. 删除排序链表中的重复元素 II
5051
83. 删除排序链表中的重复元素
5152
84. 柱状图中最大的矩形

leetcode_Java/DoneType.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,7 @@
170170
56. 合并区间(二维数组排序)
171171
59. 螺旋矩阵 II(四指针)
172172
75. 颜色分类(单指针,双指针)
173+
81. 搜索旋转排序数组 II(二分查找)
173174
88. 合并两个有序数组(排序,双指针)
174175
128. 最长连续序列(集合,排序)
175176
136. 只出现一次的数字(哈希表,列表,位运算)

leetcode_Java/Solution0033.java

Lines changed: 17 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@ public int search(int[] nums, int target) {
2121
二分查找:时间复杂度O(logn)
2222
1、左右索引求和除以2,得到中点索引,如果中点索引对应值为目标值,则返回中点索引
2323
2、在常规二分查找的时候查看当前 mid 为分割位置分割出来的两个部分 [l, mid-1] 和 [mid+1, r] 哪个部分是有序的,并判断 target 是否在有序的部分,从而决定改变二分查找的上下界
24-
如果 [l, mid-1] 是有序数组,且 target 的大小满足 nums[left] <= target < nums[mid] ,则到 [l, mid-1]寻找,否则在 [mid+1, r] 寻找
25-
如果 [mid+1, r] 是有序数组,且 target 的大小满足 nums[mid] < target <= nums[right],则到 [mid+1, r]寻找,否则在 [l, mid-1] 寻找
24+
如果 nums[left] < nums[mid],那么左半部分 [l, mid-1] 是有序数组。如果 target 的大小满足 nums[left] <= target < nums[mid] ,则到 [l, mid-1]寻找,否则在 [mid+1, r] 寻找
25+
如果 nums[left] > nums[mid],那么右半部分 [mid+1, r] 是有序数组。如果 target 的大小满足 nums[mid] < target <= nums[right],则到 [mid+1, r]寻找,否则在 [l, mid-1] 寻找
2626
*/
2727
class Solution {
2828
public int search(int[] nums, int target) {
@@ -34,28 +34,26 @@ public int search(int[] nums, int target) {
3434
return nums[0] == target ? 0 : -1;
3535
}
3636
int left = 0, right = n - 1;
37-
// 可能最后只剩下一个数要判断,所以需要等号
38-
while (left <= right) {
39-
int mid = (left + right) / 2;
40-
if (nums[mid] == target) {
37+
while (left <= right) { // 可能最后只剩下一个数要判断,所以需要等号
38+
int mid = (left + right) / 2; // (left + right + 1) / 2 也可以,因为中值判断过不满足就舍弃,所以不会存在重复中点死循环问题
39+
if (nums[mid] == target) { // 判断中点是否为目标值,是则返回中点
4140
return mid;
4241
}
43-
// 当剩下两个数时,left=mid,由于上面nums[mid]=nums[left]已经校验过不等于target,所以当前数要舍弃,期望左指针右移,所以需要等号,使if条件成立进入里面的逻辑
44-
if (nums[left] <= nums[mid]) { // 左半部分有序
45-
// nums[left]还没判断过,所以需要等号。上面nums[mid]单独判断过了,所以不用等号。
46-
if (nums[left] <= target && target < nums[mid]) {
47-
right = mid - 1;
48-
} else {
49-
left = mid + 1;
42+
// 当剩下两个数时,中点落在左边,由于上面已经校验过中值不等于目标值,所以当前数要舍弃,期望左指针右移,所以需要等号,使if条件成立进入里面的逻辑,让左指针右移
43+
if (nums[left] <= nums[mid]) { // 左半部分有序
44+
if (nums[left] <= target && target < nums[mid]) { // 目标值在左半部分。[left,mid) 因为左值还没判断,中值已经判断过了
45+
right = mid - 1; // 收缩右区间,中值已经判断过不满足了,不需要包含
46+
} else { // 目标值在右半部分
47+
left = mid + 1; // 收缩左区间,中值已经判断过不满足了,不需要包含
5048
}
51-
} else { // 右半部分有序
52-
if (nums[mid] < target && target <= nums[right]) {
53-
left = mid + 1;
54-
} else {
55-
right = mid - 1;
49+
} else { // 右半部分有序
50+
if (nums[mid] < target && target <= nums[right]) { // 目标值在右半部分。(mid,right] 因为右值还没判断,中值已经判断过了
51+
left = mid + 1; // 收缩左区间,中值已经判断过不满足了,不需要包含
52+
} else { // 目标值在左半部分
53+
right = mid - 1; // 收缩右区间,中值已经判断过不满足了,不需要包含
5654
}
5755
}
5856
}
59-
return -1;
57+
return -1; // 找不到返回-1
6058
}
6159
}

leetcode_Java/Solution0081.java

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
// 81. 搜索旋转排序数组 II
2+
3+
4+
/*
5+
1、二分查找,比“33. 搜索旋转排序数组”多了重复元素
6+
2、二分的本质是「二段性」,并非单调性。只要一段满足某个性质,另外一段不满足某个性质,就可以用「二分」,再利用性质去判断选择收缩区间
7+
3、分类讨论举例
8+
1)第一类:1011110111 和 1110111101。此种情况下 nums[start] == nums[mid],分不清到底是前面有序还是后面有序,此时 left++ 即可,相当于去掉一个重复的干扰项
9+
2)第二类:2345671。也就是 nums[left] < nums[mid],此例子中就是 2 < 5,这种情况下,左半部分有序
10+
因此如果 nums[left] <= target < nums[mid],则在左半部分找,否则去右半部分找
11+
3)第三类:6712345。也就是 nums[left] > nums[mid],此例子中就是 6 > 2,这种情况下,右半部分有序
12+
因此如果 nums[mid] < target <= nums[right],则在右半部分找,否则去前半部分找
13+
*/
14+
class Solution {
15+
public boolean search(int[] nums, int target) {
16+
int n = nums.length;
17+
int left = 0, right = n - 1;
18+
while (left <= right) { // 可能剩下最后一个数,也要判断是否为目标值,所以要等号
19+
int mid = (left + right) / 2; // (left + right + 1) / 2 也可以,因为中点值判断过不满足就舍弃,所以不会存在重复中点死循环问题
20+
if (nums[mid] == target) { // 判断中点是否为目标值,是则返回true
21+
return true;
22+
}
23+
if (nums[left] == nums[mid]) { // 由于存在重复元素,左值与中值相等时,无法区分二段性,让左指针右移,去掉一个重复的干扰项
24+
left++;
25+
continue;
26+
}
27+
if (nums[left] < nums[mid]) { // 左半部分有序。等号的情况上面已经判断了,这里就不用等号了
28+
if (nums[left] <= target && target < nums[mid]) { // 目标值在左半部分。[left,mid) 因为左值还没判断,中值已经判断过了
29+
right = mid - 1; // 收缩右区间,中值已经判断过不满足了,不需要包含
30+
} else { // 目标值在右半部分
31+
left = mid + 1; // 收缩左区间,中值已经判断过不满足了,不需要包含
32+
}
33+
} else { // 右半部分有序
34+
if (nums[mid] < target && target <= nums[right]) { // 目标值在右半部分。(mid,right] 因为右值还没判断,中值已经判断过了
35+
left = mid + 1; // 收缩左区间,中值已经判断过不满足了,不需要包含
36+
} else { // 目标值在左半部分
37+
right = mid - 1; // 收缩右区间,中值已经判断过不满足了,不需要包含
38+
}
39+
}
40+
}
41+
return false; // 没有找到目标值,返回false
42+
}
43+
}

0 commit comments

Comments
 (0)