代码随想录训练营Day1 | 704、35、27、240
一、704–二分查找
题目链接:704、二分查找
前提:元素有序,存在已知target;
核心:确定与target比较后针对不同情况的处理,比如等于target、小于target和大于target;
途径:左闭右闭 + 左闭右开,不同解决途径对于不同情况的处理是不同的,主要体现在开区间元素是无意义的,即无法可取。
1、二分查找–左闭右闭
int left=0;
int right=nums.size()-1;
while(left<=right)
{
int mid = left+(right-left)/2;
if(nums[mid]==target)
return mid;
else if(nums[mid]>target)
right=mid-1;
else
left=mid+1;
}
return -1;
2、二分查找–左闭右开
int left=0;
int right=nums.size();
while(left<right)
{
int mid=left+(right-left)/2;
if(nums[mid]==target)
return mid;
else if(nums[mid]>target)
right=mid;//注意右开时right是取不到该值的,故只能为mid,而不能为mid-1
else
left=mid+1;
}
return -1;
扩展:35–搜索插入位置
题目链接:35、搜索插入位置
1、二分查找-左闭右闭
int left=0;
int right=nums.size()-1;
while(left<=right)
{
int mid=(right-left)/2+left;
if(nums[mid]==target)
return mid;
else if(nums[mid]>target)
right=mid-1;
else
left=mid+1;
}
return left;
2、二分查找–左闭右开
int left=0;
int right=nums.size();
while(left<right)
{
int mid=(right-left)/2+left;
if(nums[mid]==target)
return mid;
else if(nums[mid]>target)
right=mid;
else
left=mid+1;
}
return left;
二、27–移除元素
题目链接:27、移除元素
核心:针对数组元素的移除,只能令有效元素覆盖无效元素。
方法:无效元素移至数组末尾,可以逐个元素移动,也可以首尾元素移动;或者仅在有效数组中添加有效元素,即将有效元素移至数组起始,这样也能使无效元素位于数组末尾。
1、暴力法
思想是:两层for循环,外层遍历原数组元素;当遇到需移除的无效元素时,内层for循环从该无效元素的下一个元素开始,逐个覆盖前一个元素直到有效数组的最后一个元素,实质是将无效元素移到末尾;否则,即遇到有效元素则无需处理。
注意1:移除无效元素后,即更新数组后的i指向原数组的i+1,故需i–后再次判断i处元素是否有效;
注意2:移除无效元素后,有效数组的长度也需要更新。
int len=nums.size();
for(int i=0;i<len;++i)
{
if(nums[i]==val)
{//遇到val需移除此nums[i],即i+1开始的元素需前移一位,覆盖前一位的元素
for(int j=i+1;j<len;++j)
{//更新结束后,此时nums size=j
nums[j-1]=nums[j];
}
i--;//更新后的i指向原来的i+1处,故仍需再次判断nums[i]
len--;//移除一个即长度减一
}
}
return len;
2、双指针法–左右指针
思想是:令left和right分别指向有效数组的首尾元素,left指针遍历有效数组元素,一旦left指向无效元素,则与right指向元素相互交换,交换后right指向无效元素,故需更新right,即right前移一位(交换后仍需判断当前left指向元素,故此时left无需后移);否则,即left指向元素是有效的,left右移继续遍历,直至left>right遍历结束。
注意:right总是指向最后一个有效元素,故有效数组长度为right+1,即left。
int left=0;
int right=nums.size()-1;
while(left<=right)
{//左闭右闭
if(nums[left]==val)
{//遇到val时交换right元素与left元素,以及right前移(因为交换后的right元素是移除的
nums[left]=nums[right];
nums[right]=val;
right--;
}
else
left++;
}
return left;//最终返回有效数组长度right+1(right指向最后一个有效元素),即left
3、双指针–快慢指针
思想是:设置slow和fast指针,fast遍历原数组元素,slow指向有效数组的元素。
当fast指向元素有效时,添加到slow指向的有效数组中,即赋值给slow,并且slow后移一位,准备接收下一个有效元素;否则,即fast指向元素无效时,继续遍历,无需对slow进行处理。
int slow=0;
for(int fast=0;fast<nums.size();++fast)
{
if(nums[fast]!=val)
{//fast指向元素有效,则赋值给slow,且slow后移一位
nums[slow]=nums[fast];
slow++;
}
}
return slow; //返回slow不是slow+1是因为slow指向下一个有效元素
三、 74–搜索二维矩阵
题目链接:74、搜索二维矩阵
核心:整个矩阵从左上角的第一个元素到右下角的最后一个元素都是非严格递增,故可以考虑全矩阵的二分查找;也可以分别从列和行考虑二分查找,即:先由第一列元素值确定target所在的row,再在此row进行二分确定target所在的列。
class Solution {
public:
bool searchMatrix(vector<vector<int>>& matrix, int target) {
//整个矩阵从第一个元素到最后一个元素都是非严格递增,考虑二分查找,先由第一列元素确定target的row,再由二分确定与row对应的col
int rows = matrix.size();
int cols = matrix[0].size();
int left = 0;
int right = rows-1;
while(left<=right) {
int mid = (left+right)/2;
if(matrix[mid][0]>target)
right = mid-1;
else if(matrix[mid][0]<target)
left=mid+1;
else
return true;
}//退出循环时要么true,要么left>right,target可能在的row是right
if(right<0)
return false; //如果right索引值小于0,说明target不存在!
int l = 0;
int r = cols-1;
while(l<=r) {
int mid=(r+l)/2;
if(matrix[right][mid]>target)
r = mid-1;
else if(matrix[right][mid]<target)
l = mid+1;
else
return true;
}//退出循环时要么true,要么没找到target
return false;
}
};
扩展、240–搜索二维矩阵II
题目链接:240、搜索二维矩阵II
1、逐行二分查找:
核心:行、列元素分别单调递增,考虑针对每一行,逐次使用二分查找。不足之处是未考虑到上下列元素的递增关系,存在较多重复计算。
class Solution {
public:
bool searchMatrix(vector<vector<int>>& matrix, int target) {
//二分查找,遍历行,对每一行的所有列进行二分,不考虑上下行之间元素的大小关系!(存在较多重复计算)
int rows = matrix.size();
int cols = matrix[0].size();
for(int i=0;i<rows;++i) {
int left = 0;
int right = cols-1;
while(left<=right) {
int mid = (left+right)/2;
if(matrix[i][mid]<target)
left = mid+1;
else if(matrix[i][mid]>target)
right=mid-1;
else
return true;
}//退出循环时要么找到target,要么没找到需在下一行继续寻找,但此时mid对应值小于target(left=right+1,right=mid)
}
return false; //遍历完最后一行都没有返回true,说明不存在target,故返回false
}
};
2、进阶二分查找:
核心: