24.两两交换链表中的节点
19.删除链表的倒数第N个节点
面试题 02.07. 链表相交
142.环形链表II
总结
24. 两两交换链表中的节点
第一想法
只是对两个节点进行交换,这个比较好操作,但是两个节点交换后如何根下一个交换后的节点建立联系。制定的规则应该符合第一个交换的节点对,然后也应该符合第二个交换的节点对,所以这个规则肯定是要联系到前一个节点对中的元素,因此要引入一个头节点。
解题思路
1->2->3->4
2->1->4-_3
第一步:dummy->2
第二步:2->1
第三步:1->3
此时为 dummy->2->1->3->4->空
把cur指针指向1重复上述
第一步:1->4
第二步:4->3
第三步:3->空
此事为 dummy->2->1->4->3->空
把cur指针指向3重复上述不满足cur->next或者cur->next->next存在
代码
class Solution {
public:
ListNode* swapPairs(ListNode* head) {
if (!head || !head->next) return head;
auto dummy = new ListNode(-1);
dummy->next = head;
auto cur = dummy;
while (cur->next != nullptr && cur->next->next != nullptr){
auto a = cur->next;
auto b = cur->next->next->next;
cur->next = cur->next->next;
cur->next->next = a;
cur->next->next->next = b;
cur = cur->next->next;
}
return dummy->next;
}
};
19. 删除链表的倒数第N个结点
第一想法
出现需要删除第一个节点的,一定需要一个虚拟头节点辅助。要判断什么时候结束循环,然后把next指向进行修改。
class Solution {
public:
ListNode* removeNthFromEnd(ListNode* head, int n) {
auto dummy = new ListNode(-1);
dummy->next = head;
auto cur = head, pre = dummy;
for (int i = 0; i < n; i ++) cur = cur->next;
while (cur != nullptr){
pre = pre->next;
cur = cur->next;
}
pre->next = pre->next->next;
return dummy->next;
}
};
面试题 02.07. 链表相交
第一想法
a,b两个ListNode指针分别指向两个头节点,如果谁先到达空节点,则指向另一个头节点,完成一次不同指向后一定能判断是否是相交的,如果不相交则最后会在空节点碰面。
解题思路
- 围城一个圈,看是否有交点,同上
- 两个链表右端对其,然后从共同的起点开始看是否有相同的节点。
代码
class Solution {
public:
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
ListNode* a = headA;
ListNode* b = headB;
while (a != b){
if (a != nullptr) a = a->next;
else a = headB;
if (b != nullptr) b = b->next;
else b = headA;
}
return a;
}
};
class Solution {
public:
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
int lenA = 0, lenB= 0;
auto curA = headA, curB = headB;
while (curA != nullptr){
lenA ++;
curA = curA->next;
}
while (curB != nullptr){
lenB ++;
curB = curB->next;
}
if (lenB > lenA){
swap(lenB, lenA);
swap(headA, headB);
}
int gap = lenA - lenB;
while (gap --) headA = headA->next;
while (headA != headB){
if (headA) headA = headA->next;
if (headB) headB = headB->next;
}
return headA;
}
};
142. 环形链表
第一想法
用快慢指针指向头节点,slow = slow->next
,fast = fast->next->next
,所以slow
走了a + b,fast
走了a+2b+c的路程,a = c
,因此相遇后把其中一个指针指向头节点,然后再次相遇就是环的起始位置。
存在问题
- 循环什么时候结束?
此时用fast != slow
来判断是不行的,因为初始化时都指向头节点。
其中fast
指针存在环的话是不会指向空节点的,如果没环的话每次走两格,最后下一个节点是空,或者下下个是空。
代码
class Solution {
public:
ListNode *detectCycle(ListNode *head) {
auto slow = head, fast = head;
for (; fast != nullptr && fast->next != nullptr;){
slow = slow->next;
fast = fast->next->next;
// cout << fast->val << slow->val << endl;
if (fast == slow){
fast = head;
while (fast != slow){
fast = fast->next;
slow = slow->next;
}
return fast;
}
}
return nullptr;
}
};
总结
1. 链表基础知识
链表是非连续存储的,增加和删除是O(1)
,但是查找是o(n)
的。
struct ListNode{
int val;
ListNode* next;
ListNode(int x): val(x), next(nullptr) {}
};
2. 题目类型
- 链表基本操作:增删改查,会写整个类方法
- 对头节点进行操作:虚拟头节点
- 反转链表:完全反转和部分反转,对于更改
next
的顺序和结束条件需要注意 - 删除节点:结束条件
- 链表相交 有环:方法类似,都是看如果有环第二次转一定有相交的