二分法相关

1.旋转数组找最小值

{3,4,5,1,2}为{1,2,3,4,5}的一个旋转数组

public int minNumberInRotateArray(int [] array) {
            int low = 0 ; int high = array.length - 1;   
            while(low < high){
                int mid = low + (high - low) / 2;        
                if(array[mid] > array[high]){
                    low = mid + 1;
                }else if(array[mid] == array[high]){
                    high = high - 1;
                }else{
                    high = mid;
                }   
            }
            return array[low];
}

 2.去一个排序数组中找距离target最近的节点的下标(二分法)

public int closestNumber(int[] A, int target) {
        // write your code here
        if(A == null || A.length == 0 || target < A[0])
            return 0;
            
        if(target > A[A.length - 1])
            return A.length - 1;
            
        int l = 0, r = A.length - 1;
        while(l < r - 1){
            int mid = l + (r - l) / 2;
            if(A[mid] == target)
                return mid;
            else if(A[mid] > target)
                r = mid;
            else
                l = mid;
        }
        
        if(Math.abs(target - A[l]) < Math.abs(target - A[r]))
            return l;
        else
            return r;
}

 依次给出n个正整数A1,A2,… ,An,将这n个数分割成m段,每一段内的所有数的和记为这一段的权重, m段权重的最大值记为本次分割的权重。问所有分割方案中分割权重的最小值是多少?

public int binarySearch(int[] a,int m) {
        int n=a.length;
        int sum=0;
        int maxa=0;
        for(int i=0;i<n;i++) {
            sum+=a[i];
            maxa=Math.max(maxa, a[i]);
        }
        //被分割的数只可能是最大值和和之间的值
        int left=maxa,right=sum;
        int countm=0;
        while(left<right) {
            int mid=(left+right)/2;
            int temp=0;
            countm=1;//计算当前被分割的个数
            for(int i=0;i<n;i++) {
                if(a[i]+temp>mid) {
                    temp=0;
                    countm++;
                }
                temp+=a[i];
            }
            if(countm>m)//被分割的次数大于原先的,说明我们安排的mid小了
                left=mid+1;
            else
                right=mid;                
        }
        return left;
    }

 3.找这样一个数组第k小的数字

3 3 3(n,m,k)

1 2 3

2 4 6

3 6 9

二分查找,去每一列里面查找比mid小的,统计个数,如果总个数比k大,就证明,end大了,向左移动,如果比k小了,start小了,向右移动(感觉把二分法用到极致了)

public int findKthNumber(int m, int n, int k) {
        int st = 1, ed = m * n;
        while (st < ed) {
          int mid = (st + ed) / 2;
          if (largeEnough(mid, m, n, k)) ed = mid;
          else st = mid + 1;
        }
        return ed;
      }
    private boolean largeEnough(int x, int m, int n, int k) {
        int cnt = 0;
        for (int i = 1; i <= m; i++) cnt += Math.min(x / i, n);
        return cnt >= k;
    }

 4.去循环有序数组里面找目标为target的数组的下标

4 5 1 2 3这种

public int search(int[] a,int n,int target) {
        int left = 0, right = n-1;
        while(left<=right)
        {
            int mid = left + ((right-left)/2);
            if(a[mid] == target)
                return mid;
     
            if(a[left] <= a[mid])//转折点在右半边,左边是有序的,应该尝试用左边的数去比较
            {
                if(a[left] <= target && target < a[mid])
                    right = mid - 1;
                else
                    left = mid + 1;
            }
            else //转折点在左半边,右边是有序的,尝试用右边的数去比较
            {
                if(a[mid] < target && target <= a[right])
                    left = mid + 1;
                else
                    right = mid - 1;
            }
         }
        return -1;//没有找到
    }

 

posted @ 2019-07-18 10:34  LeeJuly  阅读(201)  评论(0)    收藏  举报