二分查找算法复习
Posted 小智RE0
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了二分查找算法复习相关的知识,希望对你有一定的参考价值。
文章目录
基础复习
首先二分查找算法是要基于当前数组为有序数组;
实际就是每次找中心点的值
; 若目标数等于中心点了,直接返回即可,若目标数小于中间数,则在中心点的左侧查找,反之则在中心点的右边查找.
在实际的使用中,二分查找可以用不同模板的方式实现,那么具体在判断中心点时的规则就不一样.
从经典的基本问题入手;力扣原题704. 二分查找;
class Solution
public int search(int[] nums, int target)
//基本二分查找模板;
int n = nums.length;
if(n < 2) return nums[0] == target ?0:-1;
int left = 0;
int right = n - 1;
while(left<=right)
//计算中点;
//int middle = (right + left)/2;在不考虑溢出情况时可以用这个;
int middle = left + (right-left)/2;
if(nums[middle] == target)
return middle;
else if(nums[middle] > target)
right = middle -1;
else
left = middle +1;
return -1;
实际稍微改变条件后,也可以这样写
class Solution
public int search(int[] nums, int target)
//基本二分查找模板;
int n = nums.length;
if(n < 2) return nums[0] == target ?0:-1;
int left = 0;
int right = n - 1;
while(left<right)
//计算中点;
//int middle = (right + left)/2;在不考虑溢出情况时可以用这个;
int middle = left + (right-left)/2;
if(nums[middle] >= target)
right = middle;
else
left = middle +1;
//判断是否存在;
if(nums[left] == target)
return left;
return -1;
注意,如果边界条件是 left(左指针) = middle(中心点);时;
那么计算中心点需要(+1 )向上取整 ;
这是由于在数组区间仅有两个数字的时候,防止出现计算的中心点一直是左节点,导致不能退出循环;
class Solution
public int search(int[] nums, int target)
//基本二分查找模板;
int n = nums.length;
if(n < 2) return nums[0] == target ?0:-1;
int left = 0;
int right = n - 1;
while(left<right)
//计算中点;
//int middle = (right + left + 1)/2;在不考虑溢出情况时可以用这个;
int middle = left + (right-left)/2 +1;
if(nums[middle] > target)
right = middle - 1;
else
left = middle;
//判断是否存在;
if(nums[left] == target)
return left;
return -1;
或者将循环的判断条件变为
left +1 < right
由于最后会留下两个元素,注意需要在退出循环后进行两次判断;
class Solution
public int search(int[] nums, int target)
//基本二分查找模板;
int n = nums.length;
if(n < 2) return nums[0] == target ?0:-1;
int left = 0;
int right = n - 1;
while(left+1<right)
//计算中点;
int middle = left + (right-left)/2;
if(nums[middle] == target)
return middle;
else if(nums[middle] < target)
left = middle;
else
right = middle;
//判断是否存在;
if(nums[left] == target)
return left;
if(nums[right] == target)
return right;
return -1;
练习
力扣35题-搜索插入位置
原题位置:35. 搜索插入位置
给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。
如果目标值不存在于数组中,返回它将会被按顺序插入的位置。
请必须使用时间复杂度为 O(log n) 的算法。
示例 1:
输入: nums = [1,3,5,6], target = 5
输出: 2
示例 2:
输入: nums = [1,3,5,6], target = 2
输出: 1
示例 3:
输入: nums = [1,3,5,6], target = 7
输出: 4
示例 4:
输入: nums = [1,3,5,6], target = 0
输出: 0
示例 5:
输入: nums = [1], target = 0
输出: 0
提示:
1 <= nums.length <= 104
-104 <= nums[i] <= 104
nums 为无重复元素的升序排列数组
-104 <= target <= 104
(1)双指针
class Solution
public int searchInsert(int[] nums, int target)
int n = nums.length;
//由于这是已经排序的数组,则可以直接尾插;
if(nums[n-1] < target) return n;
//简易双指针;
int left = 0;
int right = n-1;
while(left < right)
int middle = left + (right-left)/2;
if(nums[middle] < target)
left = middle +1;
else
right = middle;
//由于绝对可以插入,则直接返回这个位置;
return left;
(2)之前写的双指针思路
class Solution
public int searchInsert(int[] nums, int target)
if(nums.length == 0) return 0;
List<Integer> list = new ArrayList<>();
for(int i:nums)
list.add(i);
//二分查找类型的题目;先定义左右指针;
int left = 0;
int right = nums.length-1;
//中心值;
int middle = 0;
while(left<=right)
middle = left + (right - left)/2;
if(nums[middle] == target)
return middle;
//若中心点大于目标值,则在左边查找;
if(nums[middle] > target)
right = middle - 1;
//若小于目标值,则在右边查找;
if(nums[middle] < target)
left = middle+1;
//若没找到,则返回新的添加位置;
return right+1;
(3)使用辅助变量记录
class Solution
public int searchInsert(int[] nums, int target)
int n = nums.length;
//由于这是已经排序的数组,则可以直接尾插;
if(nums[n-1] < target) return n;
//简易双指针;
int left = 0;
int right = n-1;
//辅助变量;
int res = 0;
while(left <= right)
int middle = left + (right-left)/2;
if(nums[middle] >= target)
res = middle;
right = middle -1;
else
left = middle +1;
return res;
力扣34题:在排序数组中查找元素的第一个和最后一个位置
原题位置:34. 在排序数组中查找元素的第一个和最后一个位置
题目要求
给定一个按照升序排列的整数数组 nums,和一个目标值 target。
找出给定目标值在数组中的开始位置和结束位置。
如果数组中不存在目标值 target,返回 [-1, -1]。
进阶:
你可以设计并实现时间复杂度为 O(log n) 的算法解决此问题吗?
示例 1:
输入:nums = [5,7,7,8,8,10], target = 8
输出:[3,4]
示例 2:
输入:nums = [5,7,7,8,8,10], target = 6
输出:[-1,-1]
示例 3:
输入:nums = [], target = 0
输出:[-1,-1]
提示:
0 <= nums.length <= 105
-109 <= nums[i] <= 109
nums 是一个非递减数组
-109 <= target <= 109
使用两次二分查找;
注意使用循环判断条件为 left < right 时;
由于本题需要查询第一次出现的位置和最后一次出现的位置;
则第一次出现位置查询 时 注重于查询左侧,
但最后一次出现位置查询时注重于查询右侧;
还要注意之前提到的边界情况为left = middle;
的循环退出问题,需要向上取整;
class Solution
public int[] searchRange(int[] nums, int target)
int[] res = new int[2];
int n = nums.length;
//排除特殊情况;
if(n == 0)
return new int[]-1,-1;
//使用二分查找的方法,注意需要使用两次;
int start = binarySearchStart(nums,target);
int end = binarySearchEnd(nums,target);
res[0] = start;
res[1] = end;
return res;
//查找第一次出现的元素位置;
private int binarySearchStart(int[] nums,int target)
int left = 0;
int right = nums.length-1;
while(left < right )
//中心点;
int middle = left + (right-left)/2;
if(target > nums[middle])
//右侧;
left = middle +1;
else
//target <= nums[middle]
//左侧存在;
right = middle;
//由于无法确定是否存在,这里需要判定;
if(nums[left] == target)
return left;
return -1;
//查找最后一次出现的位置;
private int binarySearchEnd(int[] nums,int target)
int left =0;
int right =nums.length-1;
while(left < right)
//中心点,这里主要注意防止死循环问题,需要向上取整;
int middle = left + (right - left )/2 +1;
if(target < nums[middle])
//左侧;
right = middle - 1;
else
//target >= nums[middle];
//右侧存在;
left = middle;
//由于无法确定是否存在,这里需要判定;
if(nums[left] == target)
return left;
return -1;
力扣744题:寻找比目标字母大的最小字母
原题位置:744. 寻找比目标字母大的最小字母
给你一个排序后的字符列表 letters ,列表中只包含小写英文字母。
另给出一个目标字母 target,请你寻找在这一有序列表里比目标字母大的最小字母。
在比较时,字母是依序循环出现的。举个例子:
如果目标字母 target = 'z' 并且字符列表为 letters = ['a', 'b'],则答案返回 'a'
示例:
输入:
letters = ["c", "f", "j"]
target = "a"
输出: "c"
输入:
letters = ["c", "f", "j"]
target = "c"
输出: "f"
输入:
letters = ["c", "f", "j"]
target = "d"
输出: "f"
输入:
letters = ["c", "f", "j"]
target = "g"
输出: "j"
输入:
letters = ["c", "f", "j"]
target = "j"
输出: "c"
输入:
letters = ["c", "f", "j"]
target = "k"
输出: "c"
提示:
letters长度范围在[2, 10000]区间内。
letters 仅由小写字母组成,最少包含两个不同的字母。
目标字母target 是一个小写字母。
使用二分查找;
注意若找的目标字母不在数组中,返回数组的第一个字母;
class Solution
public char nextGreatestLetter(char[] letters, char target)
int n = letters.length;
//若没有或者是最后一位,直接返回第一个字母;
if(letters[n-1] <= target)
return letters[0];
//标准二分查找;
int left = 0;
int right = n-1;
while(left < right)
int middle = left + (right - left) /2;
if(target >= letters[middle])
left = middle+1;
else
right = middle;
return letters[left];
力扣275题:H 指数 II
原题位置:275. H 指数 II
题目说明:
给你一个整数数组 citations ,
其中 citations[i] 表示研究者的第 i 篇论文被引用的次数,
citations 已经按照 升序排列 。
计算并返回该研究者的 h 指数。
h 指数的定义:h 代表“高引用次数”(high citations),
一名科研人员的 h 指数是指他(她)的 (n 篇论文中)
总共有 h 篇论文分别被引用了至少 h 次。
且其余的 n - h 篇论文每篇被引用次数 不超过 h 次。
提示:如果 h 有多种可能的值,h 指数 是其中最大的那个。
请你设计并实现对数时间复杂度的算法解决此问题。
示例 1:
输入:citations = [0,1,3,5,6]
输出:3
解释:
给定数组表示研究者总共有 5 篇论文,
每篇论文相应的被引用了 0, 1, 3, 5, 6 次。
由于研究者有 3 篇论文每篇 至少 被引用了 3 次,
其余两篇论文每篇被引用 不多于 3 次,所以她的 h 指数是 3 。
示例 2:
输入:citations = [1,2,100]
输出:2
提示:
n == citations.length
1 <= n <= 105
0 <= citations[i] <= 1000
citations 按 升序排列
H指数 = res, 论文引用次数至少使用了当前 res 个的 论文数量;
实际就是将论文作为一个区间;找到普遍引用高的论文开始位置;
最终计算出高引用量的论文数量;
class Solution
public int hIndex(int[] citations算法复习:二分查找
算法二分法 ② ( 排序数组中查找目标值 | 二分法的经典写法 | 在排序数组中查找元素的最后一个位置 | 二分法的通用模板 )