11.21生日-快慢指针
每日一题:3248. 矩阵中的蛇
思路:直接遍历字符串数组,根本不用定义grid。。。
class Solution {
public:
int finalPositionOfSnake(int n, vector<string>& commands) {
// int grid[n][n] = {0};
// int cnt = 0;
// for (int i = 0; i < n; i++) {
// for (int j = 0; j < n; j++) {
// grid[i][j] = cnt++;
// }
// }
int res = 0;
for (auto string : commands){
if(string == "RIGHT") res += 1;
if(string == "LEFT") res -= 1;
if(string == "UP") res -= n;
if(string == "DOWN") res += n;
}
return res;
}
};
1.6 快慢指针系列
876. 链表的中间结点
思路:快慢指针
对应题目样例的两种情况,while的条件应该为(fast && fast->next),样例2中fast走到5后还能往前走一步走到空,此时slow恰好对应4节点。
class Solution {
public:
ListNode* middleNode(ListNode* head) {
ListNode* slow = head;
ListNode* fast = head;
while (fast && fast->next) {
slow = slow->next;
fast = fast->next->next;
}
return slow;
}
};
2095. 删除链表的中间节点
思路:找中间节点+删除
当需要讨论节点个数为1 、 2时,思考能否用虚拟头节点简化。
与上题综合可得出结论:
当找n/2上取整的中点时,快慢指针从head开始循环,条件为while(fast && fast->next)
当找n/2下取整的中点时,快慢指针从dummyHead开始循环,条件为while( fast->next && fast->next->next)
此时slow->next为待找点
class Solution {
public:
ListNode* deleteMiddle(ListNode* head) {
ListNode* fast = head;
ListNode* slow = head;
if(!head->next) return NULL;
if(!head->next->next) {
head->next = nullptr;
return head;
}
while(fast->next->next && fast->next && fast->next->next->next){
fast = fast->next->next;
slow = slow->next;
}
ListNode* tmp = slow->next;
slow->next = tmp->next;
return head;
}
};
运用dummyHead简化讨论情况
class Solution {
public:
ListNode* deleteMiddle(ListNode* head) {
ListNode* dummyHead = new ListNode(0 , head);
ListNode* fast = dummyHead;
ListNode* slow = dummyHead;
while(fast->next && fast->next->next){
fast = fast->next->next;
slow = slow->next;
}
ListNode* tmp = slow->next;//slow的下一个节点为待删除节点
slow->next = tmp->next;
return dummyHead->next;
}
};
234. 回文链表
思路:快慢指针找中点 + 翻转后半段,然后比较,相当于两头开始比较。
回文的定义是收尾相等,往中间收缩。
class Solution {
public:
bool isPalindrome(ListNode* head){
//快慢指针找中点(下取整)
ListNode* dummyHead = new ListNode(0 , head);
ListNode* fast = dummyHead;
ListNode* slow = dummyHead;
while(fast->next && fast->next->next){
fast = fast->next->next;
slow = slow->next;
}
//从slow->next开始翻转后半段
ListNode* pre = nullptr;
ListNode* cur = slow->next;
ListNode* tmp;
while(cur){
tmp = cur->next;
cur->next = pre;
pre = cur;
cur = tmp;
}
//cur1从头开始,pre是后半段的头开始。while里其实可以写while(pre),因为前一段的slow->next并没有被砍断,而是指向翻转后的链表尾处。
ListNode* cur1 = head;
while(cur1 && pre){
if(cur1->val != pre->val) return false;
cur1 = cur1->next;
pre = pre->next;
}
return true;
}
};
2130. 链表最大孪生和
思路:上一题回文链表的基础上每次更新最大值res
class Solution {
public:
int pairSum(ListNode* head) {
ListNode* dummyHead = new ListNode(0 , head);
ListNode* fast = dummyHead;
ListNode* slow = dummyHead;
while(fast->next && fast->next->next){
fast = fast->next->next;
slow = slow->next;
}
ListNode* pre = nullptr;
ListNode* cur = slow->next;
while(cur){
ListNode* tmp = cur->next;
cur->next = pre;
pre = cur;
cur = tmp;
}
ListNode* cur1 = head;
int res = 0;
while(pre){
int val = cur1->val + pre->val;
res = max(res , val);
pre = pre->next;
cur1 = cur1->next;
}
return res;
}
};
143. 重排链表
思路:也是回文链表的系列题
依旧翻转后半段链表,把slow->next处断开,这样前半段的长度小于等于后半段,while条件为前半段非空(cur1->next)。依次把
pre节点插入cur1与cur1->next之间,然后循环往后移。当前半段遍历完时,把后面剩余的接到cur1后(cur1 ->next = pre)。
class Solution {
public:
void reorderList(ListNode* head){
if(!head->next || !head->next->next) return;
ListNode* dummyHead = new ListNode(0 , head);
ListNode* fast = dummyHead;
ListNode* slow = dummyHead;
while(fast->next && fast->next->next){
fast = fast->next->next;
slow = slow->next;
}
ListNode* pre = nullptr;
ListNode* cur = slow->next;
while(cur){
ListNode* tmp = cur->next;
cur->next = pre;
pre = cur;
cur = tmp;
}
slow->next = nullptr;
ListNode* cur1 = head;
while(cur1->next){
ListNode* nxt = cur1->next;
ListNode* nxt1 = pre->next;
cur1->next = pre;
pre->next = nxt;
pre = nxt1;
cur1 = nxt;
}
cur1->next = pre;
}
};
141. 环形链表
思路:快慢指针
判断有无环,能碰到则有环,while条件是(fast && fast->next),如果没环循环会停止。
class Solution {
public:
bool hasCycle(ListNode *head) {
ListNode* dummyHead = new ListNode(0 , head);
ListNode* fast = dummyHead;
ListNode* slow = dummyHead;
while(fast && fast->next){
fast = fast->next->next;
slow = slow->next;
if(fast == slow) return true;
}
return false;
}
};
142. 环形链表 II
思路:快慢指针+简单的推导
如果有环那么slow与fast从head开始遍历,相遇的节点记为slow,此时再令cur1从头开始遍历,cur1与slow相遇的位置即为环的入口。
class Solution {
public:
ListNode *detectCycle(ListNode *head) {
if(!head || !head->next) return NULL;
ListNode* fast = head;
ListNode* slow = head;
while(fast && fast->next){
fast = fast->next->next;
slow = slow->next;
if(fast == slow) {
ListNode* cur = head;
while(cur != slow){
cur = cur->next;
slow = slow->next;
}
return cur;
}
}
return NULL;
}
};
457. 环形数组是否存在循环
思路:模拟
根据题意,我们可以从每个下标 i 进行出发检查,如果以某个下标 i 为出发点发现了「循环」,返回 True,否则返回 False。
唯一需要注意的细节是,当我们处理到的下标为 cur,计算下一个跳转点
next=cur+nums[cur]时,对于越过数组的情况进行处理:
如果 next 为负数:在 next 的基础上增加
n∗⌈next/n⌉,将其映射回正值;如果 next 为正数:将 next 模数组长度 n,确保不会越界。
整理一下,我们可以统一写成
next = ((cur + nums[cur]) % n + n ) % n。在 check 内部,当以下任一条件出现,则可以结束检查(令 k 为记录过程中扫描过的下标数量):
如果在检查过程中,找到了与起点相同的下标,且 k>1,说明存在符合条件的「循环」,返回 True;
如果检查过程中扫描的数量 k 超过了数组长度 n,那么根据「鸽笼原理」,必然有数被重复处理了,同时条件一并不符合,因此再处理下去,也不会到达与起点相同的下标,返回 False;
处理过程中发现不全是正数或者负数,返回 False。
拿样例图举例:
举例:当i = 0时,cur = 0 , tag = offest + 0。在while循环里会一直替换cur = 2 、3。此时nums[next] == nums[0] == tag,外面的tag一直是0的tag没换,因此能返回true。
class Solution {
const int OFFSET = 10010;
public:
bool circularArrayLoop(vector<int>& nums) {
int n = nums.size();
for(int i = 0; i < n; i++) {
int cur = i, tag = OFFSET + i;
//排除非循环节点
while(nums[cur] < OFFSET) {
int next = ((cur + nums[cur]) % n + n) % n;
//退出条件1: 自我循环
if(next == cur) break;
//发现next已经访问过,存在循环,直接返回true
if(nums[next] == tag) return true;
//退出条件2:非全正或全负
if(nums[cur] * nums[next] < 0) break;
//更新
nums[cur] = tag;
cur = next;
}
}
return false;
}
};



浙公网安备 33010602011771号