二分查找
- 查找确切值
public int search(int [] nums, int target){
int left = 0;
int right = nums.length-1;
while(left <= right){
int mid = left + (right - left)/2;
if(nums[mid] == target){
return mid;
}else if (nums[mid] < target) left = mid + 1;
else if (nums[mid] > target) right = mid - 1;
}
return -1;
}
1.2 寻找模糊值,关键点在于条件判断
69.x 的平方根
(1)用除法代替乘法,防止越界
(2) 367.有效的完全平方数
本题中是找到不精确的平方根,那么条件就是 mid*mid <= target && (mid+1) (mid+1) >target
如果是找精确整数平方根 (midmid)==target mid是完全平方根, 但是直接转换为出发 x/mid == mid不能直接判平方根 因为5/2 = 2; if(1.0 * num/mid > mid) left = mid + 1; 排除这种情况
class Solution {
public int mySqrt(int x) {
if(x==0 || x==1) return x;
int left = 1;
int right = x/2;
while(left <= right){
int mid = left + (right - left)/2;
if(x/mid >= mid && x/(mid+1) < mid +1) return mid;
if(x/mid > mid) left = mid + 1;
if(x/mid < mid) right = mid -1;
}
return -1;
}
}
- 查找左边界
34. 在排序数组中查找元素的第一个和最后一个位置
35.搜索插入位置
对于闭区间的搜索 结束条件是 right + 1 == left left要指向大于等于target的元素的第一个位置。
(1)闭区间搜索框架不变
(2)nums[mid] == target时 压缩上边界 也就是 right = mid - 1;新的搜索区间就变成了[left ,mid-1]
疑问为啥最终left就是对的
1.如果数组中有target元素,right会不停的变为target元素左边那一个 到停止条件时 ,right+1left left就是左边界
2. 如果数组中没有该元素
1. target比所有数组元素都大, left一路mid+1,最后leftnums.length
2. target比所有数组元素都小, right一路mid-1,最终 right = -1 left = 0,这时候如果 nums[0] != target, 说明没有该元素
3. target在中间, right+1 == left的时候,num[right]一定时小于target的,num[left]时大于等于target的数,这就需要判断,若num[left] !=target 说明没有 这个可以跟2 合起来
left只有两种情况,一种是一路越界,一种是指向大于等于的第一个位置,第二终情况就可以判断是不是等于从而判断是不是左边界
public int searchL(int [] nums, int target){
int left = 0;
int right = nums.length-1;
while(left <= right){
int mid = left + (right - left)/2;
if(nums[mid] == target){
right = mid - 1;
}else if (nums[mid] < target) left = mid + 1;
else right = mid - 1;
}
if(left == nums.length || nums[left] != target) return -1;
return left;
}
- 查找右边界
结束条件: right + 1 = left
方法是不断压缩下边界, left = mid +1;left不停成为右边界的下一位置,当结束时,left一定指向一个大于或者是越界的位置,right指向小于等于target的元素,这是由就需要判断,到底有没有我们需要的元素
当然,如果 target小于所有元素,right一路减小,最后right为-1, 此时也时找步到右边界
public int searchL(int [] nums, int target){
int left = 0;
int right = nums.length-1;
while(left <= right){
int mid = left + (right - left)/2;
if(nums[mid] == target){
left = mid + 1;
}else if (nums[mid] < target) left = mid + 1;
else right = mid - 1;
}
if( right == -1 || nums[right] != target) return -1;
return right;
}
5.没有target,通过mid和左右两端比较进行空间的缩小
153 寻找旋转排序数组中的最小值
本题关键点:升序数组,且没有相同的元素
数组旋转结束 要么是第一种 要么就是变回去。
mid存在的三种情况
第一种 最小值肯定是在右边
第二种是本身或者左边
第三种是在左侧
在图一中,因为不存在相同元素,那么num[0] 一定大于num[n-1]。 使用num[mid]与num[right]比较就可以了
class Solution {
public int findMin(int[] nums) {
int left = 0;
int right = nums.length-1;
while(left < right){
int mid = left + (right - left)/2;
if(nums[mid] > nums[right]) left = mid + 1;
else if(nums[mid] <= nums[right]) right = mid;
}
return nums[left];
}
}
154. 寻找旋转排序数组中的最小值 II
关键问题:重复元素
- 先把两端的去重,那么大概形状就会回到上一题
class Solution {
public int findMin(int[] nums) {
int left = 0;
int right = nums.length - 1;
while(left < right){
while(left<right && nums[left] == nums[left+1]) left++;
while(left<right && nums[right] == nums[right-1]) right--;
int mid = left + (right - left)/2;
if(nums[mid] >nums[right]) left = mid + 1;
else if(nums[mid] <= nums[right]) right = mid;
}
return nums[right];
}
}
移除元素(双指针)
- 相向双指针
class Solution {
public int removeElement(int[] nums, int val) {
int left = 0;
int right = nums.length-1;
while(left <= right){
while(left <= right && nums[left]!=val) left++;
while(left <= right && nums[right] == val) right--;
if(left>right) break;
int temp = nums[left];
nums[left] = nums[right];
nums[right] = temp;
}
return left;
}
}
class Solution {
public int[] sortedSquares(int[] nums) {
int[] a = new int[nums.length];
int last = nums.length-1;
int left = 0;
int right = last;
while(left <= right){
if(Math.abs(nums[left]) >= Math.abs(nums[right])){
a[last--] = nums[left] * nums[left];
left++;
}else{
a[last--] = nums[right] * nums[right];
right--;
}
}
return a;
}
}
- 同向双指针(快慢双指针)
快指针 fast 。遍历作用,寻找合适的元素
慢指针 slow 。数组下标是0,slow表示符合要求元素个数,slow索引指向的位置是可以进行覆盖的位置,slow-1之前都是符合要求的元素数组
本质时依次覆盖,合适的往前覆盖,slow一定慢于或者等于fast 那么不需要管覆盖位置的死活
27. 移除元素
不停的将符合要求的元素复制到前面,保持相对顺序
class Solution {
public int removeElement(int[] nums, int val) {
int slow = 0;
for(int fast=0; fast<nums.length; fast++){
if(nums[fast]!=val){
nums[slow]= nums[fast];
slow++;
}
}
return slow ;
}
}
26.删除排序数组中的重复项
注意点:fast寻找满足要求的元素,需要跟当前已经满足的最后一个元素进行比较
(1)slow依旧指向当前满足要求的最后一个元素,那么要先自增再进行复制
(2)slow指向可以覆盖的位置,那么判断时比较 nums[fast] != nums[slow-1]
class Solution {
public int removeDuplicates(int[] nums) {
int slow = 0;
for(int fast = 1; fast<nums.length; fast++){
if(nums[fast] != nums[slow]){
slow ++;
nums[slow] = nums[fast];
}
}
return slow + 1;
}
}
模拟栈操作,slow 指向栈顶索引,指向当前可以覆盖的位置
遇到“#” slow进行回退
举例 ab#c
fast指向#时,此时slow=2 也就时覆盖位置为#,但是需要回退一个,slow = 1,也就是可覆盖位置变成b,当fast指向c时,c就可以覆盖b
本题易错点 就是首字母为# 不进行回退,continue掉就行
class Solution {
public:
bool backspaceCompare(string s, string t) {
int slow_s = edit(s);
int slow_t = edit(t);
if(slow_s != slow_t) return false;
for(int i=0; i < slow_s; i++){
if(s[i] != t[i]) return false;
}
return true;
}
int edit(string &s){
int slow = 0;
for(int fast = 0; fast < s.length(); fast++){
if(s[fast] == '#'){
if(slow == 0) continue;
else slow--; //可以覆盖的位置前移一个
}
else {
s[slow++] = s[fast];
}
}
return slow;
}
};