算法--二分查找(二)
算法原理
1.二分算法的特点:与其他算法相比,在未了解本质的情况下是最恶心、细节最多、最容易写出死循环的算法,但当我们了解了二分算法的本质后,他将变得十分轻松
2.学习的侧重点
1.算法原理(***很重要)
2.模版
1.朴素的二分模版 十分简单,我们之前就有接触过,有限制

2.查找左边界的二分模版 2,3细节多,适用范围广
3.查找右边界的二分模版
while(left<right){
int mid=left+(right-left)/2;
if(...){
left=mid+1;
}else if(..){
right=mid;
}else{
return
}
}
while(left<right){
int mid=left+(right-left+1)/2;
if(...){
left=mid;
}else if(..){
right=mid-1;
}else{
return
}
}
题目解析
1.山脉数组的峰顶索引
https://leetcode.cn/problems/peak-index-in-a-mountain-array/description/
题目解析
给定一个山脉数组,返回山脉数组峰顶的下标
算法原理
解法一:暴力枚举,找到一个数,他左边和右边都比他小
解法二:利用二段性,进行二分法
由解法一可知峰顶左边是递增的,峰顶右边是递减的
我们可以利用这一特性将数组一份为二,如果中间的数大于mid-1,那么那么他处于山脉的左侧,我们需要让left=mid,继续进行二分,如果小于mid-1,那么他处于山脉的右侧,并且这个mid一定不是峰顶,所以right=mid-1;

代码实现
class Solution {public int peakIndexInMountainArray(int[] arr) {int left=1,right=arr.length-2;while(left<right){int mid=left+(right-left+1)/2;if(arr[mid]>arr[mid-1]){left=mid;}else{right=mid-1;}}return left;}
}
2.寻找峰值
https://leetcode.cn/problems/find-peak-element/description/
题目解析
峰值元素是指其值严格大于左右相邻值的元素
给定一个整数数组,找到峰值元素并返回其索引,注意数组可能不止一个峰值,返回任意一个即可
算法原理
数组可能出现的情况

解法一:暴力解法,从第一个位置开始,一直往后走,按照上述情况进行讨论
解法二:在解法一的基础上,发现其二段性,并利用二分法来进行优化

代码实现
class Solution {public int findPeakElement(int[] nums) {int left=0,right=nums.length-1;while(left<right){int mid=left+(right-left)/2;if(nums[mid]>nums[mid+1]){right=mid;}else{left=mid+1;}}return left;}
}
3.寻找旋转数组中的最小值
https://leetcode.cn/problems/find-minimum-in-rotated-sorted-array/description/
题目解析
给定一个旋转数组,旋转数组中的元素值各不相同,他原来是一个升序排列的数组,经历了多次旋转,请返回该数组中的最小值
旋转规则:每次旋转将数组最后一个元素插到第一个位置,其余元素保持相对位置不变
算法原理
解法一:暴力枚举
解法二:二分法
由于旋转规则的特殊性,我们很容易发现该数组具有二段性


代码实现
class Solution {public int findMin(int[] nums) {int left=0,right=nums.length-1;int n=nums.length-1;while(left<right){int mid=left+(right-left)/2;if(nums[mid]>nums[n]){left=mid+1;}else{right=mid;}}return nums[left];}
}
4.点名
https://leetcode.cn/problems/que-shi-de-shu-zi
class Solution {public int takeAttendance(int[] records) {int left=0,right=records.length-1;while(left<right){int mid=left+(right-left)/2;if(records[mid]==mid){left=mid+1;}else{right=mid;}}if(records[left]==left){return records.length;}else{return left;}}
}
https://leetcode.cn/problems/que-shi-de-shu-zi
题目解析
一个长度为n-1的递增数组中所有的数字都是唯一的,请找出0~n中确实的数字
算法原理
1.哈希表
将数组中的元素遍历到哈希表中,然后从0开始一个个看哪个哈希表中不包含
2.直接遍历找结果
3.位运算
利用^(0~n)
4.数字(高斯求和公式)
然后相减即可得缺失的数字
5.二分法

代码实现
public int takeAttendance1(int[] records) {Set<Integer> set=new HashSet<Integer>();for(int num:records){set.add(num);}int n=records.length+1;for(int i=0;i<n-1;i++){if(!set.contains(i)){return i;}}return n-1;}
public int takeAttendance2(int[] records) {int n=records.length+1;for(int i=0;i<n-1;i++){if(records[i]!=i){return i;}}return n-1;}
public int takeAttendance4(int[] records) {int x=0;for(int num:records){x^=num;}int n=records.length+1;for(int i=0;i<n;i++){x^=i;}return x;}
public int takeAttendance3(int[] records) {int sum=0;for(int num:records){sum+=num;}int n=records.length+1;return n*(n-1)/2-sum;}
