24.两两交换链表中的节点
思路
正常模拟,使用虚拟头结点,否则每次针对头结点的时候还需单独处理。以下是通过画图来展示
通过以上三个步骤就能对相邻两节点进行交换(因此交换的前提是确保两个节点是存在的,若只剩余一个节点是不需要交换的)
操作之后,链表如下:
展开观察
记得还需移动临时节点node
class Solution {
public:
ListNode* swapPairs(ListNode* head) {
ListNode *dummyNode = new ListNode(0);
ListNode *node = dummyNode;
dummyNode->next = head;
while (node->next != NULL && node->next->next != NULL) {
ListNode *tmp = node->next;
node->next = node->next->next;
tmp->next = node->next->next;
node->next->next = tmp;
node = tmp;
}
return dummyNode->next;
}
};
19. 删除链表的倒数第 N 个结点
思路:
要想只需遍历一次就可完成操作,使用双指针,快指针最终指向链表末尾(NULL),慢指针指向待删除节点的上一个节点,因此快指针比慢指针多走 n+1 步。
- 定义fast指针和slow指针,初始值为虚拟头节点
- 移动快指针,快指针先走 n+1 步
- 同时移动快慢指针,直到快指针指向最后一个节点(null)
- 删除slow指向的下一个节点
C++代码如下
class Solution {
public:
ListNode* removeNthFromEnd(ListNode* head, int n) {
// 使用双指针,快指针最后指向空指针,快慢指针相差n个节点,
ListNode *slow, *fast;
ListNode *dummyNode = new ListNode(0);
dummyNode->next = head;
slow = dummyNode;
fast = dummyNode;
n++;
while(n-- && fast != NULL) {
fast = fast->next;
}
while(fast != NULL) {
fast = fast->next;
slow = slow->next;
}
ListNode *node = slow->next;
slow->next = slow->next->next;
delete(node);
return dummyNode->next;
}
};
面试题02.07链表相交
思路:
因为不存在环,因此一旦找到相同节点,代表着该节点之后的所有结点都是相同的,因此两个链表从该节点开始后的节点个数完全一致。
因此两个链表长度的差异出现在第一个相同结点之前,设置两个指针分别指向两个链表,并求出两个链表的长度差值为n,移动长链表的指针n个单位,然后一起移动两个指针,直到这两个指针指向的节点相同,代表着找到相同节点。
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
// 获取两个链表各自的长度,长链表优先移动几个节点
int lenA = 0, lenB = 0;
ListNode *nodeA = headA;
ListNode *nodeB = headB;
while (nodeA) {
lenA++;
nodeA = nodeA->next;
}
while (nodeB) {
lenB++;
nodeB = nodeB->next;
}
nodeA = headA;
nodeB = headB;
if (lenA > lenB) {
int n = lenA - lenB;
while (n--) {
nodeA = nodeA->next;
}
} else if (lenA < lenB) {
int n = lenB - lenA;
while (n--) {
nodeB = nodeB->next;
}
}
while (nodeA != nodeB && nodeA != NULL) {
nodeA = nodeA->next;
nodeB = nodeB->next;
}
return nodeA == NULL ? NULL : nodeA;
}
};
142.环形链表
思路:
该题考察两个知识点:
- 判断链表是否有环
- 如果有环,如何找到这个环的入口
判断链表是否有环:
使用快慢指针法,分别定义 fast 和 slow 指针,从头结点出发,fast指针每次移动两个节点,slow指针每次移动一个节点,如果fast和slow指针相遇则代表这个链表有环。
原因:fast指针一定先进入环中,如果fast和slow指针相遇,一定是在环中相遇的。
其次,当fast和slow都在环中后,fast指针相对slow指针快一个节点,近似于fast指针追逐slow指针,一定会相遇。
如果有环,如何找到环入口:
快慢指针相遇,慢指针移动了 (x + y)个节点,快指针移动了 (x + y) + n(z + y) 个节点。快指针移动的节点数量是慢指针的2倍,因此:
2*(x + y)= x + y + n * (z + y )
x + y = n * (z + y )
n = 1时, x = z。因此当快慢指针相遇后,从头结点和相遇节点出发,相遇地方则是环入口。
class Solution {
public:
ListNode *detectCycle(ListNode *head) {
ListNode *slow = head, *fast = head;
while (fast != NULL && fast->next != NULL) {
fast = fast->next->next;
slow = slow->next;
if (fast == slow) {
ListNode *node = head;
while (node != slow) {
node = node->next;
slow = slow->next;
}
return node;
}
}
return NULL;
}
};