复习复习复习!!
- 1.长度最小的子数组
- 2.移动零
- 3.盛水最多的容器
- 4.旋转链表
- 5.最接近的三数之和
- 6.删除有序数组中的重复项
- 7.返回倒数第k个节点的值
- 8.四数之和
- 9.验证回文串
1.长度最小的子数组
考滑动窗口的
要注意是大于等于不是等于
看错题目一顿调
class Solution {
public:
int minSubArrayLen(int target, vector<int>& nums) {
int left = 0;
int right = 0;
int sum = 0;
int len = INT_MAX;
while(right < nums.size()){
sum += nums[right];
right++;
while(sum >= target){
len = min(len, right - left);
sum -= nums[left];
left ++;
}
}
return len == INT_MAX ? 0 : len;
}
};
2.移动零
非0元素前移,这和秒杀双指针里面的移除特定元素是一样的
最后别忘了填充
二刷debug:秒
class Solution {
public:
void moveZeroes(vector<int>& nums) {
//非0前移
int slow = 0;
for(int fast = 0; fast < nums.size(); fast ++){
if(nums[fast] != 0){
nums[slow] = nums[fast];
slow++;
}
}
//剩下部分填充0
for(; slow < nums.size(); slow ++) nums[slow] = 0;
}
};
3.盛水最多的容器
双指针秒杀里面的原题
class Solution {
public:
int maxArea(vector<int>& height) {
int left = 0, right = height.size() - 1;
int r = 0;
while(left < right){
r = max(r, min(height[right], height[left]) * (right - left));
if(height[left] < height[right]) left ++;
else right --;
}
return r;
}
};
4.旋转链表
整体思路是,将链表头尾连起来,再在新的表尾处砍断
里面有非常多的小细节,比如 k = k % length;
寻找新的表尾:成环右移k个,说明链表倒数第k个会变成表头,for (int i = 0; i < length - k - 1; i++)可以定位到表头前面一个也就是表尾
class Solution {
public:
ListNode* rotateRight(ListNode* head, int k) {
if (!head || !head->next || k == 0) return head;
// 第一步:计算链表长度并形成环形链表
ListNode* lastNode = head;
int length = 1;
while (lastNode->next) {
lastNode = lastNode->next;
length++;
}
lastNode->next = head; // 形成环形链表
// 第二步:计算实际需要移动的步数
k = k % length;
if (k == 0) {
lastNode->next = NULL; // 恢复链表
return head;
}
// 第三步:找到新的头节点和尾节点
ListNode* newTail = head;
for (int i = 0; i < length - k - 1; i++) {
newTail = newTail->next;
}
ListNode* newHead = newTail->next;
// 断开链表
newTail->next = NULL;
return newHead;
}
};
二刷debug:换了一个写法,用快慢指针做,需要注意的是快指针停下来的地方不应该是null,而是head
class Solution {
public:
ListNode* rotateRight(ListNode* head, int k) {
if(!head || !head->next || k == 0) return head;//检查链表是否为空或不需要旋转
ListNode* fast = head;
ListNode* slow = head;
ListNode* p = head;
int len = 1;
while(p->next){
p = p->next;
len ++;
}
p->next = head;
//倒数第 n 个节点变成尾节点
k = k % len;
for(int i = 0; i < k; i ++) fast = fast->next;
while(fast->next != head){//这里是head而不是null
fast = fast->next;
slow = slow->next;
}
ListNode* newHead = slow->next;
slow->next = nullptr;
return newHead;
}
};
5.最接近的三数之和
别人写的代码怎么这么优雅┭┮﹏┭┮
重点在于判断最近的,用abs绝对值
if(abs(target - close) < abs(target - closeNum)) closeNum = close
close是当前的三个元素,closeNum是最接近的,初始化为nums[0] + nums[1] + nums[2]
这个return的是和,重复不重复只是降低复杂度,不会改变答案,不用像三数之和一样死扣不重复
class Solution {
public:
int threeSumClosest(vector<int>& nums, int target) {
sort(nums.begin(), nums.end());
int closeNum = nums[0] + nums[1] + nums[2];
for(int i = 0; i < nums.size(); i ++){
if(i && nums[i] == nums[i - 1]) continue;
int left = i + 1, right = nums.size() - 1;
while(left < right){
int close = nums[i] + nums[left] + nums[right];
if(abs(target - close) < abs(target - closeNum)) closeNum = close;
if(close > target) right--;
else if(close < target) left++;
else return target;
}
}
return closeNum;
}
};
6.删除有序数组中的重复项
快慢指针,一开始没能灵活应用,还得练
二刷debug:不会
class Solution {
public:
int removeDuplicates(vector<int>& nums) {
int slow = 0, fast = 1;
while(fast < nums.size()){
if(nums[slow] == nums[fast]){
fast++;
}else{
slow++;
nums[slow] = nums[fast];
}
}
return slow + 1;
}
};
7.返回倒数第k个节点的值
假如链表长度是k,返回倒数第k个的情况要留意处理
这题题目给的k一定是合理的!
class Solution {
public:
int kthToLast(ListNode* head, int k) {
ListNode* slow = head;
ListNode* fast = head;
// fast 指针先前进 k 步
for (int i = 0; i < k; i++) {
fast = fast->next;
}
// 同时前进 slow 和 fast,直到 fast 到达链表末尾
while (fast != nullptr) {
fast = fast->next;
slow = slow->next;
}
// 此时 slow 所指向的即为倒数第 k 个节点
return slow->val;
}
};
8.四数之和
双指针秒杀原题
二刷忘记了一些细节,longlong和需要排序sort
class Solution {
public:
vector<vector<int>> fourSum(vector<int>& nums, int target) {
sort(nums.begin(), nums.end());
vector<vector<int>> res;
for(int i = 0; i < nums.size(); i ++){
if(i && nums[i] == nums[i - 1]) continue;
for(int j = i + 1; j < nums.size(); j ++){
if(j > i + 1 && nums[j] == nums[j - 1]) continue;
for(int k = j + 1, u = nums.size() - 1; k < u; k ++){//双指针
if(k > j + 1 && nums[k] == nums[k - 1]) continue;
while(k < u && (long long)nums[i] + nums[j] + nums[k] + nums[u] > target) u--;
if(k < u && (long long)nums[i] + nums[j] + nums[k] + nums[u] == target) res.push_back({nums[i], nums[j], nums[k], nums[u]});
}
}
}
return res;
}
};
9.验证回文串
先把所有字符转化成小写,并过滤掉空格和标点这类字符。然后对剩下的字符执行双指针中的两端向中心的双指针算法即可。
新学到的库函数:
- isalnum( c ):isalnum 检查字符c是不是字母或者数字,如果是的话,返回true,不是的话返回false
- tolower( c ):tolower 将字符 c 转换为小写字母。如果 c 本身是小写字母或非字母字符,返回值将与 c 相同。
class Solution {
public:
bool isPalindrome(string s) {
string sb;
for(int i = 0; i < s.size(); i ++){
char c = s[i];
if(isalnum(c)){
sb += tolower(c);
}
}
//双指针两头往中间验证
s = sb;
int left = 0, right = sb.size() - 1;
while(left < right){
if(sb[left] != sb[right]) return false;
left++;
right--;
}
return true;
}
};