递归迭代和分治:二分查找的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种实现方式)

六十八快速幂算法牛顿迭代法累加数组+二分查找的变形

实验2 递归和分治法(二分查找)

算法导论第2章 分治法与归并排序, 二分查找法

递归与分治思想:治思想 && 折半查找法(迭代 && 递归)

五大经典算法思想之分治策略