24.两两交换链表中的节点
题目: 给你一个链表,两两交换其中相邻的节点,并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题(即,只能进行节点交换)。
链接: https://leetcode.cn/problems/swap-nodes-in-pairs/
思路:
- 设置
dummyNode
,dummyNode->next
指向head
。 currNode
从dummyNode
开始处理,dummyNode->next
指向2,2指向1,1指向3。处理过程中存在暂存下一个节点的操作。- 循环条件:
currNode->next
和currNode->next->next
都不为空。
class Solution {
public:
ListNode* swapPairs(ListNode* head) {
ListNode* dummyNode = new ListNode(0);
dummyNode->next = head;
ListNode* currNode = dummyNode;
while(currNode->next != nullptr && currNode->next->next != nullptr){
ListNode* tempNode = currNode->next;
currNode->next = tempNode->next;
ListNode* tempNode1 = currNode->next->next;
currNode->next->next = tempNode;
tempNode->next = tempNode1;
currNode = currNode->next->next;
}
return dummyNode->next;
}
};
19.删除链表的倒数第N个节点
题目: 给你一个链表,删除链表的倒数第 n
个结点,并且返回链表的头结点。
链接: https://leetcode.cn/problems/remove-nth-node-from-end-of-list/
思路:
- 无法直接通过内置库函数获得链表的长度,所以需要依靠遍历去确定倒数第N个节点在哪里。
- 双指针法:定义一个指针
fast
,一个指针slow
。fast
先移动n
步,当fast
移动到链表末尾的nullptr
时,slow
刚好指向倒数第N个节点。(当fast
移动到链表的最后一个元素时,操作slow->next
的指向即可删除倒数第n个节点。)
class Solution {
public:
ListNode* removeNthFromEnd(ListNode* head, int n) {
ListNode* dummyNode = new ListNode(0);
dummyNode->next = head;
ListNode* fastNode = dummyNode;
ListNode* slowNode = dummyNode;
while(n--){
fastNode = fastNode->next;
}
while(fastNode->next != nullptr){
slowNode = slowNode->next;
fastNode = fastNode->next;
}
slowNode->next = slowNode->next->next;
return dummyNode->next;
}
};
面试题02.07. 链表相交
题目: 给你两个单链表的头节点headA和headB,请你找出并返回两个单链表相交的起始节点。如果两个链表没有交点,返回null。题目数据 保证 整个链式结构中不存在环。
注意,函数返回结果后,链表必须 保持其原始结构 。
链接: https://leetcode.cn/problems/intersection-of-two-linked-lists-lcci
思路:
【双指针法】两个链表的交点,不是数值相等,而是指针相等。
求出两个链表的长度,并求出两个链表长度的差值,然后让链表A的指针currA移动到使链表A和链表B末尾对齐的位置。此时再进行currA和currB的比较,如果遇到currA==currB
则找到交点。
class Solution {
public:
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
ListNode* currA = headA;
ListNode* currB = headB;
int lenA = 0, lenB = 0;
while(currA != nullptr){
lenA++;
currA = currA->next;
}
while(currB != nullptr){
lenB++;
currB = currB->next;
}
currA = headA;
currB = headB;
if(lenA < lenB){
swap(currA, currB);
swap(lenA, lenB);
}
int diff = lenA - lenB;
while(currA != nullptr && diff--){
currA = currA->next;
}
while(currA != nullptr){
if(currA == currB){
return currA;
}
currA = currA->next;
currB = currB->next;
}
return nullptr;
}
};
时间复杂度: O(m+n)
空间复杂度: O(1)
【哈希集合】使用哈希集合存储链表节点,首先遍历链表A,并将链表A中的每个节点都加入哈希集合中。然后遍历链表B,对于每个节点,判断该节点是否在哈希集合中。
- 如果当前节点不在哈希集合中,则继续遍历下一个节点;
- 如果当前的节点在哈希集合中,则后面的节点都在哈希集合中,即从当前节点开始的所有节点都在两个链表相交的部分,因此在链表B中遍历到的第一个在哈希集合中的节点就是两个链表相交的节点,返回该节点。
class Solution {
public:
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
unordered_set<ListNode*> set;
ListNode* currNode = headA;
while(currNode != nullptr){
set.insert(currNode);
currNode = currNode->next;
}
ListNode* resNode = headB;
while(set.find(resNode) == set.end() && resNode != nullptr){
resNode = resNode->next;
}
return resNode;
}
};
142.环形链表II
题目: 给定一个链表的头节点 head ,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。
如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos 是 -1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。
不允许修改 链表。
链接: https://leetcode.cn/problems/linked-list-cycle-ii
思路:
【哈希表法】一个节点一个节点地存,遇到存过的就是环形起点。
class Solution {
public:
ListNode *detectCycle(ListNode *head) {
unordered_set<ListNode*> set;
ListNode* currNode = head;
while(currNode != nullptr){
if(set.find(currNode) == set.end()){
set.insert(currNode);
currNode = currNode->next;
}
else{
return currNode;
}
}
return nullptr;
}
};
【双指针法】
- 定义两个指针
fast
和slow
,fast
一次走两步,slow
一次走一步。 - 如果有环,
fast
和slow
两个指针必定会在环中相遇。相遇是满足
x + n(y + z) + y = 2(x + m(y+z) + y)
x = (k - 1) (y + z) + z
fast
和slow
两个指针在环中相遇的位置设为index1
,原点位置设为index2
,由上述公式可推出
x + k'(y + z) = z + k'(y + z)
,所以index1
和index2
同时向前移动,每次移动一步,必定会在环的入口处相遇。- 思路为先求
fast
和slow
两个指针在环中相遇的位置,再求index1
和index2
相遇的位置。 - 注意:(注释中已标注)
class Solution {
public:
ListNode *detectCycle(ListNode *head) {
ListNode* fast = head;
ListNode* slow = head;
while(fast != nullptr && fast->next != nullptr){
fast = fast->next->next;
slow = slow->next;
if(fast == slow)//fast和slow起始位置相同,所以要先移动再找相遇点
break;
}
if(fast == nullptr || fast->next == nullptr){
return nullptr;
}
else{
ListNode* index1 = head;
ListNode* index2 = fast;
while(index1 != index2){//有可能fast和slow直接在入口处相遇,所以要先判断是否相等再移动两个index
index1 = index1->next;
index2 = index2->next;
}
return index1;
}
return nullptr;
}
};