二分查找 binarySearch

基本概念

在一串有序数组中(上升或下降)找出一个key值。
• 如果key小于中值,则继续查找左边(上升)或者右边(下降)
• 如果key等于中值,则查找成功
• 如果key大于中值,则继续查找右边(上升)或者左边(下降)

时间复杂度和空间复杂度

时间复杂度: O(nlogn + 1) 空间复杂度: O(1)
关于时间复杂度:
let n be a power of 2. After the first comparison, n/2 elements are left for further search; after the second comparison, (n/2)/2 elements are left. After the kth comparison, n/2^k elements are left for further search. When k = log2n, only one element is left in the array, and you need only one more comparison. Therefore, in the worst case when using the binary search approach, you need log2n+1 comparison to find an element in the sorted array. In the worst case for a list of 1024 (2^10) elements, binary search requires only 11 comparisons, whereas a linear search requires 1023 comparisons in the worst case.
在这里插入图片描述

如何取mid

  • 第一种方法:(left + right) / 2;
  • 第二种方法: left + (right - left) / 2;

第一种方法在left和right都近似int的最大值 2^31 的情况下会出现溢出,所以用第二种方法

Level 1: 一般实现

迭代法


	public static int binarySearchNoneRecursive(int key, int [] input){
		int left = 0;
		int right = input.length -1;
		int mid;
		
		while(left <= right) {
			mid = (left+right)/2;
			if(key == input[mid]) {
				return mid;
			}		
			if(key < input[mid]) {
				right = mid - 1;
			}
			else if (key > input[mid]) {
				left =  mid + 1;
			}			
		}
		
		return -1;
	}

递归法

public static int binarySearchRecursive(int key, int [] input, int left, int right){
		if(right < left)
			return -1;
		
		int mid = left + (right - end) / 2;
	
		if(input[mid] == key) {
			return mid;
		}
		else if(key < input[mid]) {
			return binarySearchRecursive(key, input, left, mid-1);
		}
		else if (key > input[mid]) {
			return binarySearchRecursive(key, input, mid+1, right);
		}
		else {
			return -1;
		}
	}

Level 2: First or Last Position of Target

Last Position of Target

Last Position of Target: 取数组中最后一次出现的target的index, 比如: [1,2,3,3,3,56],取第三个3的下标

public class Solution {
    /**
     * @param nums: The integer array.
     * @param target: Target to find.
     * @return: The first position of target. Position starts from 0.
     */
    public int lastPosition(int[] nums, int target) {
        if (nums.length == 0) {
            return -1;
        }
        int left = 0;
        int right = nums.length -1;
        //这里是left + 1 < right
        //也就是还有两个数的时候退出循环
        while (left + 1 < right) {
            int mid = left + (right - left) / 2;
            
            if (nums[mid] == target) {
          //这里是left = mid,不直接return 因为不确定是否是最后一次出现的位置
          //left=mid 把二分的范围向右收缩,这样可以找到最后一次出现的target
                left = mid;
            }
            else if (target < nums[mid]) {
                right = mid - 1;
            }
            else {
                left = mid + 1;
            }
        }
    	
    	//退出循环的时候还有两个数,所以要分别判断是否是target
    	//因为是找最后一次出现的位置,所以先判断right
        if (nums[right] == target) {
            return right;
        }
        
        if (nums[left] == target) {
            return left;
        }
        
        return -1;
    }
}

First Position of Target

First Position of Target: 取数组中第一次出现的target的index, 比如: [1,2,3,3,3,56],取第一个3的下标

public class Solution {
    /**
     * @param nums: The integer array.
     * @param target: Target to find.
     * @return: The first position of target. Position starts from 0.
     */
    public int binarySearch(int[] nums, int target) {
        if (nums.length == 0) {
            return -1;
        }
        
        int left = 0;
        int right = nums.length -1;
         //这里是left + 1 < right
        //也就是还有两个数的时候退出循环
        while (left + 1 < right) {
            int mid = left + (right - left) / 2;
            
          //这里是right=mid,不直接return 因为不确定是否是第一次出现的位置
          //right=mid 把二分的范围向左收缩,这样可以找到第一次出现的target
            if (nums[mid] == target) {
                right = mid;
            }
            else if (target < nums[mid]) {
                right = mid - 1;
            }
            else {
                left = mid + 1;
            }
        }
        
        //退出循环的时候还有两个数,所以要分别判断是否是target
    	//因为是找第一次出现的位置,所以先判断left
        if (nums[left] == target) {
            return left;
        }        
        if (nums[right] == target) {
            return right;
        }
        
        return -1;
    }
}

Level 3: Find Minimum in Rotated Sorted Array

Suppose an array of length n sorted in ascending order is rotated between 1 and n times. For example, the array nums = [0,1,2,4,5,6,7] might become:

  • [4,5,6,7,0,1,2] if it was rotated 4 times.
  • [0,1,2,4,5,6,7] if it was rotated 7 times.

Notice that rotating an array [a[0], a[1], a[2], …, a[n-1]] 1 time results in the array [a[n-1], a[0], a[1], a[2], …, a[n-2]].
Given the sorted rotated array nums, return the minimum element of this array.

Example 1:
Input: nums = [3,4,5,1,2]
Output: 1
Explanation: The original array was [1,2,3,4,5] rotated 3 times.
Example 2:

Input: nums = [4,5,6,7,0,1,2]
Output: 0
Explanation: The original array was [0,1,2,4,5,6,7] and it was rotated 4 times.
Example 3:

