查找算法之“二分查找”
查找算法之“二分查找”
在平常我们经常见到的查找算法有:1.线性查找 2. 二分查找 3.插值查找 4. 黄金分割查找(斐波那契查找),其中又以二分查找最为经典和重要,本文将带你简单了解一下二分查找及一些二分查找的变式算法题。
+ + + +
/**
* 二分查找算法的练习:
* 题目:对于一个大小为 n 的有序无重复值的数组nums, 给定一个target值,
* 请你在nums数组中查找该target的位置并返回,如果数组中不存在该值则返回-1。
* 示例:输入: [1,2,5,6,8], 2 输出:1
*/
public int binarySearch(int[] nums, int target){
//左右指针,指向左右边界
int l = 0, r = nums.length - 1;
//每次循环的过程中,先取区间的中间值,再判断中间值与目标值之间的大小关系
//根据大小关系决定区间向那一边缩小,最终直到找到目标值返回或者区间缩小的为0后退出
while(l <= r){
int mid = l + (r - l)/2;//此种写法能避免大数溢出
if(nums[mid] < target){
l = mid + 1;
}else if(nums[mid] > target){
r = mid -1;
}else{
return mid;
}
}
return -1;
}
剑指 Offer 53 - II. 0~n-1中缺失的数字
题目:一个长度为n-1的递增排序数组中的所有数字都是唯一的,并且每个数字都在范围0~n-1之内。在范围0~n-1内的n个数字中有且只有一个数字不在该数组中,请找出这个数字。
示例:
输入: [0,1,3]
输出: 2
class Solution {
//思想:二分查找算法的变式
//此题与二分查找非常类似但又有所不同,二分查找是给定一个有序的数组和目标值让我们从中查找目标值的位置
//此题给了我们一个特殊的有序数组——范围0~n-1内的n个数字中有且只有一个数字不在该数组中,让我们从中找缺失的数字,它与二分唯一的区别就是范围缩小的条件。
public int missingNumber(int[] nums) {
int i = 0, j = nums.length - 1;
while(i <= j) {
int m = i + (j - i )/ 2;
if(nums[m] == m) i = m + 1;
else j = m - 1;
}
return i;
}
}
后面两道有难度,初学的兄弟酌情选择
剑指 Offer 11. 旋转数组的最小数字
题目:把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。输入一个递增排序的数组的一个旋转,输出旋转数组的最小元素。例如,数组 [3,4,5,1,2] 为 [1,2,3,4,5] 的一个旋转,该数组的最小值为1。
示例:
输入:[3,4,5,1,2]
输出:1
class Solution {
public int minArray(int[] numbers) {
int left = 0;
int right = numbers.length-1;
while(left < right){
int mid = left + (right - left)/2;
if(numbers[right] > numbers[mid]){
right = mid;
}else if(numbers[right] < numbers[mid]){
left = mid + 1;
}else{
right--;
}
}
return numbers[left];
}
}
在排序数组中查找元素的第一个和最后一个位置
题目:给定一个按照升序排列的整数数组 nums,和一个目标值 target。找出给定目标值在数组中的开始位置和结束位置。如果数组中不存在目标值 target,返回 [-1, -1]。
进阶:你可以设计并实现时间复杂度为 O(log n) 的算法解决此问题吗?
示例:
输入:nums = [5,7,7,8,8,10], target = 8
输出:[3,4]
class Solution {
//二分法的变式
public int[] searchRange(int[] nums, int target) {
int[] res = new int[2];
res[0] = findBoundary(nums, 0, nums.length-1, target, 1);//1表示寻找左边界
res[1] = findBoundary(nums, 0, nums.length-1, target, 2);//2表示寻找右边界
return res;
}
public int findBoundary(int[] nums, int l , int r, int target, int flag){
while(l <= r){
int mid = l + (r-l)/2;
//找到目标值后根据需要的边界来缩小区间
if(nums[mid] == target){
if(flag == 1){
r = mid;
if(nums[l] == target) return l;
}else {
l = mid;
if(nums[r] == target) return r;
else r--;
}
}else if(nums[mid] < target){
l = mid + 1;
}else if(target < nums[mid]){
r = mid - 1;
}
}
return -1;
}
}
小穆黄埔军校
坚持、共享、开源、自由
让进步成为一种习惯