针对的是一个有序数组进行查找的情况,用二分查找法进行数据查找是一个不错的选择,时间复杂度O(log(n)),定义start和end,不断改变start和end的值来缩小查找的区域
普通二分查找法
- 给出一个有序数组,在数组中查找数值为a的值
- 首先定义start和end分别代表数组中查找范围的最小值和最大值的下标,start=0,end=array.length;然后找到中间值array[(start+end)/2]与a比较,若a小则表示找大了,前移缩小最大值,end=(start+end)/2-1;否则证明找小了,start前移,start=(start+end)/2+1,每操作一次缩小1/2的范围,时间复杂度O(log(n)).
- 实现
public int search(int[] array ,int a){
int start=0;
int end =array.length-1;
int temp;
while(start<end){
temp=(start+end)/2;
if(array[temp]>a){
end=temp-1;
}else if(array[temp]<a){
start=temp+1;
}else {
return temp;
}
}
//start==end跳出时判断是否找到
if(array[start]!=a){
System.out.println("没有找到!");
return -1;
}
return start;
}
进阶二分查找法
- 题目:给定两个大小为 m 和 n 的有序数组 nums1 和 nums2。请你找出这两个有序数组的中位数,并且要求算法的时间复杂度为 O(log(m + n))。你可以假设 nums1 和 nums2 不会同时为空。
- 首先分析,时间复杂度为O(log(m+n)),而且是两个有序数组,无疑是用二分查找法解决。
- 具体思路
因为是找中位数,可以知道中位数的特点是左右两边的元素个数相等,则先将两个有序数组分为分为左右两部分,left和right,只要left和right的个数相等并且maxleft<=minright,则可以找出中位数为maxleft(m+n为奇数)或 (maxleft+minright)/2(m+n为偶数)
以第一个数组为基准,进行二分查找 ,halflen=(m+n+1)/2,j=halflen-i;直到array1[i-1]<=array2[j] && array2[j-1]<=array1[i]时,说明找到的i,j刚好符合条件
class Solution {
public double findMedianSortedArrays(int[] A, int[] B) {
int m = A.length;
int n = B.length;
//方便操作假定m<=n,否则A和B互换
if (m > n) { // to ensure m<=n
int[] temp = A; A = B; B = temp;
int tmp = m; m = n; n = tmp;
}
//定义iMax=m而不是m-1是为了保证iMin+iMax>=0
int iMin = 0, iMax = m, halfLen = (m + n + 1) / 2;
while (iMin <= iMax) {
//保证i+j=m+n-i-j(偶数)或者i+j=m+n+1-i-j(奇数)
int i = (iMin + iMax) / 2;
int j = halfLen - i;
//二分法查找
//i<iMax为了让iMin=i+1不会越界
if (i < iMax && B[j-1] > A[i]){
iMin = i + 1; // i is too small
}
else if (i > iMin && A[i-1] > B[j]) {
iMax = i - 1; // i is too big
}
else { // i is perfect
//找到left中最大值
int maxLeft = 0;
//i==0时i-1越界,maxLeft=B[j-1]
//同时解决了A为空的情况
if (i == 0) { maxLeft = B[j-1]; }
//j==0时,j-1越界
else if (j == 0) { maxLeft = A[i-1]; }
//其他情况,maxLeft取A[i-1],A[j-1]中的最大值
else { maxLeft = Math.max(A[i-1], B[j-1]); }
//如果m+n为奇数,则只有一个中间数为中位数
if ( (m + n) % 2 == 1 ) { return maxLeft; }
int minRight = 0;
//i==m时,i越界
if (i == m) { minRight = B[j]; }
//j越界
else if (j == n) { minRight = A[i]; }
else { minRight = Math.min(B[j], A[i]); }
return (maxLeft + minRight) / 2.0;
}
}
return 0.0;
}
}
总结下来就三点:1.分界线,即i,j,halflen的取值,取i,j左边的为left,i,j自身以及右边放入right,根据自己的分界情况数组角标也不同。2.对数组为空的处理,因为要考虑到数组可能为空,iMax取m,才能保证iMax+iMin大于0,因为-1/2=0,0/2=0,1/2=0结果的i都为0,不能具体判断i==0是由什么导致的,主要是方便区分数组为空的情况。3.越界问题,i=0时i-1越界,i=m时i越界,j同理