Input: nums = [11,13,15,17]
Output: 11
Explanation: The original array was [11,13,15,17] and it was rotated 4 times.

/*
原理是利用二分法:
对于任意一个旋转过的数组:
Example: 6,7,1,2,3,4,5
如果mid的值大于right的值 (Example中的 2大于5) 则说明最小值在右边
如果mid的值小于right的值 则说明最小值在左边
如果mid的左边的值比mid大,mid右边的值也比mid大,则说明mid就是最小值 (3,1,2)
*/


class Solution {
    public int findMin(int[] nums) {
        if (nums.length == 1) {
            return nums[0];
        }

        int left = 0;
        int right = nums.length - 1;

        while (left <= right) {
            int mid = left + (right - left)/2;

            //处理mid等于第一个元素时的情况
            if (mid == 0) {
                return nums[0] < nums[1] ? nums[0] : nums[1];
            }
            //处理mid等于最后一个元素时的情况
            if (mid == nums.length - 1) {
                return nums[nums.length-1] < nums[nums.length - 2] ? nums[nums.length - 1] : nums[nums.length - 2];
            }

            //如果mid的左边的值比mid大,mid右边的值也比mid大,则说明mid就是最小值 (3,1,2)
            if (nums[mid - 1] > nums[mid] && nums[mid + 1] > nums[mid]) {
                return nums[mid];
            }
            //如果mid的值大于right的值, 则说明最小值在右边
            else if (nums[mid] > nums[right]) {
                left = mid + 1;
            }
            //其余情况在左边 (mid的值小于right的值 则说明最小值在左边)
            else {
                right = mid - 1;
            }
        }

        return nums[0];
    }
}

Level 4: Search in Rotated Sorted Array

You are given an integer array nums sorted in ascending order (with distinct values), and an integer target.
Suppose that nums is rotated at some pivot unknown to you beforehand (i.e., [0,1,2,4,5,6,7] might become [4,5,6,7,0,1,2]).
If target is found in the array return its index, otherwise, return -1.

Example 1:
Input: nums = [4,5,6,7,0,1,2], target = 0
Output: 4
Example 2:

Input: nums = [4,5,6,7,0,1,2], target = 3
Output: -1
Example 3:

Input: nums = [1], target = 0
Output: -1

/*
采用二分法:
Example    6 7 8 9 1 2 3 4 5
在中间切一刀
如果nums[mid] > nums[left],则说明left到mid有序(递增顺序)
    判断 target是否在 left到mid之间,如果在则说明在左边,如果不在说明在右边

如果 nums[mid] < nums[left], 则说明mid到right有序(递增顺序)
    判断 target是否在 mid到right之间,如果在则说明在右边,如果不在说明在左边
*/

class Solution {
    public int search(int[] nums, int target) {
        if (nums.length == 0) {
            return -1;
        }
        int left = 0;
        int right = nums.length - 1;

        while (left + 1< right) {
            int mid = left + (right - left) / 2;
            
            //如果nums[mid] > nums[left],则说明left到mid有序(递增顺序)
            if (nums[mid] > nums[left]) {
                // 判断 target是否在 left到mid之间
                if (target >= nums[left] && target <= nums[mid]) {
                    right = mid;
                }
                else {
                    left = mid;
                }
            }
            //如果 nums[mid] < nums[left], 则说明mid到right有序(递增顺序)
            else {
                // 判断 target是否在 mid到right之间
                if (target >= nums[mid] && target <= nums[right]) {
                    left = mid;
                }
                else {
                    right = mid;
                }
            }
        }

        if (nums[left] == target) {
            return left;
        }
        if (nums[right] == target) {
            return right;
        }

        return -1;
    }
}

Level 5: Find K Closest Elements

Level 6: Wood Cut

Given n pieces of wood with length L[i] (integer array). Cut them into small pieces to guarantee you could have equal or more than k pieces with the same length. What is the longest length you can get from the n pieces of wood? Given L & k, return the maximum length of the small pieces.

Example 1
Input:
L = [232, 124, 456]
k = 7
Output: 114
Explanation: We can cut it into 7 pieces if any piece is 114cm long, however we can’t cut it into 7 pieces if any piece is 115cm long.
Example 2

Input:
L = [1, 2, 3]
k = 7
Output: 0
Explanation: It is obvious we can’t make it.


/*基于答案值域的二分法。
木头长度的范围在 1 到 max(L),在这个范围内二分出一个长度 length,
然后看看以这个 wood length 为前提的基础上,能切割出多少木头,
如果少于 k 根,说明要短一些才行,如果多余 k,说明可以继续边长一些。
*/

public class Solution {
    /**
     * @param L: Given n pieces of wood with length L[i]
     * @param k: An integer
     * @return: The maximum length of the small pieces
     */
    public int woodCut(int[] L, int k) {
        int maxL = 0;
        for (int i = 0; i < L.length; i++) {
            maxL = Math.max(maxL, L[i]);
        }
        
        int left = 1;
        int right = maxL;
        int result = 0;
        while (left <= right) {
            int mid = left + (right - left) / 2;
            int temp = count(mid, L);
            
            if (temp >= k) {
                result = mid;
                left = mid + 1;
            }
            else {
                right = mid - 1;
            }
        }
        
        return result;
    }
    
    public int count(int mid, int [] L) {
        int sum = 0;
        for (int i = 0; i < L.length; i++) {
            sum += L[i] / mid;
        }
        
        return sum;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值