24. 两两交换链表中的节点
题目中传入的链表没有头结点,为了统一操作(处理头结点),创建一个虚拟头结点指向当前链表第一个结点。每一次交换两个相邻结点(令两结点分别为A、B,且A在B左侧)需要操作三个节点的指针,也就是A、B以及A前一个节点的next指针。代码中p指向A,q指向B,pre指向A前一个节点。
时间复杂度:
O
(
n
)
O(n)
O(n)
空间复杂度:
O
(
1
)
O(1)
O(1)
c++写法:
// c++
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* swapPairs(ListNode* head) {
ListNode* dummyHead = new ListNode;
dummyHead->next = head;
ListNode *pre = dummyHead, *p = head, *q = nullptr;
if(p) q = p->next;
while(p && q){
pre->next = q;
p->next = q->next;
q->next = p;
pre = p;
p = p->next;
if(p) q = p->next;
}
return dummyHead->next;
}
};
java写法:
// java
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public ListNode swapPairs(ListNode head) {
ListNode dummyHead = new ListNode(0);
dummyHead.next = head;
ListNode p = head, q = null, pre = dummyHead;
if(p!=null) q = p.next;
while(p!=null && q!=null){
pre.next = q;
p.next = q.next;
q.next = p;
pre = p;
p = p.next;
if(p!=null) q = p.next;
}
return dummyHead.next;
}
}
19. 删除链表的倒数第 N 个结点
题目中传入的链表没有头结点,为了统一操作(处理头结点),创建一个虚拟头结点指向当前链表第一个结点。删除倒数第n个结点,需要使用三个指针:fast、slow、pre。fast、slow初始指向链表第一个节点,pre初始指向头结点。
- fast指针先从头遍历链表,到第n个节点停止。
- slow、fast、pre同时向后移动,直至fast到链表尾。
上述步骤结束后slow指向的就是倒数第n个元素,pre指向slow前一个节点。
时间复杂度:
O
(
n
)
O(n)
O(n)
空间复杂度:
O
(
1
)
O(1)
O(1)
// c++
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* removeNthFromEnd(ListNode* head, int n) {
int cnt = n;
ListNode *dummyHead = new ListNode;
dummyHead->next = head;
ListNode *slow = head, *fast = head, *pre=dummyHead;
while(cnt--){
fast = fast->next;
}
while(fast){
fast = fast->next;
slow = slow->next;
pre = pre->next;
}
pre->next = slow->next;
delete slow;
return dummyHead->next;
}
};
面试题 02.07. 链表相交
本题无需额外添加头结点,因为不涉及到操作目标结点的前一个结点。
要找到两个链表的交点,需要先将两链表右对齐。
注意: 题目要求的交点是 地址相同 不是 值相同 !
时间复杂度:
O
(
n
)
O(n)
O(n)
空间复杂度:
O
(
1
)
O(1)
O(1)
// c++
/**
* 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) {
ListNode *p = headA, *q = headB;
/*右对齐操作*/
while(p && q){
p = p->next;
q = q->next;
}
if(p){
// A表更长
q = headA;
while(p){
p = p->next;
q = q->next;
}
p = headB;
}else{
// B表更长
p = headB;
while(q){
p = p->next;
q = q->next;
}
q = headA;
}
/*右对齐操作结束*/
// 寻找交点
while(p && q){
if(p==q) return p;
p = p->next;
q = q->next;
}
return nullptr;
}
};
*142. 环形链表 II
解法一
借助哈希表。创建一个哈希表,在遍历链表的同时用哈希表存储访问过的结点地址,第一个出现在hash表内的重复结点即为入环的第一个结点。
时间复杂度:
O
(
n
)
O(n)
O(n)
空间复杂度:
O
(
n
)
O(n)
O(n)
// c++
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode *detectCycle(ListNode *head) {
unordered_map<ListNode *, int> hash;
ListNode *p = head;
while(p){
if(hash[p]>0) return p;
hash[p]++;
p = p->next;
}
return nullptr;
}
};
解法二
使用快慢双指针。两个指针初始都指向头结点。快指针每次向后移动两个节点,慢指针每次向后移动一个节点,当二者第一次相遇时,将慢指针移回头结点,快指针不变。然后两个指针同时每次向后移动一个节点(快指针也变为移动一个节点),二者第二次相遇即为环入口。
将一个环形链表分为三部分:
- 链表头到环入口 (设长度为x)
- 环入口到两个指针第一次相遇的位置 (设长度为y)
- 两个指针第一次相遇的位置到环出口 (设长度为z)
在第一次相遇时,由于快指针每次移动两个节点,所以快指针走的长度是慢指针的2倍。
故有下式:
(x + y) * 2 = x + y + n * (y + z)
y+z 表示环的长度,乘n表示在两指针相遇前fast在环内走了n趟。
要找到环形入口,所以要求链表头到入口的距离,即x:
x = (n - 1) * (y + z) + z
所以在两指针相遇后有x == z。即在第一次相遇后,分别从相遇点和链表头出发,每次移动一个节点,当再次相遇时就是环的入口。
代码实现如下:
时间复杂度:
O
(
n
)
O(n)
O(n)
空间复杂度:
O
(
1
)
O(1)
O(1)
// c++
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode *detectCycle(ListNode *head) {
ListNode *fast = head, *slow = head;
while(fast && fast->next) {
fast = fast->next->next;
slow = slow->next;
if(slow == fast) {
slow = head;
break;
}
}
if(fast == nullptr || fast->next == nullptr) return nullptr;
while(fast != slow) {
fast = fast->next;
slow = slow->next;
}
return slow;
}
};