1.24. 两两交换链表中的节点 - 力扣(LeetCode)
思路如下图所示。
代码注意点:while(fast!=nullptr && fast->next != nullptr),这里是&&,不是||。也就是说剩一个节点或者指向null的时候直接返回。
另外这几根指针别被指晕了。。。。还有下面的代码有个缺陷哈~虚拟头节点最后应该delete掉。
我写的时候忘了😂。。。
ListNode* swapPairs(ListNode* head) {
if(head==nullptr||head->next==nullptr) return head;//空链表和只有一个元素的链表直接返回head;
ListNode *dummy = new ListNode(0);
ListNode *fast = head;
ListNode *slow = head;
ListNode *tmp = dummy;//临时节点,用于连接
while(fast !=nullptr && fast->next != nullptr){
fast = fast->next;
tmp->next = fast;
slow->next = fast->next;
fast->next = slow;
tmp = tmp->next->next;
fast = slow->next;
slow = slow->next;
}
return dummy->next;
}
2.19. 删除链表的倒数第 N 个结点 - 力扣(LeetCode)
这道题是双指针的经典应用
思路:让fast移动n步,然后fast和slow同时移动,直到fast走到链表末尾,然后删掉slow->next。因为这样的话,fast正好指向最后一个结点,slow正好指向要删除元素的前一个,所以要定义一个tmp去删除他!最后注意内存管理。
ListNode *removeNthFromEnd(ListNode *head, int n)
{
ListNode *dummy = new ListNode(0);
dummy->next = head;
ListNode *fast = dummy;
ListNode *slow = dummy;
while (n--)
{
fast = fast->next;
}
while (fast->next != nullptr)
{
fast = fast->next;
slow = slow->next;
}
ListNode *tmp = slow->next;
slow->next = slow->next->next;
delete tmp;//C++管理内存!!!
return dummy->next;
}
3.面试题 02.07. 链表相交 - 力扣(LeetCode)
简单来说是求两个链表相交的指针,而不是相交的指针所对应的数值。
思路一:常规方法,就是用两层循环,直到找到两个节点相等的点。
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
ListNode *a = headA;
ListNode *b = headB;
if(a==nullptr||b==nullptr) return nullptr;
for(;b!=nullptr;b = b->next){
a = headA;
while(a!=nullptr){
if(a == b){
return a;
}
a = a->next;
}
}
return nullptr;
}
优化做法:首先求出两个链表的长度,并求出两个链表长度的差值,然后让curA移动到,和curB 末尾对齐的位置。此时我们就可以比较curA和curB是否相同,如果不相同,同时向后移动curA和curB,如果遇到curA == curB,则找到交点。否则循环退出返回空指针。
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
ListNode *a = headA;
ListNode *b = headB;
int lenA = 0,lenB = 0;
while(a != nullptr){
lenA++;
a = a->next;
}
while(b != nullptr){
lenB++;
b = b->next;
}//求出A和B的长度
a = headA;
b = headB;
if(lenB>lenA){
swap(a,b);
swap(lenA,lenB);//让A最长
}
int gap = lenA - lenB;
while(gap--){
a = a->next;
}//A和B末端对齐
while(a != nullptr){
if(a == b){
return a;
}
a = a->next;
b = b->next;
}
return nullptr;
}
这道题算是二刷了,相当于是追及问题。快指针每次走两步,慢指针每次走一步。如果fast==slow证明链表有环。那么如何计算环形入口节点呢。
快指针走的距离:x+y+n(z+y)。 慢指针走的距离:x+y。由于快指针速度是慢指针二倍,所以
x+y+n(z+y)=2(x+y) => x = n(y+z)-y => 当n等于1的时候,x==z。所以转化成相遇问题了,具体细节如下方代码所示:
ListNode *detectCycle(ListNode *head)
{
if (head == nullptr || head->next == nullptr)
return nullptr;
ListNode *slow = head;
ListNode *fast = head;
while (fast != nullptr && fast->next != nullptr)
{
fast = fast->next->next;
slow = slow->next;
if (fast == slow)
{ // 相遇证明有环
ListNode *index1 = fast;
ListNode *index2 = head;
while (index1 != index2)
{
index1 = index1->next;
index2 = index2->next;
}
return index1;
}
}
return nullptr;
}
小结:
理论:
- 链表的种类主要为:单链表,双链表,循环链表
- 链表的存储方式:链表的节点在内存中是分散存储的,通过指针连在一起。
- 链表是如何进行增删改查的。
- 数组和链表在不同场景下的性能分析。
经典题目:
链表的一大问题就是操作当前节点必须要找前一个节点才能操作。这就造成了,头结点的尴尬,因为头结点没有前一个节点了。每次对应头结点的情况都要单独处理,所以使用虚拟头结点的技巧,就可以解决这个问题。
- 链表的基本操作 707. 设计链表 - 力扣(LeetCode)(虚拟头结点版)
- 获取链表第index个节点的数值
- 在链表的最前面插入一个节点
- 在链表的最后面插入一个节点
- 在链表第index个节点前面插入一个节点
- 删除链表的第index个节点的数值
迭代法&&递归法
- 两两交换链表中的结点 24. 两两交换链表中的节点 - 力扣(LeetCode)
- 删除倒数第N个节点 19. 删除链表的倒数第 N 个结点 - 力扣(LeetCode)
结合虚拟头结点 和 双指针法来移除链表倒数第N个节点。