链表定义与基础操作#
链表的结点定义#
1
2
3
4
5
6
7
|
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) {}
};
|
建立单链表#
建立单链表可以使用尾插法和头插法,为了方便操作这里申请了一个“dummy_head”结点,指向链表头部。使用头插法建立的链表和输入序列相反,例如输入:“1 2 3 4 5”,建立的链表为:5→4→3→2→1。
尾插法#
1
2
3
4
5
6
7
8
9
10
11
|
ListNode *create_linklist_tail_insert() {
ListNode *dummy_head = new ListNode(-1);
ListNode *node = dummy_head;
int t;
while (cin >> t) {
node->next = new ListNode(t);
node = node->next;
}
return dummy_head->next;
}
|
头插法#
1
2
3
4
5
6
7
8
9
10
11
12
|
ListNode *create_linklist_front_insert() {
ListNode *dummy_head = new ListNode(-1);
ListNode *node = nullptr;
int t;
while(cin >> t) {
node = new ListNode(t);
node->next = dummy_head->next;
dummy_head->next = node;
}
return dummy_head->next;
}
|
合并两个有序单链表#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
ListNode *merge_two_ordered_linklist(ListNode *l1, ListNode *l2) {
if (l1 == nullptr) return l2;
if (l2 == nullptr) return l1;
ListNode *dummy_head = new ListNode(-1);
ListNode *node = dummy_head;
while(l1 && l2) {
if (l1->val < l2->val) {
node->next = l1;
l1 = l1->next;
} else {
node->next = l2;
l2 = l2->next;
}
node = node->next;
}
node->next = l1? l1: l2;
return dummy_head->next;
}
|
判断链表是否有环#
通过快慢指针有效解决此类问题。快指针每次相对慢指针多走一步,当快慢指针都在链表的环内时,快指针最多多走一圈能追上慢指针。
1
2
3
4
5
6
7
8
9
10
|
bool hasCycle(ListNode *head) {
if (head == nullptr || head ->next == nullptr) return false;
ListNode *slow = head, *fast = head;
while(fast && fast->next) {
slow = slow->next;
fast = fast->next->next;
if (slow == fast) return true;
}
return false;
}
|
进阶:找到环形链表的入口结点#
给定一个链表的头节点 head
,返回链表开始入环的第一个节点。 如果链表无环,则返回 null
。
[解析]:我们找到快慢指针 $f,s$ 相遇的环内结点,此时让指针 $t$ 从链表头部出发,让 $s$ 和 $t$ 同步移动,当 $s$ 和 $t$ 相遇时,该处即为环形链表的入口结点。证明如下:
证明
设链表中环外部分的长度为 $a$,慢指针进入环后,又走了 $b$ 的距离与快指针相遇。此时,设快指针已经走完了环的 $n$ 圈,因此它走过的总距离为 $a+n(b+c)+b$。
又因为快指针走过的距离一定为慢指针的两倍,所以有:
\begin{equation}
\begin{aligned}
&a+n(b+c)+b=2(a+b)\\
\Rightarrow\quad &a=c+(n-1)(b+c)
\end{aligned}
\end{equation}
我们发现:从相遇点到入环点的距离加上 $n−1$ 圈的环长,恰好等于从链表头部到入环点的距离,所以 $s$ 和 $t$ 会在环形链表的入口结点相遇。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
ListNode *detectCycle(ListNode *head) {
if (head == nullptr || head->next == nullptr) return nullptr;
ListNode *s = head, *f = head, *t = head;
while(f && f->next) {
s = s->next;
f = f->next->next;
if (f == s) break;
}
if (f != s) return nullptr; // 无环
while(t != s) {
t = t->next, s = s->next;
}
return t;
}
|