链表
一、数据结构介绍
(单)链表是由节点和指针构成的数据结构,每个节点存有一个值,和一个指向下一个节点 的指针,因此很多链表问题可以用递归来处理。不同于数组,链表并不能直接获取任意节点的值, 必须要通过指针找到该节点后才能获取其值。同理,在未遍历到链表结尾时,我们也无法知道链 表的长度,除非依赖其他数据结构储存长度。LeetCode 默认的链表表示方法如下。
struct ListNode {
int val;
ListNode *next;
ListNode(int x) : val(x), next(nullptr) {}
};
由于在进行链表操作时,尤其是删除节点时,经常会因为对当前节点进行操作而导致内存或 指针出现问题。有两个小技巧可以解决这个问题:一是尽量处理当前节点的下一个节点而非当前 节点本身,二是建立一个虚拟节点 (dummy node),使其指向当前链表的头节点,这样即使原链表 所有节点全被删除,也会有一个 dummy 存在,返回 dummy->next 即可。
一般来说,算法题不需要删除内存。在刷 LeetCode 的时候,如果想要删除一个节点,可以 直接进行指针操作而无需回收内存。实际做软件工程时,对于无用的内存,笔者建议尽量显式回 收,或利用智能指针。
二、经典问题
1. 链表的基本操作
给你单链表的头节点 head
,请你反转链表,并返回反转后的链表。
链表翻转是非常基础也一定要掌握的技能。我们提供了两种写法——递归和非递归,且我们建议你同时掌握这两种写法。
递归的写法为:
class Solution {
public:
ListNode* reverseList(ListNode* head, ListNode* prev = nullptr) {
if(!head){
return prev;
}
ListNode* next = head->next;
head->next = prev;
return reverseList(next, head);
}
};
非递归的写法为:
class Solution {
public:
ListNode* reverseList(ListNode* head) {
ListNode* prev = nullptr, *next;
while(head){
next = head->next;
head->next = prev;
prev = head;
head = next;
}
return prev;
}
};
将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
我们提供了递归和非递归,共两种写法。
递归的写法为:
/**
* 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* mergeTwoLists(ListNode* list1, ListNode* list2) {
if(!list1){
return list2;
}
if(!list2){
return list1;
}
if(list1->val > list2->val){
list2->next = mergeTwoLists(list1, list2->next);
return list2;
}else{
list1->next = mergeTwoLists(list1->next, list2);
return list1;
}
}
};
非递归的写法为:
/**
* 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* mergeTwoLists(ListNode* list1, ListNode* list2) {
ListNode* dummy = new ListNode(0), *node = dummy;
while(list1 && list2){
if(list1->val > list2->val){
node->next = list2;
list2 = list2->next;
}else{
node->next = list1;
list1 = list1->next;
}
node = node->next;
}
node->next = list1? list1: list2;
return dummy->next;
}
};
给你一个链表,两两交换其中相邻的节点,并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题(即,只能进行节点交换)。
利用指针进行交换操作,没有太大难度,但一定要细心。
/**
* 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* p = head, *s;
if(p && p->next){
s = p->next;
p->next = s->next;
s->next = p;
head = s;
while(p->next && p->next->next){
s = p->next->next;
p->next->next = s->next;
s->next = p->next;
p->next = s;
p = s->next;
}
}
return head;
}
};
2. 其他链表技巧
160. Intersection of Two Linked Lists
给你两个单链表的头节点 headA
和 headB
,请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点,返回 null
。
/**
* 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* l1 = headA, *l2 = headB;
while(l1 != l2){
l1 = l1? l1->next: headB;
l2 = l2? l2->next: headA;
}
return l1;
}
};
给你一个单链表的头节点 head
,请你判断该链表是否为回文链表。如果是,返回 true
;否则,返回 false
。
以 O(1) 的空间复杂度,判断链表是否回文。
先使用快慢指针找到链表中点,再把链表切成两半;然后把后半段翻转;最后比较两半是否相等。
/**
* 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:
// 主函数
bool isPalindrome(ListNode* head) {
if(!head || !head->next){
return true;
}
ListNode* slow = head, *fast = head;
while(fast->next && fast->next->next){
slow = slow->next;
fast = fast->next->next;
}
slow->next = reverseList(slow->next);
slow = slow->next;
while(slow){
if(head->val != slow->val){
return false;
}
head = head->next;
slow = slow->next;
}
return true;
}
// 辅函数 - 链表翻转
ListNode* reverseList(ListNode* head){
ListNode* prev = nullptr, *next;
while(head){
next = head->next;
head->next = prev;
prev = head;
head = next;
}
return prev;
}
};
三、巩固练习
83. Remove Duplicates from Sorted List
19. Remove Nth Node From End of List
欢迎大家共同学习和纠正指教