递归迭代和分治:二分查找的5个变形题
Posted 纵横千里,捭阖四方
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了递归迭代和分治:二分查找的5个变形题相关的知识,希望对你有一定的参考价值。
二分查找很经典,但是面试的时候不一定直接考察这个问题,而是考察其变形题,我们一起看一下。
1.元素中有重复的二分查找
假如在上面的基础上,元素存在重复,该怎么做呢?这个是我面快手时真实遇到的的。如果刷了二分查找,我们就能分析出来,这里的关键是找到目标结果之后不是返回而是继续向左侧移动。
1.1 循环的两种方式
查找到之后继续向左逼近:
public int search(int[] nums, int target) {
//边界条件判断
if (nums == null || nums.length == 0)
return -1;
int left = 0;
int right = nums.length - 1;
while (left < right) {
int mid = left + (right - left) / 2;
if (nums[mid] < target) {
left = mid + 1;
} else {
right = mid;
}
}
return nums[left] == target ? left : -1;
}
第二种方式是查找之后往左边继续查找:
public int search(int[] nums, int target) {
if (nums == null || nums.length == 0)
return -1;
int left = 0;
int right = nums.length - 1;
while (left <= right) {
int mid = left + (right - left) / 2;
if (nums[mid] < target) {
left = mid + 1;
} else if (nums[mid] > target) {
right = mid - 1;
} else {
//找到之后,往左边找
while (mid != 0 && nums[mid] == nums[mid - 1])
mid--;
return mid;
}
}
return -1;
}
1.2 递归方式
递归方式还是比较简洁的,主要问题是找到目标元素之后,根据要求继续递归就好了,可以这么写:
public int search(int[] nums, int target) {
if (nums == null || nums.length == 0)
return -1;
return binSearch(nums, target, 0, nums.length - 1);
}
private int binSearch(int[] nums, int target, int left, int right) {
if (left > right)
return -1;
if (nums[left] == target)
return left;
int mid = left + (right - left) / 2;
if (nums[mid] < target) {
return binSearch(nums, target, mid + 1, right);
} else if (nums[mid] > target) {
return binSearch(nums, target, left, mid - 1);
} else {
return binSearch(nums, target, left, mid);
}
}
2. 求平方根
实现函数 int sqrt(int x).计算并返回x的平方根这个题的思路是用最快的方式找到n*n=x的n。这里涉及到四舍五入,所以采用折半进行比较:
public int sqrt (int x) {
int l=1,r=x;
while(l <= r){
int mid = l + (r - l)/2;
if(x/mid > mid){
l = mid + 1;
} else if(x / mid < mid){
r = mid - 1;
} else if(x/mid == mid){
return mid;
}
}
return r;
}
3.找缺失数字
从0,1,2,…,n这n+1个数中选择n个数,组成有序数组,请找出缺失的那个数,要求O(n)尽可能小。这个题有三种解法:从头到尾遍历一遍,即可确定。第二个方式,根据数学公式计算累加和,n(n+1)/2-sum就是需要的结果。如果用数学公式先计算累加和,可以这么做:
public int solve (int[] a) {
// write code here
int sum=0;
if(a.length==0)
return 0;
int n=a.length;
sum=(n*(n+1))/2;
for(int i=0;i<n;i++){
sum-=a[i];
}
return sum;
}
对于有序的,这个与从头开始遍历没有本质区别,甚至还需要计算,更浪费资源,种方式更适合数据是无序的情况。对于有序的也可以用二分查找,如果从0到当前的mid,与中间位置进行比较:
public int solve (int[] a) {
// write code here
int left = 0;
int right = a.length-1;
while(left < right){
int mid = (left+right)/2;
if(a[mid]==mid){
left = mid+1;
}else{
right = mid;
}
}
return left;
}
这个程序在执行的时候 提示一个case没有过,不知道是什么场景。
4.旋转数字的最小数字
这个也是出现频率非常高的面试题。
把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。输入一个非递减排序的数组的一个旋转,输出旋转数组的最小元素。NOTE:给出的所有元素都大于0,若数组大小为0,请返回0。
这个题目其实还是根据二分查找来比较,不同的仅仅是比较条件而已。
public int minNumberInRotateArray(int [] array) {
if(array.length==0){
return 0;
}
int l=0,r=array.length-1;
int m=0;
while(array[l]>=array[r]){
if(r-l==1){
m=r;
break;
}
m=l+(r-l)/2;
if(array[l]<=array[m]){
l=m;
}
if(array[r]>=array[m]){
r=m;
}
}
return array[m];
}
}
5. 统计一个数字在升序数组种出现的次数
统计一个数字在升序数组中出现的次数。例如;输入;[1,2,3,3,3,3,4,5],3,输出为4这个也是二分查找的变型题:先用二分法找到元素,然后开始向左右开始遍历,可以使用Arrays自带的方法来实现:
import java.util.*;
public class Solution {
public int GetNumberOfK(int [] array , int k) {
int index = Arrays.binarySearch(array, k);
if(index<0)return 0;
int cnt = 1;
for(int i=index+1; i < array.length && array[i]==k;i++)
cnt++;
for(int i=index-1; i >= 0 && array[i]==k;i--)
cnt++;
return cnt;
}
}
其它类似的题目还有一些,这里就不列举了。原理和处理方法基本都一致。
以上是关于递归迭代和分治:二分查找的5个变形题的主要内容,如果未能解决你的问题,请参考以下文章
虽然简单,但是面试时却容易写错的一个算法:二分查找(迭代和递归2种实现方式)