二分法查找有序数组arr中,大于等于k的最左侧的位置
提示:二分法系列文章
最基础的二分法:
二分法查找有序数组中的k所在的位置
文章目录
二分法查找有序数组arr中,大于等于k的最左侧的位置
查找arr中大于等于k的最左侧的位置。
一、审题
示例:arr=1 1 1 2 2 2 3 3 3 3 4 4 5 5 5,k=2
从左往右数,>=2的最左侧的位置就是i=4的位置
如果k很大很大呢,你是不是要从左往右数?????
肯定不行的
二、解题
显然,和之前说的一样,有序数组,查找某一个数k,一定不能遍历o(n)
需要二分查找,如果arr中有很多重复的元素,那你可能突然某一个mid瞬间找到了k
但是此时很多数都是k,我们需要最左侧那个位置,那就需要继续向左逼近
(1)最开始,mid=(L+R)/2=14/2=7,发现arr[mid]=3>=k,显然,k可能还会在mid左边,令R=mid-1=6,此时记下>=2的最左侧位置j=mid=7;
(2)然后,mid=(L+R)/2=6/2=3,发现arr[mid]=2>=k,显然,k可能还会在mid左边 ,令R=mid-1=2,此时记下>=2的最左侧位置j=mid=3;
(3)然后,mid=(L+R)/2=2/2=1,发现arr[mid]=1<k,显然,k可能还会在mid右边 ,令L=mid+1=2,此时不管j;
(4)然后,mid=(L+R)/2=2+2/2=2,发现arr[mid]=1<k,显然,k可能还会在mid右边 ,令L=mid+1=3,此时不管j;
显然L>R了,挂!
发现了没,实际上,每次都是二分查找,不断逼近最左侧的>=k的那个位置
没有重复的k,可以找找到唯一位置mid
有重复的k的话,那要不断地让k逼近左边,然后记忆>=k的那个元素的位置j,直到最后L>R了,j就是最左侧的位置
自己画个过程比比,看懂我的不代表你能懂,能写,自己动手写写,一下子就明白了
手撕代码:
//寻找<=k的最左侧的位置
public static int findLessThanKmostLeftIndex(int[] arr, int L, int R, int k){
int j = -1;
while (L <= R){
int mid = L +((R - L) >> 1);
if (arr[mid] >= k) {
j = mid;
R = mid - 1;//可能还需要继续去左边找,你不是要最左边的位置吗?
}else {//arr[mid] < k
//如果mid太小,显然还需要去mid右边找
L = mid + 1;
}
}
return j;
}
这个的暴力解,肯定也是不行的,N太大,失效了
//暴力解法
public static int findRNum1(int[] arr, int value){
int index = -1;
for (int i = 0; i < arr.length; i++) {
if(arr[i] > value){
index = i-1;
break;//刚刚到最右边,就停止
}
}
return index;
}
有序数组中arr中,找<=k最右侧的位置j
那么相同的:
在有序数组中arr中,找<=k最右侧的位置j,同样的做法。
arr=2,2,2,2,3,3,3,3,4,4,4,4,当k=3时,找到最右边那个3的位置,即j=7
那么这时候要找得最右位置,那如果找到了mid,发现arr[mid]<=k时,记录j=mid,而且还有可能需要继续向右逼近,故记录L=mid+1,继续去右边找。
但是发现arr[mid]>k时,压根不需要右边找,k肯定在左边,即令R=mid-1,j不操作。
手撕代码一个道理:
//二分思想解法
//有序数组中,找<=value最右侧的位置比如
// arr==222222333334444444,当value==3时,找到最右边那个3的位置
public static int findLessThanValue2(int[] arr, int value){
int L = 0;
int R = arr.length-1;
int index = -1;
while (L <= R){
int mid = L +((R - L)>> 1);//求中点
if(arr[mid] > value) {
R = mid -1;//比value大,则继续找左边
}else {//<=value 继续往右边找,直到L==R
index = mid;
L = mid +1;
}
}
return index;
}
暴力解在N很大时就失效了,半天找不到。
//暴力解法:
public static int findLessThanValue1(int[] arr, int value){
int index = -1;//记录对号
for (int i = 0; i < arr.length; i++) {
if(arr[i] == value+1) {
index = i-1;//刚找到value,左边位置就是最右边值
break;//提前突出
}
}
return index;
}
测试代码:
public static void main(String[] args) {
int[] arr = {2,2,2,2,3,3,3,3,4,4,4,4};
int value = 3;
//暴力解法,正确的解法
int pos = findLessThanValue1(arr, value);
System.out.println(pos);
//优化算法,二分思想
int pos1 = findLessThanValue2(arr, value);
System.out.println(pos1);
System.out.println(pos == pos1 ? "wright!":"wrong!!!");
int pos2 = findLessThanKmostLeftIndex(arr, 0, arr.length - 1, 3);
System.out.println(pos2);
}
总结
提示:重要经验:
1)二分查找有序数组中<=k的最左侧,或者最右侧位置,非常容易
2)理解透二分查找就能很容易手撕出代码来。