文章目录
前言
关于二分法以及一维数组元素移除的一些知识点总结。
704.二分法
使用前提
- 有序数组
- 数组元素不重复
- 注意区间
二分法区间分类
1.左闭右闭区间
- 循环条件left <= right允许成立
- 当left>mid,left == mid+1;当right<mid,right == mid-1
- right==num.size()-1
2.左闭右开区间
- 循环条件left < right
- 当left>mid,left == mid+1;当eight<mid,right == mid
- right==num.size()
注:最好用mid == left+(right-left)/2,防止整型溢出。
本题代码实现
1. 左闭右闭法
class Solution {
public:
int search(vector<int>& nums, int target) {
int left=0,right=nums.size()-1;
//左闭右闭
while(left<=right){
int mid=(right-left)/2+left;
if(nums[mid]>target)right=mid-1;
else if(nums[mid]<target)left=mid+1;
else return mid;
}
return -1;
}
};
2. 左闭右开
class Solution {
public:
int search(vector<int>& nums, int target) {
int left=0,right=nums.size();
//左闭右开
while(left<right){
int mid=(right-left)/2+left;
if(nums[mid]>target)right=mid;
else if(nums[mid]<target)left=mid+1;
else return mid;
}
return -1;
}
};
其他题目
1. 值查找
下面是我的第一思路,就是直接把插入元素情况分成:1.小于最小 、2.大于最大、3.可以插入在数组内
情况1和情况2很简单,就直接插入在头或尾;
情况3我又细分成了下面三种情况:
- 当nums[left]>nums[mid],在这种情况下,有可能nums[mid-1]<target,这时直接就找到了插入位置,否则left=mid+1;
- 当nums[left]<nums[mid],有可能nums[mid+1]>target,这时也直接就找到了插入位置,right=mid-1;
- 当nums[left]=nums[mid],那么就不需要操作,直接返回mid就好了。
而最后要写一个return 0是函数的硬性要求,其实插入情况已经包含在前面了,这个return不管返回的是什么就满足题意
class Solution {
public:
int searchInsert(vector<int>& nums, int target) {
int left=0,right=nums.size()-1;
if(nums[0]>target)return 0;
if(nums[nums.size()-1]<target)return nums.size();
//左闭右闭
while(left<=right){
int mid=(right-left)/2+left;
if(nums[mid]>target){
if(nums[mid-1]<target){
return mid;
}
right=mid-1;
}
else if(nums[mid]<target){
if(nums[mid+1]>target)return mid+1;
left=mid+1;
}
else return mid;
}
return 0;
}
};
不过还有一种更简单的方法就是直接查找,如果真的有nums[mid]==target,就返回下标;
否则就是没有找到和target相同的元素,这时跳出while循环后,right肯定刚好小于left,right的下标则为刚好小于target的最大值,返回right+1即可。
题目是查找一个整数值,用二分模板即可解决
class Solution {
public:
bool isPerfectSquare(int x) {
int left=0,right=x,ans;
while(left<=right){
int mid=(right-left)/2+left;
if((long long)mid*mid<x){
ans=mid;
left=mid+1;
}
else if((long long)mid*mid>x){
right=mid-1;
}
else return true;
}
return false;
}
};
2. 边界查找
查找左边界时,当nums[mid] == target 时不要立即返回,而要收紧右侧边界以锁定左侧边界
查找右边界时,当nums[mid] == target 时不要立即返回,而要收紧左侧边界以锁定右侧边界
下面的函数都是以查找左边界为准,所以在查找target右边界时选择传入target+1,这样只需要找到target+1的左边界,其值减1则是target的右边界。
class Solution {
public:
int binary_search(vector<int>& nums, int target) {
int left = 0;
int right = nums.size() - 1;
while (left <= right) {
int mid = (left + right) / 2;
if (nums[mid] >= target) {
right = mid - 1;
} else {
left = mid + 1;
}
}
return right + 1;
}
vector<int> searchRange(vector<int>& nums, int target) {
int left_idx = binary_search(nums, target); // 找到左边界
int right_idx = binary_search(nums, target + 1) - 1; // 找到下一个数的左边界后减1
if (left_idx <= right_idx) { // 只要left <= right 就合法
return vector<int>{left_idx, right_idx};
}
return vector<int>{-1, -1};
}
};
因为题目要求省略小数点位数,题目也就相当于找左边界,和上一题一样
class Solution {
public:
int mySqrt(int x) {
int left=0,right=x,ans;
while(left<=right){
int mid=(right-left)/2+left;
if((long long)mid*mid<=x){
ans=mid;
left=mid+1;
}
else{
right=mid-1;
}
}
return ans;
}
};
27.移除元素
注意:数组的元素在内存地址中是连续的,不能单独删除数组中的某个元素,只能覆盖。
覆盖方法分类
1.暴力法
两个for循环,一个查找,一个覆盖
2.双指针法
同向指针:一个快指针查找,一个慢指针覆盖
双向指针 (不支持原地覆盖):因为原地覆盖之后让元素减少或不变,不需要删除的元素整体是往前移动的,所以双向移动,从左往右走的指针用来找不需要删除原地留下的元素,当读到需要删除的地方就可以停下,再让从右往左走的指针查找不需要删除的元素,读取成功后让右指针查询带的元素覆盖左指针需要删除的元素,依次类推。
本题代码实现
1.暴力法
// 时间复杂度:O(n^2)
// 空间复杂度:O(1)
class Solution {
public:
int removeElement(vector<int>& nums, int val) {
int size = nums.size();
for (int i = 0; i < size; i++) {
if (nums[i] == val) { // 发现需要移除的元素,就将数组集体向前移动一位
for (int j = i + 1; j < size; j++) {
nums[j - 1] = nums[j];
}
i--; // 因为下标i以后的数值都向前移动了一位,所以i也向前移动一位
size--; // 此时数组的大小-1
}
}
return size;
}
};
2. 单向指针
class Solution {
public:
int removeElement(vector<int>& nums, int val) {
int fast=0,slow=0;
while(fast!=nums.size()){
if(nums[fast]!=val)nums[slow++]=nums[fast];
fast++;
}
return slow;
}
};
3. 双向指针(不支持本题,注意理解思路)
class Solution {
public:
int removeElement(vector<int>& nums, int val) {
int left= 0;
int right=nums.size()-1;
while (left<=right) {
// 找左边等于val的元素
while (left<=right&& nums[left]!=val){
++left;
}
// 找右边不等于val的元素
while (left<=right&& nums[right]==val) {
--right;
}
// 将右边不等于val的元素覆盖左边等于val的元素
if (left<right) {
nums[left++] = nums[right--];
}
}
return left; // left一定指向了最终数组末尾的下一个元素
}
};
其他题目
class Solution {
public:
int removeDuplicates(vector<int>& nums) {
int slow=0;
for(int i=0;i<nums.size();i++){
if(i>0&&nums[i]==nums[i-1])continue;
else nums[slow++]=nums[i];
}
return slow;
}
};
class Solution {
public:
void moveZeroes(vector<int>& nums) {
int slow=0,flag=0,fast=0;
for(;fast<nums.size();fast++){
if(nums[fast]==0)continue;
else nums[slow++]=nums[fast];
}
for(int i=slow;i<fast;i++){
nums[i]=0;
}
}
};
class Solution {
public:
bool backspaceCompare(string s, string t) {
int slows=0,slowy=0;
for(int fast=0;fast<s.size();fast++){
if(s[fast]=='#'){
if(slows>0)slows--;
else continue;
}
else s[slows++]=s[fast];
}
for(int fast=0;fast<t.size();fast++){
if(t[fast]=='#'){
if(slowy>0)slowy--;
else continue;
}
else t[slowy++]=t[fast];
}
if(slows!=slowy)return false;
while(slows){
slows--;slowy--;
if(s[slows]!=t[slowy])return false;
}
return true;
}
};
总结
一刷:因为昨天已经刷过这两道题了,没有记录时间。不仅是第一次写博客,更是第一次下定决心要努力改变自己,坚持坚持坚持
二刷:修改完善了一下博客,复习了之前学的东西,整体感觉还不错,继续加油。