【LeetCode】3.链表系列
总目录:
LeetCode系列导航目录像我这样优秀的人,本该灿烂过一生, 这么多天LeetCode刷下来,还在链表里浮沉。
0.理论基础
0.1.要点
链表是一种通过指针串联在一起的线性结构,每一个节点由两部分组成,一个是数据域一个是指针域(存放指向下一个节点的指针),最后一个节点的指针域指向null(空指针的意思)。
种类:单链表、双向链表、循环链表。
链表在内存中不是连续分布的,不能通过索引访问。
0.2.常见操作
删除节点,跳过指定节点
增加节点,增加新的指向
0.3.优缺点
1.问题
1.1.问题描述
给你一个链表的头节点 head
和一个整数 val
,请你删除链表中所有满足 Node.val == val
的节点,并返回 新的头节点 。
链接https://leetcode.cn/problems/remove-linked-list-elements/
1.2.要点
头结点的处理是个要点
1迭代,添加虚节点作头节点,返回前再去掉
2递归
1.3.代码实例
迭代
1 class Solution { 2 public: 3 ListNode* removeElements(ListNode* head, int val) { 4 struct ListNode* dummyHead = new ListNode(0, head); 5 struct ListNode* temp = dummyHead; 6 while (temp->next != NULL) { 7 if (temp->next->val == val) { 8 temp->next = temp->next->next; 9 } else { 10 temp = temp->next; 11 } 12 } 13 return dummyHead->next; 14 } 15 };
递归
1 class Solution { 2 public: 3 ListNode* removeElements(ListNode* head, int val) { 4 //本层逻辑 5 while(head!=NULL && head->val==val){ 6 head=head->next; 7 } 8 9 //中止条件 10 if(head == NULL){ 11 return head; 12 } 13 14 //递归调用 15 head->next = removeElements(head->next,val); 16 17 return head; 18 } 19 };
2.设计链表添加指定功能
2.1.问题描述
设计链表的实现。您可以选择使用单链表或双链表。单链表中的节点应该具有两个属性:val 和 next。val 是当前节点的值,next 是指向下一个节点的指针/引用。如果要使用双向链表,则还需要一个属性 prev 以指示链表中的上一个节点。假设链表中的所有节点都是 0-index 的。
在链表类中实现这些功能:
get(index):获取链表中第 index 个节点的值。如果索引无效,则返回-1。
addAtHead(val):在链表的第一个元素之前添加一个值为 val 的节点。插入后,新节点将成为链表的第一个节点。
addAtTail(val):将值为 val 的节点追加到链表的最后一个元素。
addAtIndex(index,val):在链表中的第 index 个节点之前添加值为 val 的节点。如果 index 等于链表的长度,则该节点将附加到链表的末尾。如果 index 大于链表长度,则不会插入节点。如果index小于0,则在头部插入节点。
deleteAtIndex(index):如果索引 index 有效,则删除链表中的第 index 个节点。
链接:https://leetcode.cn/problems/design-linked-list
2.2.要点
1保存好头节点
2维护好链表长度
2.3.代码实例
略
3.翻转链表
3.1.问题描述
给你单链表的头节点 head
,请你反转链表,并返回反转后的链表。
链接https://leetcode.cn/problems/reverse-linked-list/
3.2.要点
双指针,维护好前后关系而已
递归,通过层序来维护前后关系
3.3.代码实例
双指针
1 class Solution { 2 public: 3 ListNode* reverseList(ListNode* head) { 4 ListNode* cur = NULL, *pre = head; 5 while (pre != NULL) { 6 ListNode* t = pre->next; 7 pre->next = cur; 8 cur = pre; 9 pre = t; 10 } 11 return cur; 12 } 13 };
递归
1 class Solution { 2 public: 3 ListNode* reverseList(ListNode* head) { 4 if (head == NULL || head->next == NULL) { 5 return head; 6 } 7 ListNode* ret = reverseList(head->next); 8 head->next->next = head; 9 head->next = NULL; 10 return ret; 11 } 12 };
4.两两交换链表中的元素
4.1.问题描述
给你一个链表,每两个交换一下位置,并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题(即,只能进行节点交换)。
链接https://leetcode.cn/problems/swap-nodes-in-pairs/
4.2.要点
迭代,略
递归,管理当前的两个节点、下层节点的头节点,然后返回当前两个节点的头节点
4.3.代码实例
迭代,略
递归
1 class Solution { 2 public: 3 ListNode* swapPairs(ListNode* head) { 4 if (head == NULL || head->next == NULL) 5 return head; 6 ListNode* rest = head->next->next; 7 ListNode* newHead = head->next; 8 newHead->next = head; 9 head->next = swapPairs(rest); 10 return newHead; 11 } 12 };
5.删除链表倒数第n个节点
5.1.问题描述
给你一个链表,删除链表的倒数第 n
个结点,并且返回链表的头结点。
链接:https://leetcode.cn/problems/remove-nth-node-from-end-of-list/
5.2.要点
2次遍历计算长度,第二次遍历时直接取值
栈
双指针
5.3.代码实例
略
6.找到两个单链表的起始交点
6.1.问题描述
给你两个单链表的头节点 headA
和 headB
,请你找出并返回两个单链表相交的起始节点。如果两个链表没有交点,返回 null
。
链接https://leetcode.cn/problems/intersection-of-two-linked-lists-lcci/
6.2.要点
双指针,走到头后分别再从对方的起点走起,使两个指针走过相同的路程,从而到达第一个公共点。
哈希,略
6.3.代码实例
略
7.找到环形链表的入口节点
7.1.问题描述
如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos 是 -1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。
链接:https://leetcode.cn/problems/linked-list-cycle-ii
7.2.要点
哈希
快慢指针法,数学计算距离,分析入口点的数学特性:
fast每次走2步,slow每次走1步,假设走到b到c的交界处时fast==slow,
fast走过的距离:a+b+c+b,slow走过的距离:a+b,
因为fast速度是slow的2倍,因此a+b+c+b=2*(a+b),得出a=c
此时让fast和slow分别从head和b到c交点处出发,每次都只走一步,再次相交时即为环入口。
7.3.代码实例
略
8.总结
8.1.经典题
虚拟头节点
增、删、取指定序号的节点
翻转链表
倒数第k个节点
链表相交
环形链表
xxx.问题
xxx.1.问题描述
111
xxx.2.要点
222
xxx.3.代码实例
333