欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

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

程序员文章站 2023-12-21 20:08:22
...

二分查找是一种就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;
}

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


Leecode33. 搜索旋转排序数组

假设按照升序排序的数组在预先未知的某个点上进行了旋转。
( 例如,数组 [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;
}

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

给定一个按照升序排列的整数数组 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;
}

Leecode35. 搜索插入位置

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

示例 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;
    }
相关标签: 做题记录

上一篇:

下一篇: