二分查找算法相关算法题合集

二分查找是一种就nm很离谱的算法。
二分查找必须要保证待查找的数组是从小到大有序的。二分查找也叫折半查找,简单的说就是将我们需要查找的target与数组的中间mid进行比较,如果等于就可以直接返回mid了。最初我们设置的mid就是数组长度的一半(low + high) / 2,当target小于mid的时候移动high让它成为为mid - 1;反之将low移动成为high + 1
二分查找的模板(基础实现):

public int binarySearch(int[] nums, int target){
	//离谱1:注意high的取值
	int low = 0, high = nums.length - 1, mid;
	//离谱2:注意while的判断条件
	while (low <= high) {	
		mid = (low + high) / 2;
		//离谱3:注意下面三个判断条件
		if (target = nums[mid]) {
			//返回mid所在的下表
			return mid;
		} else if (target < nums[mid]) {
			high = mid - 1;
		} else {
			low = mid + 1;
		}
	}
	return -1;
}

有了这个模板,在做题的时候套就行,然后根据题里面的特殊要求根据改造。但是要时刻注意离谱的地方,不注意就会出错。


Leetcode33. 搜索旋转排序数组

假设按照升序排序的数组在预先未知的某个点上进行了旋转。
( 例如,数组 [0,1,2,4,5,6,7] 可能变为 [4,5,6,7,0,1,2] )。
搜索一个给定的目标值,如果数组中存在这个目标值,则返回它的索引,否则返回 -1 。
你可以假设数组中不存在重复的元素。
你的算法时间复杂度必须是 O(log n) 级别。

示例 1:
输入: nums = [4,5,6,7,0,1,2], target = 0
输出: 4

示例 2:
输入: nums = [4,5,6,7,0,1,2], target = 3
输出: -1

题目中说保证时间复杂度为O(log n),那只能从二分查找法入手了。审题后我们发现,它给的数组可以理解为是“二分有序的”,即我们可以将这个数组分半,每一半都是从小到大有序的。
我们具体要做的是先判断前后哪个部分是完全有序的,这样方便我们进行二分查找。

public int search(int[] nums, int target) {
	if ((nums.length == 0) || (nums == null)) {
		return -1;
	}
	int low = 0, high = nums.length - 1, mid;
	while (low <= high) {
		mid = (low + high) / 2;
		if (target == nums[mid]) {
			return mid;
		}
		if (nums[low] < nums[mid]){//前半部分完全有序
			if (target < nums[mid] && nums[low] <= target) {
				high = mid - 1;
			} else {
				low = mid + 1;
			}
		} else {//后半部分完全有序
			if (target > nums[mid] && nums[high] >= target) {
				low = mid + 1;
			} else {
				high = mid - 1;
			}
		}
	}
	return -1;
}

Leetcode34. 在排序数组中查找元素的第一个和最后一个位置

给定一个按照升序排列的整数数组 nums,和一个目标值 target。
找出给定目标值在数组中的开始位置和结束位置。
你的算法时间复杂度必须是 O(log n) 级别。
如果数组中不存在目标值,返回 [-1,-1]。

示例 1:
输入: nums = [5,7,7,8,8,10], target = 8
输出: [3,4]

示例 2:
输入: nums = [5,7,7,8,8,10], target = 6
输出: [-1,-1]

遇到这种要求时间复杂度为O(log n)的题就直接考虑二分查找,这个题的要求是返回一个有两个元素的数组,第一个为起始的索引,第二个为结束索引,所以我们在找到的时候还要向左右扩展找到范围,我们可以用一个布尔类型的flag,如果它为true我们就至扫左侧。

public int find(int[] nums, int target, boolean flag) {
	int low = 0. high = nums.length, mid;
	while (low < high) {
		mid = (low + high) / 2;
		//给flag表示为true,如果mid就是
		if ((target < nums[mid]) || (flag && target == nums[mid])) {
			high = mid;
		} else {
			low = mid + 1;
		}
	}
	return low;
}

public int[] searchRange(int[] nums, int target) {
	int[] arr = {-1,-1};
	int result = find(nums, target, true);
	if ((nums[result] != target) || (result == nums.length)) {
		return arr;
	}
	arr[0] = result;
	arr[1] = find(nums, target, false) - 1;
	return arr;
}

Leetcode35. 搜索插入位置

给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。
如果目标值不存在于数组中,返回它将会被按顺序插入的位置。

示例 1:
输入: [1,3,5,6], 5
输出: 2

示例 2:
输入: [1,3,5,6], 2
输出: 1

没找到就返回被插入的位置,这个位置其实就是我们最终没有搜索到时low的下标,做题的时候就是没想到这一点,看来还是二分查找理解的不到位。

public int searchInsert(int[] nums, int target) {
        int low = 0, high = nums.length - 1, mid;
        while (low <= high) {
            mid = (high + low) / 2;
            if (nums[mid] == target) {
                return mid;
            } else if (target < nums[mid]){
                high = mid - 1;
            } else {
                low = mid + 1;
            }
        }
        return low;
    }

猜你喜欢

转载自blog.csdn.net/laobanhuanghe/article/details/103674735
今日推荐