链表
#algorithm/linkedList # 206. 反转链表
给你单链表的头节点 head
,请你反转链表,并返回反转后的链表。
示例 1:
输入:head = [1,2,3,4,5] 输出:[5,4,3,2,1]
示例 2:
输入:head = [1,2] |
示例 3:
输入:head = [] |
提示:
- 链表中节点的数目范围是
[0, 5000]
-5000 <= Node.val <= 5000
三指针
/** |
递归
/** |
/** |
160. 相交链表
给你两个单链表的头节点 headA
和 headB
,请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点,返回 null
。
图示两个链表在节点 c1
开始相交:
题目数据 保证 整个链式结构中不存在环。
注意,函数返回结果后,链表必须 保持其原始结构 。
自定义评测:
评测系统 的输入如下(你设计的程序 不适用 此输入):
intersectVal
- 相交的起始节点的值。如果不存在相交节点,这一值为0
listA
- 第一个链表listB
- 第二个链表skipA
- 在listA
中(从头节点开始)跳到交叉节点的节点数skipB
- 在listB
中(从头节点开始)跳到交叉节点的节点数
评测系统将根据这些输入创建链式数据结构,并将两个头节点 headA
和 headB
传递给你的程序。如果程序能够正确返回相交节点,那么你的解决方案将被 视作正确答案 。
示例 1:
输入:intersectVal = 8, listA = [4,1,8,4,5], listB = [5,6,1,8,4,5], skipA = 2, skipB = 3 |
示例 2:
输入:intersectVal = 2, listA = [1,9,1,2,4], listB = [3,2,4], skipA = 3, skipB = 1 |
示例 3:
输入:intersectVal = 0, listA = [2,6,4], listB = [1,5], skipA = 3, skipB = 2 |
提示:
listA
中节点数目为m
listB
中节点数目为n
1 <= m, n <= 3 * 104
1 <= Node.val <= 105
0 <= skipA <= m
0 <= skipB <= n
- 如果
listA
和listB
没有交点,intersectVal
为0
- 如果
listA
和listB
有交点,intersectVal == listA[skipA] == listB[skipB]
思路: 根据题目意思 如果两个链表相交,那么相交点之后的长度是相同的
我们需要做的事情是,让两个链表从同距离末尾同等距离的位置开始遍历。这个位置只能是较短链表的头结点位置。
为此,我们必须消除两个链表的长度差
- 指针 pA 指向 A 链表,指针 pB 指向 B 链表,依次往后遍历
- 如果 pA 到了末尾,则 pA = headB 继续遍历
- 如果 pB 到了末尾,则 pB = headA 继续遍历
比较长的链表指针指向较短链表head时,长度差就消除了 如此,只需要将最短链表遍历两次即可找到位置
source code
public class Solution { |
141. 环形链表
给你一个链表的头节点 head
,判断链表中是否有环。
如果链表中有某个节点,可以通过连续跟踪 next
指针再次到达,则链表中存在环。
为了表示给定链表中的环,评测系统内部使用整数 pos
来表示链表尾连接到链表中的位置(索引从
0
开始)。注意:pos
不作为参数进行传递 。仅仅是为了标识链表的实际情况。
如果链表中存在环 ,则返回 true
。
否则,返回 false
。
示例 1:
输入:head = [3,2,0,-4], pos = 1 |
示例 2:
输入:head = [1,2], pos = 0 |
示例 3:
输入:head = [1], pos = -1 |
提示:
- 链表中节点的数目范围是
[0, 104]
-105 <= Node.val <= 105
pos
为-1
或者链表中的一个 有效索引 。
思路: 快慢指针 fast
走两步
slow
走一步,如果是环最后肯定会相遇
source code class Solution(object):
def hasCycle(self, head):
if head == None:
return False
fast, slow = head, head
while fast is not None and fast.next is not None:
slow = slow.next
fast = fast.next.next
if slow == fast:
return True
return False
142. 环形链表 II
给定一个链表的头节点
head
,返回链表开始入环的第一个节点。 如果链表无环,则返回 null
。
如果链表中有某个节点,可以通过连续跟踪 next
指针再次到达,则链表中存在环。
为了表示给定链表中的环,评测系统内部使用整数 pos
来表示链表尾连接到链表中的位置(索引从
0
开始)。如果 pos
是 -1
,则在该链表中没有环。注意:pos
不作为参数进行传递,仅仅是为了标识链表的实际情况。
不允许修改 链表。
示例 1:
输入:head = [3,2,0,-4], pos = 1 |
示例 2:
输入:head = [1,2], pos = 0 |
示例 3:
输入:head = [1], pos = -1 |
提示:
- 链表中节点的数目范围在范围
[0, 104]
内 -105 <= Node.val <= 105
pos
的值为-1
或者链表中的一个有效索引 # 92. 反转链表 II
给你单链表的头指针 head
和两个整数 left
和 right
,其中 left <= right
。请你反转从位置 left
到位置 right
的链表节点,返回 反转后的链表 。
示例 1:
输入:head = [1,2,3,4,5], left = 2, right = 4 |
示例 2:
输入:head = [5], left = 1, right = 1 |
提示:
- 链表中节点数目为
n
1 <= n <= 500
-500 <= Node.val <= 500
1 <= left <= right <= n
思路:
source code class Solution:
def reverseBetween(self, head: Optional[ListNode], left: int, right: int) -> Optional[ListNode]:
dummy = ListNode(next=head)
p0 = dummy
for _ in range(left - 1):
p0 = p0.next
pre = None
cur = p0.next
for _ in range(right - left + 1):
next = cur.next
cur.next = pre
pre = cur
cur = next
p0.next.next = cur
p0.next = pre
return dummy.next
143. 重排链表
给定一个单链表 L
的头节点 head
,单链表 L
表示为:
\(L0 → L1 → … → Ln - 1 → Ln\)
请将其重新排列后变为:
\(L0 → Ln → L1 → Ln - 1 → L2 → Ln - 2 → …\)
不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。
示例 1:
输入:head = [1,2,3,4] |
示例 2:
输入:head = [1,2,3,4,5] |
提示:
- 链表的长度范围为
[1, 5 * 104]
1 <= node.val <= 1000
思路: 找到链表的midNode, 然后反转mid作为head2
依次连接head1和head2
source code
class Solution:
def reorderList(self, head: Optional[ListNode]) -> None:
mid = self.middleNode(head)
head2 = self.reverse(mid)
while head2.next:
next = head.next
next2 = head2.next
head.next = head2
head2.next = next
head = next
head2 = next2
def reverse(self, head):
prev, cur = None, head
while cur:
next = cur.next
cur.next = prev
prev = cur
cur = next
return prev
def middleNode(self, head):
slow = fast = head
while fast and fast.next:
slow = slow.next
fast = fast.next.next
return slow
25. K 个一组翻转链表
给你链表的头节点 head
,每 k
个节点一组进行翻转,请你返回修改后的链表。
k
是一个正整数,它的值小于或等于链表的长度。如果节点总数不是 k
的整数倍,那么请将最后剩余的节点保持原有顺序。
你不能只是单纯的改变节点内部的值,而是需要实际进行节点交换。
示例 1:
输入:head = [1,2,3,4,5], k = 2 |
示例 2:
输入:head = [1,2,3,4,5], k = 3 |
提示:
- 链表中的节点数目为
n
1 <= k <= n <= 5000
0 <= Node.val <= 1000
思路:
步骤分解:
链表分区为已翻转部分+待翻转部分+未翻转部分
每次翻转前,要确定翻转链表的范围,这个必须通过 k 此循环来确定
需记录翻转链表前驱和后继,方便翻转完成后把已翻转部分和未翻转部分连接起来
初始需要两个变量
pre
和end
,pre
代表待翻转链表的前驱,end
代表待翻转链表的末尾经过k此循环,
end
到达末尾,记录待翻转链表的后继next = end.next
翻转链表,然后将三部分链表连接起来,然后重置
pre
和end
指针,然后进入下一次循环特殊情况,当翻转部分长度不足
k
时,在定位end
完成后,end==null
,已经到达末尾,说明题目已完成,直接返回即可时间复杂度为 \(O(n * K)\) 最好的情况为 \(O(n)\) 最差的情况未 \(O(n^2)\)
空间复杂度为 \(O(1)\) 除了几个必须的节点指针外,我们并没有占用其他空间
输入:head = [1,2,3,4,5], n = 2 输出:[1,2,3,5]class Solution:
def reverseKGroup(self, head: Optional[ListNode], k: int) -> Optional[ListNode]:
dummy = ListNode(0)
dummy.next = head
pre = dummy
end = dummy
while end.next:
for i in range(k):
end = end.next
if not end: break
if not end: break
start = pre.next
next = end.next
end.next = None
pre.next = self.reverse(start)
start.next = next
end = start
pre = end
return dummy.next
def reverse(self, head):
prev, cur = None, head
while cur:
next = cur.next
cur.next = prev
prev = cur
cur = next
return prev```
# [19. 删除链表的倒数第 N 个结点](https://leetcode.cn/problems/remove-nth-node-from-end-of-list/)
给你一个链表,删除链表的倒数第 `n` 个结点,并且返回链表的头结点。
**示例 1:**
输入:head = [1], n = 1 输出:[]
**示例 2:**输入:head = [1,2], n = 1 输出:[1]
**示例 3:**
**提示:**
- 链表中结点的数目为 `sz`
- `1 <= sz <= 30`
- `0 <= Node.val <= 100`
- `1 <= n <= sz`
**思路:**
- 设置虚拟节点 `dummy` 指向 `head`
- 设定双指针 `slow` 和 `fast` ,初始都指向虚拟节点 `dummy`
- 移动 `fast`,直到 `slow` 与 `fast` 之间相隔的元素个数为 `n`
- 同时移动 `slow` 与 `fast`,直到 `fast` 指向的为 `None`
- 将 `slow` 的下一个节点指向下下个节点
**source code**
```python
class Solution:
def removeNthFromEnd(self, head: Optional[ListNode], n: int) -> Optional[ListNode]:
dummy = ListNode(0)
dummy.next = head
slow = dummy
fast = dummy
for i in range(n - 1):
fast = fast.next
while fast.next:
slow = slow.next
fast = fast.next
slow.next = slow.next.next
return dummy.next
思路:
弹出栈的第 n
个节点就是需要删除的节点,并且目前栈顶的节点就是待删除节点的前驱节点。
source code
class Solution: |
82. 删除排序链表中的重复元素 II
给定一个已排序的链表的头 head
, 删除原始链表中所有重复数字的节点,只留下不同的数字 。返回 已排序的链表 。
示例 1:
输入:head = [1,2,3,3,4,4,5]
输出:[1,2,5]
示例 2:
输入:head = [1,1,1,2,3] |
提示:
- 链表中节点数目在范围
[0, 300]
内 -100 <= Node.val <= 100
- 题目数据保证链表已经按升序 排列
思路: 把重复节点删除即可
source code class Solution:
def deleteDuplicates(self, head: Optional[ListNode]) -> Optional[ListNode]:
if not head:
return head
dummy = ListNode(0,head)
cur = dummy
while cur.next and cur.next.next:
if cur.next.val == cur.next.next.val:
x = cur.next.val
while cur.next and cur.next.val == x:
# 代表删除节点
cur.next = cur.next.next
else:
cur = cur.next
return dummy.next
1261. 在受污染的二叉树中查找元素
给出一个满足下述规则的二叉树:
root.val == 0
- 如果
treeNode.val == x
且treeNode.left != null
,那么treeNode.left.val == 2 * x + 1
- 如果
treeNode.val == x
且treeNode.right != null
,那么treeNode.right.val == 2 * x + 2
现在这个二叉树受到「污染」,所有的 treeNode.val
都变成了 -1
。
请你先还原二叉树,然后实现 FindElements
类:
FindElements(TreeNode* root)
用受污染的二叉树初始化对象,你需要先把它还原。bool find(int target)
判断目标值target
是否存在于还原后的二叉树中并返回结果。
示例 1:
输入: |
示例 2:
输入: |
示例 3:
输入: |
提示:
TreeNode.val == -1
- 二叉树的高度不超过
20
- 节点的总数在
[1, 10^4]
之间 - 调用
find()
的总次数在[1, 10^4]
之间 0 <= target <= 10^6
思路: 方法一:哈希表 从 \(\textit{root}\) 出发 DFS
这棵树,除了传入当前节点 \(\textit{node}\),还传入需要还原的值 \(\textit{val}\)。
递归左儿子:传入 \(\textit{val}\cdot 2 + 1\)。
递归右儿子:传入 \(\textit{val}\cdot 2 + 2\)。
递归的同时,把还原后的节点值加到一个哈希表中。这样对于 \(\texttt{find}\),只需要看 \(\textit{target}\)是否在哈希表中即可。
source code class FindElements:
def __init__(self, root: Optional[TreeNode]):
s = set()
def dfs(node: Optional[TreeNode], val: int) -> None:
if node is None:
return
s.add(val)
dfs(node.left, val * 2 + 1)
dfs(node.right, val * 2 + 2)
dfs(root, 0)
self.s = s
def find(self, target: int) -> bool:
return target in self.s
148. 排序链表
给你链表的头结点 head
,请将其按 升序 排列并返回 排序后的链表 。
示例 1:
输入:head = [4,2,1,3] |
示例 2:
ww
输入:head = [-1,5,3,4,0]
输出:[-1,0,3,4,5]
输入:head = [] |
提示:
- 链表中节点的数目在范围
[0, 5 * 104]
内 -105 <= Node.val <= 105
思路:
归并排序(递归法)
source code class Solution:
def sortList(self, head: Optional[ListNode]) -%3E Optional[ListNode]:
if not head or not head.next: return head
slow, fast = head, head.next
while fast and fast.next:
fast, slow = fast.next.next, slow.next
mid, slow.next = slow.next, None
left, right = self.sortList(head), self.sortList(mid)
h = res = ListNode(0)
while left and right:
if left.val %3C right.val:
h.next, left = left, left.next
else:
h.next, right = right, right.next
h = h.next
h.next = left if left else right
return res.next