今天是2月1号,参加代码随想录训练营第一天,做了好几道题,希望能一直坚持下去!
今天的任务是数组理论基础。主要学习的知识点是二分法和双指针。例题是704. 二分查找和27. 移除元素。
二分法
Leetcode 704. Binary Search
这是一道二分法的模版题。对于解二分法的题,我比较习惯背Acwing里y总的算法模版。
class Solution {
public:
int search(vector<int>& nums, int target) {
int l = 0, r = nums.size() - 1;
int mid;
while(l < r){
mid = l + r >> 1;
if(nums[mid] >= target) r = mid;
else l = mid + 1;
}
if(nums[r] != target) return -1;
return r;
}
};
其中if(nums[mid] >= target)
中的语句为check函数。
这个算法有两种更新方式:
- mid = l + r + 1 >> 1
那么当check的结果为true时,区间应更新为[mid, r],更新方式为l = mid
。 当check的结果为true时,区间应更新为[l, mid - 1],更新方式为r = mid - 1
。 - mid = l + r >> 1
那么当check的结果为true时,区间应更新为[l, mid],更新方式为r = mid
。 当check的结果为true时,区间应更新为[mid + 1, r],更新方式为l = mid + 1
。
如何选择呢?
先写check函数,想如何更新。如果更新方式是l = mid, r = mid - 1
要补上+1。
又复习了几道去年暑假做的二分法的题,已经忘了咋做了,真的要经常复习。
Leetcode 33. Search in Rotated Sorted Array
class Solution {
public:
int search(vector<int>& nums, int target) {
int l = 0, r = nums.size() - 1;
while(l < r){
int mid = l + r + 1 >> 1;
if(nums[mid] >= nums[0]) l = mid;
else r = mid - 1;
}
if(target >= nums[0]) l = 0;
else l = r + 1, r = nums.size() - 1;
while(l < r){
int mid = l + r >> 1;
if(nums[mid] >= target) r = mid;
else l = mid + 1;
}
if(nums[r] != target) return -1;
return r;
}
};
这道题先通过二分法找到pivot,再判断出target所在的区间继续二分。
- Leetcode 81. Search in Rotated Sorted Array II
class Solution {
public:
bool search(vector<int>& nums, int target) {
int l = 0, r = nums.size() - 1;
while(nums[0] == nums[r] && r > 0) r --;
if(r <= 0) return nums[0] == target;
while(l < r){
int mid = l + r + 1 >> 1;
if(nums[mid] >= nums[0]) l = mid;
else r = mid - 1;
}
if(target >= nums[0]) l = 0;
else l = r + 1, r = nums.size() - 1;
while(l < r){
int mid = l + r >> 1;
if(nums[mid] >= target) r = mid;
else l = mid + 1;
}
if(nums[r] != target) return false;
return true;
}
};
这道题与上一题的区别是数组中有重复元素。如果nums[0] == nums[r]
,不满足使用二分法所需的二段性,要先去掉重复元素。
Leetcode 35. Search Insert Position
直接套用二分法模版就可以。
Leetcode 34. Find First and Last Position of Element in Sorted Array
用两次二分法,check函数分别为nums[mid] >= target
和nums[mid] <= target
。
Leetcode 69. Sqrt(x)
class Solution {
public:
int mySqrt(int x) {
int l = 0, r = x;
while(l < r){
int mid = l + r + 1ll >> 1;
if(mid <= x / mid) l = mid;
else r = mid - 1;
}
return r;
}
};
check函数如果为mid * mid <= x
会出现数据太大而Runtime Error。
这里用这种更新方式是因为要找的不是第一个i的平方大于目标数x的i,而是要最后一个i的平方不大于目标数x的i。有一个小技巧是把+1
换成+1ll
,将mid转成long long型。
Leetcode 367. Valid Perfect Square
上一题的简单版
双指针
Leetcode 27. Remove Element
class Solution {
public:
int removeElement(vector<int>& nums, int val) {
int k = 0;
for(int i = 0; i < nums.size(); i ++){
if(nums[i] != val) nums[k ++] = nums[i];
}
return k;
}
};
用k当作慢指针,i当作快指针。i不停往前走。当i遇到等于val的数k不动;当遇到不等于val的数,就把这个数复制到k所在位置,然后k继续往前走。
Leetcode 26. Remove Duplicates from Sorted Array
class Solution {
public:
int removeDuplicates(vector<int>& nums) {
int k = 0;
for(int i = 1; i < nums.size();i ++){
if(nums[i] != nums[i - 1]) nums[++ k] = nums[i];
}
return k + 1;
}
};
当nums[i] != nums[i - 1]
时,把nums[i]
复制到k所在位置,然后k往前走。
Leetcode 283. Move Zeroes
class Solution {
public:
void moveZeroes(vector<int>& nums) {
int k = 0;
for(int i = 0; i < nums.size(); i ++){
if(nums[i] != 0) nums[k ++] = nums[i];
}
for(int j = k; j < nums.size(); j ++) nums[j] = 0;
}
};
当nums[i] != 0
时,把nums[i]
复制到k所在位置,然后k往前走。
Leetcode 844. Backspace String Compare
双指针做法:
class Solution {
public:
bool backspaceCompare(string s, string t) {
return get(s) == get(t);
}
string get(string s){
int slow = 0;
for(int fast = 0; fast < s.size(); fast ++)
{
if(s[fast] != '#')
s[slow ++] = s[fast];
else if(slow != 0)
slow --;
}
s.resize(slow);
return s;
}
};
感觉用stack会简单点:
class Solution {
public:
bool backspaceCompare(string s, string t) {
return get(s) == get(t);
}
string get(string s){
string res;
for(auto c : s){
if(c == '#') {
if(res.size()) res.pop_back();
}
else res += c;
}
return res;
}
};
Leetcode 977. Squares of a Sorted Array
class Solution {
public:
vector<int> sortedSquares(vector<int>& nums) {
int n = nums.size();
vector<int> res(n);
for(int i = 0, j = n - 1, k = n - 1; i <= j;){
if(nums[i] * nums[i] >= nums[j] * nums[j]){
res[k --] = nums[i] * nums[i];
i ++;
}
else{
res[k --] = nums[j] * nums[j];
j --;
}
}
return res;
}
};
nums[i]作为数组头,nums[j]作为数组尾,谁的平方大就把谁放入res里,然后向中间移动。