1.典型的二分查找(注意二分查找的适用条件是递增的数组)
1).递归方式
class BinaryTree { public static void main(String[] args) { int[] a = {1,3,4,6,8,9,10,11,23}; System.out.println(search(a,12,0,a.length-1)); } public static int search(int[] a,int data,int low,int high) { if(low<=high) int pivot = (low+high)/2; if(data==a[pivot]) return pivot; else if(data>a[pivot]) { return search(a,data,pivot+1,high); } else { return search(a,data,low,pivot-1); } } else { return -1; //通过返回-1,表示找不到 } } }
2).非递归方式
class BinaryTree { public static void main(String[] args) { int[] a = {1,3,4,6,8,9,10,11,23}; System.out.println(search(a,11)); } public static int search(int[] a,int data) { int low = 0; int high = a.length - 1; while(low<=high) { int pivot = (low + high)/2; if(data==a[pivot]) return pivot; else if(data>a[pivot]) { low = pivot + 1; } else { high = pivot -1; } } return -1; } }
2.使用二分查找的思想来解题:
1.题目描述 (剑指offer 面试题11 旋转数组)
分析:本题给出的数组一定程度上是排序的,因此可以试者使用二分查找法寻找最小值。
1)使用low和high分别指向数组的第一个元素和最后一个元素。如果没有旋转第一个元素是小于等于最后一个元素,旋转后第一个元素应该是大于等于最后一个元素(等于是有重复元素的情况)
2)中间元素大于第一个元素,此时说明中间元素位于前面的递增子数组中,此时最小元素位于中间元素后面的。我们可以让low指向中间元素。
3)中间元素小于第一个元素,此时说明中间元素位于后面的递增子数组中,此时最小元素位于中间元素的前面,我们可以让high指针指向中间元素。
4)通过2)3)可以缩小寻找的范围,重复2)3)直到low+1=high时,high指向的元素就是最小元素
5)考虑几种特殊情况,如果把排序数组的前面0个元素搬到最后面,即排序数组本身,此时第一个元素就是最小的。
6){1,0,1,1,1}和{1,1,1,0,1}都可看成是递增排序数组{0,1,1,1,1}的旋转,此时mid,low,high指向的数都相同,第一种情况下,中间元素位于后面的子数组,第二种情况,中间数字位于前面的子数组,此时只能采用顺序寻找的方式。
代码实现:
public class Solution { public int minNumberInRotateArray(int [] array) { if(array.length==0){ return 0; } if(array[0]<array[array.length-1]) return array[0]; return binarySearch(array,0,array.length-1); } public int binarySearch(int[] a,int low,int high){ while(a[low]>=a[high]){ //这边为什么要等于号 if(low+1==high){ return a[high]; } //如果下标low,high和mid相等,这时候就需要顺序查找 int mid = (low + high)/2; if(a[low]==a[high]&&a[low]==a[mid]){ return MininOrder(a,low,high); } if(a[mid]>=a[low]){ //这边的等于号不能丢 low = mid; }else if(a[mid]<=a[low]){ high = mid; } } return a[0]; } public int MininOrder(int[] a,int low,int high){ int min = a[low]; for(int i=low+1;i<=high;i++){ if(a[i]<min){ min = a[i]; } } return min; } }2. 题目描述(剑指offer在排序数组中查找数字)
思路:该题的数组是一个排序数组,思路找到第一次出现3的位置,和最后出现3位置,两者相减就得到3的个数。可以考虑使用二分查找法,如上面的数字所示,典型的二分查找在找到数字3后就停止了,该题中可以在找到3后(第一次找到的是中间的3),判断3前面的数字是不是3(前面有数字的情况下,如果前面没有数字,那么这个3肯定就是第一出现的3),如果前面的数字不是3,那么找到的3就是一次出现的3,而如果前面的数字也是3,那么就在数组的前半段继续查找3,直到找到第一次出现3的位置。同样的方法找到最后一个出现3的位置,将两个位置相减就得到出现的个数。
代码实现:
class Solution { public int GetNumberOfK(int [] array , int k) { int N = array.length - 1; int i = getFirstK(array,k,0,N); System.out.println("i: "+i); int j = getLastK(array,k,0,N); System.out.println("j: "+j); if(i>-1&&j>-1) return j-i+1; return 0; } public int getFirstK(int[] array,int k,int low,int high){ while(low<=high){ int mid = (low+high)/2; if(array[mid]<k) low = mid + 1; else if(array[mid]>k) high = mid -1; else{ //找了数字k,还需继续判断这个数字是不是第一次出现 if(mid==0) //如果前面没有数字了,这个数字就是第一次出现了 return mid; else if(mid-1>=0&&array[mid-1]!=k){ //如果前面的数字和这个数字不等,那么也是第一个出现了 return mid; } else{ high = mid - 1; } } } return -1; //如果没有找到该数字,返回-1 } public int getLastK(int[] array,int k,int low,int high){ if(low<=high){ int mid = (low+high)/2; if(array[mid]>k){ return getLastK(array,k,low,mid-1); } else if(array[mid]<k){ return getLastK(array,k,mid+1,high); } else{ if(mid==array.length-1){ return mid; } if(array[mid+1]<=array.length-1&&array[mid+1]==k){ return getLastK(array,k,mid+1,high); } else{ return mid; } } } return -1; } }