给你一个链表的头节点head和一个整数val,请你删除链表中所有满足Node.val == val的节点,并返回新的头节点 。
示例 1:
1 2 输入:head = [1,2,6,3,4,5,6], val = 6 输出:[1,2,3,4,5]
示例 2:
1 2 输入:head = [], val = 1 输出:[]
示例 3:
1 2 输入:head = [7,7,7,7], val = 7 输出:[]
提示:
列表中的节点数目在范围 [0, 104] 内
1 <= Node.val <= 50
0 <= val <= 50
💡 思路: 思路一:无虚拟头节点
循环检查头节点是否需要删除,直到头节点的值不等于 **val**。
从头节点开始遍历,删除后续所有值为 val 的节点。 思路二:使用虚拟头节点
代码(思路一):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 impl Solution { pub fn remove_elements (head: Option <Box <ListNode>>, val: i32 ) -> Option <Box <ListNode>> { let mut head = head; while let Some (node) = head { if node.val == val { head = node.next; } else { head = Some (node); break ; } } let mut cur = &mut head; while let Some (node) = cur.as_mut () { if let Some (next_node) = &mut node.next { if next_node.val == val { node.next = next_node.next.take (); } else { *cur = &mut node.next; } } else { break ; } } head } }
代码(思路二):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 impl Solution { pub fn remove_elements (head: Option <Box <ListNode>>, val: i32 ) -> Option <Box <ListNode>> { let mut dummy = Box ::new (ListNode { val: 0 , next: head }); let mut cur = &mut dummy; while let Some (node) = cur.next.as_mut () { if node.val == val { cur.next = node.next.take (); } else { cur = cur.next.as_mut ().unwrap (); } } dummy.next } }
你可以选择使用单链表或者双链表,设计并实现自己的链表。
单链表中的节点应该具备两个属性:val 和 next 。val 是当前节点的值,next 是指向下一个节点的指针/引用。
如果是双向链表,则还需要属性 prev 以指示链表中的上一个节点。假设链表中的所有节点下标从 0 开始。
实现 MyLinkedList 类:
MyLinkedList() 初始化 MyLinkedList 对象。
int get(int index) 获取链表中下标为 index 的节点的值。如果下标无效,则返回 1 。
void addAtHead(int val) 将一个值为 val 的节点插入到链表中第一个元素之前。在插入完成后,新节点会成为链表的第一个节点。
void addAtTail(int val) 将一个值为 val 的节点追加到链表中作为链表的最后一个元素。
void addAtIndex(int index, int val) 将一个值为 val 的节点插入到链表中下标为 index 的节点之前。如果 index 等于链表的长度,那么该节点会被追加到链表的末尾。如果 index 比长度更大,该节点将 不会插入 到链表中。
void deleteAtIndex(int index) 如果下标有效,则删除链表中下标为 index 的节点。
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 输入 ["MyLinkedList", "addAtHead", "addAtTail", "addAtIndex", "get", "deleteAtIndex", "get"] [[], [1], [3], [1, 2], [1], [1], [1]] 输出 [null, null, null, null, 2, null, 3] 解释 MyLinkedList myLinkedList = new MyLinkedList(); myLinkedList.addAtHead(1); myLinkedList.addAtTail(3); myLinkedList.addAtIndex(1, 2); // 链表变为 1->2->3 myLinkedList.get(1); // 返回 2 myLinkedList.deleteAtIndex(1); // 现在,链表变为 1->3 myLinkedList.get(1); // 返回 3
提示:
0 <= index, val <= 1000
请不要使用内置的 LinkedList 库。
调用 get、addAtHead、addAtTail、addAtIndex 和 deleteAtIndex 的次数不超过 2000 。
代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 struct MyLinkedList { head: Option <Box <ListNode>>, size: usize , } struct ListNode { val: i32 , next: Option <Box <ListNode>>, } impl MyLinkedList { pub fn new () -> Self { MyLinkedList { head: None , size: 0 , } } pub fn get (&self , index: i32 ) -> i32 { if index < 0 || index >= self .size as i32 { return -1 ; } let mut current = &self .head; for _ in 0 ..index { current = ¤t.as_ref ().unwrap ().next; } current.as_ref ().unwrap ().val } pub fn add_at_head (&mut self , val: i32 ) { let new_node = Box ::new (ListNode { val, next: self .head.take (), }); self .head = Some (new_node); self .size += 1 ; } pub fn add_at_tail (&mut self , val: i32 ) { let new_node = Box ::new (ListNode { val, next: None }); if self .head.is_none () { self .head = Some (new_node); } else { let mut current = &mut self .head; while current.as_ref ().unwrap ().next.is_some () { current = &mut current.as_mut ().unwrap ().next; } current.as_mut ().unwrap ().next = Some (new_node); } self .size += 1 ; } pub fn add_at_index (&mut self , index: i32 , val: i32 ) { if index < 0 || index > self .size as i32 { return ; } if index == 0 { self .add_at_head (val); return ; } let mut current = &mut self .head; for _ in 0 ..index - 1 { current = &mut current.as_mut ().unwrap ().next; } let new_node = Box ::new (ListNode { val, next: current.as_mut ().unwrap ().next.take (), }); current.as_mut ().unwrap ().next = Some (new_node); self .size += 1 ; } pub fn delete_at_index (&mut self , index: i32 ) { if index < 0 || index >= self .size as i32 { return ; } if index == 0 { self .head = self .head.take ().unwrap ().next; self .size -= 1 ; return ; } let mut current = &mut self .head; for _ in 0 ..index - 1 { current = &mut current.as_mut ().unwrap ().next; } let node_to_remove = &mut current.as_mut ().unwrap ().next; if let Some (node) = node_to_remove { let next_node = node.next.take (); current.as_mut ().unwrap ().next = next_node; } self .size -= 1 ; } }
给你单链表的头节点head,请你反转链表,并返回反转后的链表。
示例 1:
1 2 输入:head = [1,2,3,4,5] 输出:[5,4,3,2,1]
示例 2:
1 2 输入:head = [1,2] 输出:[2,1]
示例 3:
提示:
链表中节点的数目范围是 [0, 5000]
-5000 <= Node.val <= 5000
进阶: 链表可以选用迭代或递归方式完成反转。你能否用两种方法解决这道题?
💡 思路:
迭代法:建立一个新链表None ,使用prev 指向Node,cur 指向head ,循环判断cur 是否为Some ,是的话将当前指向的node 的next 指向新链表的prev位置,并将prev前移,最后将cur在原链表中后移一位
递归法:每次递归将原链表的一个节点翻转到新链表
代码(思路一):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 impl Solution { pub fn reverse_list (head: Option <Box <ListNode>>) -> Option <Box <ListNode>> { let mut prev = None ; let mut cur = head; while let Some (mut node) = cur { let temp = node.next.take (); node.next = prev; prev= Some (node); cur = temp; } new_list } }
代码(思路二):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 impl Solution { pub fn reverse_list (head: Option <Box <ListNode>>) -> Option <Box <ListNode>> { fn reverse (prev: Option <Box <ListNode>>, cur: Option <Box <ListNode>>) -> Option <Box <ListNode>>{ match cur { Some (mut node) => { let temp = node.next.take (); node.next = prev; reverse (Some (node), temp) } None => prev } } reverse (None , head) } }
❗ 注意: 代码中take()的使用,在循环中
node.next 是一个 Option<Box<ListNode>>, Box 表示独占所有权
不能同时从 node.next 读取值并修改 **node.next**,这违反了所有权规则
当你有 mut node 的可变借用时,不能同时移动 node.next 的所有权并修改 node.next
给你一个链表,两两交换其中相邻的节点,并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题(即,只能进行节点交换)。
示例 1:
1 2 输入:head = [1,2,3,4] 输出:[2,1,4,3]
示例 2:
示例 3:
提示:
链表中节点的数目在范围 [0, 100] 内
0 <= Node.val <= 100
代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 impl Solution { pub fn swap_pairs (head: Option <Box <ListNode>>) -> Option <Box <ListNode>> { head.and_then (|mut cur| { match cur.next { Some (mut next) => { cur.next = Self ::swap_pairs (next.next); next.next = Some (cur); Some (next) }, None => Some (cur) } }) } }
给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。
示例 1:
1 2 输入:head = [1,2,3,4,5], n = 2 输出:[1,2,3,5]
示例 2:
1 2 输入:head = [1], n = 1 输出:[]
示例 3:
1 2 输入:head = [1,2], n = 1 输出:[1]
提示:
链表中结点的数目为 sz
1 <= sz <= 30
0 <= Node.val <= 100
1 <= n <= sz
进阶: 你能尝试使用一趟扫描实现吗?
💡 思路: 双指针的经典应用,如果要删除倒数第n个节点,让fast移动n步,然后让fast和slow同时移动,直到fast指向链表末尾。删掉slow所指向的节点就可以了。
代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 impl Solution { pub fn remove_nth_from_end (head: Option <Box <ListNode>>, n: i32 ) -> Option <Box <ListNode>> { let dummy = ListNode {val: -1 , next: head}; let mut fast = &dummy; let mut slow = &dummy; for _ in 0 ..n { fast = fast.next.as_ref ().unwrap (); } while let Some (ref node) = fast.next { fast = node; slow = slow.next.as_ref ().unwrap (); } #[allow(mutable_transmutes)] let slow : &mut ListNode = unsafe { std::mem::transmute (slow) }; slow.next = slow.next.take ().unwrap ().next; dummy.next } }
给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点。如果两个链表没有交点,返回 null 。
图示两个链表在节点 c1 开始相交:
题目数据 保证 整个链式结构中不存在环。
注意 ,函数返回结果后,链表必须 保持其原始结构 。
示例 1:
1 2 3 4 5 输入:intersectVal = 8, listA = [4,1,8,4,5], listB = [5,0,1,8,4,5], skipA = 2, skipB = 3 输出:Intersected at '8' 解释:相交节点的值为 8 (注意,如果两个链表相交则不能为 0)。 从各自的表头开始算起,链表 A 为 [4,1,8,4,5],链表 B 为 [5,0,1,8,4,5]。 在 A 中,相交节点前有 2 个节点;在 B 中,相交节点前有 3 个节点。
示例 2:
1 2 3 4 5 输入:intersectVal = 2, listA = [0,9,1,2,4], listB = [3,2,4], skipA = 3, skipB = 1 输出:Intersected at '2' 解释:相交节点的值为 2 (注意,如果两个链表相交则不能为 0)。 从各自的表头开始算起,链表 A 为 [0,9,1,2,4],链表 B 为 [3,2,4]。 在 A 中,相交节点前有 3 个节点;在 B 中,相交节点前有 1 个节点。
示例 3:
1 2 3 4 5 输入:intersectVal = 0, listA = [2,6,4], listB = [1,5], skipA = 3, skipB = 2 输出:null 解释:从各自的表头开始算起,链表 A 为 [2,6,4],链表 B 为 [1,5]。 由于这两个链表不相交,所以 intersectVal 必须为 0,而 skipA 和 skipB 可以是任意值。 这两个链表不相交,因此返回 null 。
提示:
listA 中节点数目为 m
listB 中节点数目为 n
0 <= m, n <= 3 * 104
1 <= Node.val <= 105
0 <= skipA <= m
0 <= skipB <= n
如果 listA 和 listB 没有交点,intersectVal 为 0
如果 listA 和 listB 有交点,intersectVal == listA[skipA + 1] == listB[skipB + 1]
进阶: 你能否设计一个时间复杂度 O(n) 、仅用 O(1) 内存的解决方案?
💡 思路 :
哈希表法
双指针法 a. 构建两个节点指针 a , b 分别指向两链表头节点 headA , headB ,做如下操作: 同时遍历两链表,遍历链表A,指针a走过的步数为a。遍历链表B,指针b走过的步数为b。此时将a指向B,b指向A。继续向下遍历直到相交点,当a,b相等时,指针指向的为相交点或空。 b. 当A,B有交点时,指针a走了a + (b - c)步,指针b走了b + (a - c)步,c为相交部分长度,c=0代表不相交,a,b为nullptr 链接:https://leetcode.cn/problems/intersection-of-two-linked-lists-lcci/solutions/1190240/mian-shi-ti-0207-lian-biao-xiang-jiao-sh-b8hn/
代码(思路一):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 class Solution {public : ListNode *getIntersectionNode (ListNode *headA, ListNode *headB) { unordered_set<ListNode *> visited; ListNode *temp = headA; while (temp != nullptr ) { visited.insert (temp); temp = temp -> next; } temp = headB; while (temp != nullptr ) { if (visited.count (temp)) { return temp; } temp = temp -> next; } return nullptr ; } };
代码(思路二):
1 2 3 4 5 6 7 8 9 10 11 class Solution {public : ListNode *getIntersectionNode (ListNode *headA, ListNode *headB) { ListNode *a = headA, *b = headB; while (a != b) { a = a != nullptr ? a->next : headB; b = b != nullptr ? b->next : headA; } return a; } };
给定一个链表的头节点 head ,返回链表开始入环的第一个节点。 如果链表无环,则返回 _null_。
如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始 )。如果 pos 是 -1,则在该链表中没有环。注意: pos 不作为参数进行传递 ,仅仅是为了标识链表的实际情况。
不允许修改 链表。
示例 1:
1 2 3 输入:head = [3,2,0,-4], pos = 1 输出:返回索引为 1 的链表节点 解释:链表中有一个环,其尾部连接到第二个节点。
示例 2:
1 2 3 输入:head = [1,2], pos = 0 输出:返回索引为 0 的链表节点 解释:链表中有一个环,其尾部连接到第一个节点。
示例 3:
1 2 3 输入:head = [1], pos = -1 输出:返回 null 解释:链表中没有环。
提示:
链表中节点的数目范围在范围 [0, 104] 内
105 <= Node.val <= 105
pos 的值为 1 或者链表中的一个有效索引
进阶: 你是否可以使用 O(1) 空间解决此题?
💡 思路:
哈希表法
双指针法
代码(思路一):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 class Solution {public : ListNode *detectCycle (ListNode *head) { unordered_set<ListNode *> visited; while (head != nullptr ) { if (visited.count (head)) { return head; } visited.insert (head); head = head->next; } return nullptr ; } };
思路二: 双指针法是解决链表类问题的常用方法,特别适用于寻找距离尾部第 K 个节点、寻找环入口、寻找公共尾部入口等场景。 在本题中,双指针会发生两次”相遇”。第一次相遇: 设置两个指针 fast 和 slow,初始都指向链表头部 head。令 fast 每次走 2 步,slow 每次走 1 步。这样可能出现两种结果:第一种结果: fast 指针到达链表末端,说明链表无环,返回 null。第二种结果: 如果链表有环,两指针必然相遇。这是因为每轮移动后,fast 与 slow 的距离会增加 1,fast 最终会追上 slow。 当 fast 与 slow 首次相遇时,我们来分析它们走过的步数: 假设链表总共有 a+b 个节点:从头部到环入口有 a 个节点(不含入口节点),环内有 b 个节点。设两指针分别走了 f 步和 s 步,则:由于 fast 速度是 slow 的两倍,所以 f=2s;同时,fast 比 slow 多走了 n 个环的长度,即 f=s+nb(两指针都走了 a 步到达环,然后在环内循环直到相遇)。联立求解得 f=2nb,s=nb,表示 fast 和 slow 分别走了 2n 个和 n 个环的长度。 那么下一步该如何找到环的入口? 从链表头部出发,走到环入口节点的步数 k 可以表示为:k=a+nb。目前 slow 已经走了 nb 步,所以只要再让它走 a 步就能到达环入口。 虽然我们不知道 a 的具体值,但可以用双指针来解决:让一个指针从头部出发,与 slow 同时前进。当这两个指针相遇时,就找到了环的入口。第二次相遇: 将 fast 重置到链表头部(此时 f=0,s=nb)。让 slow 和 fast 每次都走 1 步。当 fast 走了 a 步,slow 走到 s=a+nb 时,两指针会在环入口处相遇。此时返回任一指针所指的节点即可。
代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 class Solution {public : ListNode *detectCycle (ListNode *head) { ListNode* fast = head; ListNode* slow = head; while (true ) { if (fast == nullptr || fast->next == nullptr ) return nullptr ; fast = fast->next->next; slow = slow->next; if (fast == slow) break ; } fast = head; while (slow != fast) { slow = slow->next; fast = fast->next; } return fast; } };