24.两两交换列表中的节点
大二上数据结构期末考试的题目,刚开始做的时候还是延续着去年的错误思路,因为写过反转链表的题目,所以想着这不就是把链表分成两个两个一组然后再反转,后面发现反转过后没法再把链表按照要求连起来了,就没有思路了,但其实跟前几道题有异曲同工之妙,链表的题我自己总结出来了个小规律,那就是,每次操作链表的节点都需要找到它的前驱
这题同理,代码中的cur就是这个前驱节点
要注意终止条件在链表节点为奇数个时是最后一组只剩一个节点,也就是1->2->3当反转为2->1->3时就退出while循环即cur->next->next为空时;当为偶数个时到cur->next为空时退出循环
typedef ListNode node;
class Solution {
public:
ListNode* swapPairs(ListNode* head) {
if (head == nullptr || head->next == nullptr)return head;
node*dummy = new ListNode();
dummy->next = head;
node*cur = dummy;
while (cur!=nullptr&&cur->next!=nullptr)
{
if (cur->next->next == nullptr)break;
node*tmp = cur->next;
cur->next = tmp->next;
tmp->next = cur->next->next;
cur->next->next = tmp;
cur = tmp;
}
return dummy->next;
}
};
19.删除链表中的倒数第n个结点
这题依旧是双指针法的一个应用,大二上数据结构的ppt中有讲过,朱允刚老师yyds,有一个巧妙的思路就是让fast比slow快n步,当fast走到表尾部的NULL时slow正好走到倒数第n个节点这样就找到了目标节点,然后再用之前做过的链表删除节点操作来删除目标节点,要注意链表长度与给定n的关系来判断边界条件
typedef ListNode node;
class Solution {
public:
void deleteNode(node*head, int index) {
}
ListNode* removeNthFromEnd(ListNode* head, int n) {
if (head == nullptr)return head;
node*fast = head;
node*slow = head;
for (int i = 1; i < n; i++)
{
fast = fast->next;
if (fast == nullptr)return head;
}
while (fast!=nullptr&&fast->next!=nullptr)
{
slow = slow->next;
fast = fast->next;
}//slow就是要寻找的节点
node*dummy = new ListNode();
dummy->next = head;
node*pre = dummy;
while (pre!=nullptr&&pre->next!=slow)
{
pre = pre->next;
}//pre是slow前一个结点
node*tmp = slow;
//if (pre == nullptr)return head;
pre->next = tmp->next;
delete tmp;
tmp = nullptr;
return dummy->next;
}
};
优化版就是仅遍历一次链表来完成上述操作,因为删除需要找到目标节点的前驱节点,所以只需要让fast比slow快n+1步,就可以让slow最后指在目标节点的前驱节点上
typedef ListNode node;
class Solution {
public:
//删除链表结点时操作指针需要指向被删除结点的前一个结点
ListNode* removeNthFromEnd(ListNode* head, int n) {//快慢指针法
if (head == nullptr)return head;
node*dummy = new ListNode();
dummy->next = head;
node*fast = dummy;
node*slow = dummy;
for (int i = 0; i < n+1; i++) {
if (fast == nullptr)return dummy->next;
//当链表长度<n时就无法找到目标节点
fast = fast->next;
}
while (fast!=nullptr)
{
fast = fast->next;
slow = slow->next;
}//slow为要删结点的前一个结点
node*tmp = slow->next;
if (tmp == nullptr)return dummy->next;
slow->next = tmp->next;
delete tmp;
tmp = nullptr;
return dummy->next;
}
};
160.相交链表
朱老师的含金量还在提升!依旧是双指针,这题还是先看的课内老师所讲的思路,两个链表相交时候先计算两个链表的长度和它们的差n,先让两个指针pa,pb都指向两个链表的head节点,pa指向的链表总是较长的那一支,然后让pa先走n步,也就是进行一个对齐的操作,再将两个指针同时移动,若在为空前有相等的情况则第一次相等的那个结点就是目标节点,不然两个链表就不相交
typedef ListNode* node;
class Solution{
public:
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB){
if (headA == nullptr || headB == nullptr)return nullptr;
node pa = headA;
node pb = headB;
int la = 0;
int lb = 0;
while (pa!=nullptr)
{
pa = pa->next;
la++;
}
while (pb!=nullptr)
{
pb = pb->next;
lb++;
}
if (la >= lb) {
pa = headA;
pb = headB;
}
else
{
pa = headB;
pb = headA;
}//pa总是较长链表的head
for (int i = 0; i < abs(la-lb); i++)
{
pa = pa->next;
}//对齐
while (pa!=pb&&pa!=nullptr&&pb!=nullptr)
{
pa = pa->next;
pb = pb->next;
}
return pa;
}
};
142.环形链表II
这题主要分为两个步骤,第一个步骤是判断是否有环,第二个步骤是若有环则找环,判断有环的思路也是双指针,定义slow与fast两个指针同时从head出发,fast一次走两步slow一次走一步,若二者相遇则有环,若fast为空二者都不相遇则无环
当链表确认链表有环时由于二者的速度关系,所以fast的路程必为slow的二倍,相遇点到环起点的距离与链表起点到环起点的距离一定相等,即a==c,所以就对应上一道相交链表题目中的两指针已对齐找交点
typedef ListNode* node;
class Solution {
public:
ListNode *detectCycle(ListNode *head) {
node slow = head;
node fast = head;
while (fast!=nullptr&&fast->next!=nullptr)//判环
{
slow = slow->next;
fast = fast->next->next;
if (slow == fast) {
//寻找入口节点,用上一道题相交链表的方法,而且数学关系可证明无需对齐
node p1 = head;
node p2 = fast;
while (p1!=p2)
{
p1 = p1->next;
p2 = p2->next;
}
return p1;
}
}
return nullptr;
}
};
补充题目 876.链表的中间结点
这题用双指针解法,要找中间结点只需要fast比slow速度快一倍就好,也就是slow每次走一步fast每次走两步,值得注意的是当结点数为偶数个的时候返回后一个中间结点,需要加一个判断条件,也就是下图中红色的fast指到最后一个结点时,判断若fast不为空但后继为空,则返回slow的后续为目标结点
typedef ListNode* node;
class Solution {
public:
ListNode* middleNode(ListNode* head) {
node dummyHead=new ListNode();
dummyHead->next=head;
node slow=dummyHead;
node fast=dummyHead;
while (fast!= nullptr&&fast->next!= nullptr){
slow=slow->next;
fast=fast->next->next;
}
if(fast!= nullptr&&fast->next== nullptr)return slow->next;
return slow;
}
};
总结
我做下来感觉链表章节的题目就是以反转删除为模板和以双指针解法为模板的两类题型,第一类题型操作时需要找到要操作节点的前驱节点,这样才能操作后面的节点;第二类题型需要想好双指针之间的距离或者速度的关系来解决相对应的场景