算法长征(2)链表

链表

在对链表进行操作时,一种常用的技巧是添加一个哑节点(dummy node),它的 next 指针指向链表的头节点。这样一来,我们就不需要对头节点进行特殊的判断了。

  • 递归、辅助栈

输入一个链表的头节点,从尾到头反过来返回每个节点的值(用数组返回)

1
2
3
4
5
6
7
8
9
10
11
12
13
class Solution:
def reversePrint(self, head: ListNode) -> List[int]:
# 递归法
return self.reversePrint(head.next) + [head.val] if head else []
def reversePrint(self, head: ListNode) -> List[int]:
# 辅助栈法
if not head:
return []
ans = []
while head:
ans.append(head.val)
head = head.next
return list(reversed(ans))

定义一个函数,输入一个链表的头节点,反转该链表并输出反转后链表的头节点

  • 递归、迭代
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Solution:
def reverseList(self, head: ListNode) -> ListNode:
# 迭代
cur, pre = head, None
while cur:
tmp = cur.next # 暂存后继节点 cur.next
cur.next = pre # 修改 next 引用指向
pre = cur # pre 暂存 cur
cur = tmp # cur 访问下一节点
return pre

def reverseList(self, head: ListNode) -> ListNode:
# 递归
def recur(cur, pre):
if not cur: return pre # 终止条件
res = recur(cur.next, cur) # 递归后继节点
cur.next = pre # 修改节点引用指向
return res # 返回反转链表的头节点

return recur(head, None) # 调用递归并返回

请实现 copyRandomList 函数,复制一个复杂链表。在复杂链表中,每个节点除了有一个 next 指针指向下一个节点,还有一个 random 指针指向链表中的任意节点或者 null

  • defaultdict(),空为0

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Solution:
def copyRandomList(self, head: 'Node') -> 'Node':
# 拼接+拆分 【哈希表】
if not head:
return head
dic = collections.defaultdict()
cur = head
while cur:
dic[cur] = Node(cur.val)
cur = cur.next
cur = head
while cur:
dic[cur].next = dic[cur.next] if cur.next else None
dic[cur].random = dic[cur.random] if cur.random else None# keyerror
cur = cur.next
return dic[head]

给定一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。

1
2
3
4
5
6
7
8
9
10
11
12
class Solution:
def removeNthFromEnd(self, head: ListNode, n: int) -> ListNode:
dummy = ListNode(0, head)
first = head
second = dummy
for i in range(n):
first = first.next
while first:
first = first.next
second = second.next
second.next = second.next.next
return dummy.next

给定一个链表,返回链表开始入环的第一个节点。 从链表的头节点开始沿着 next 指针进入环的第一个节点为环的入口节点。如果链表无环,则返回 null。

为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。注意,pos 仅仅是用于标识环的情况,并不会作为参数传递到函数中。

说明:不允许修改给定的链表。

示例 1:

img

输入:head = [3,2,0,-4], pos = 1 输出:返回索引为 1 的链表节点 解释:链表中有一个环,其尾部连接到第二个节点。

1
2
3
4
5
6
7
8
9
10
11
12
13
class Solution:
def detectCycle(self, head: ListNode) -> ListNode:
fast = slow = head
while fast and fast.next:
fast = fast.next.next
slow = slow.next
if fast == slow:
fast = head
while fast != slow:
fast = fast.next
slow = slow.next
return slow
return None

给定两个单链表的头节点 headAheadB ,请找出并返回两个单链表相交的起始节点。如果两个链表没有交点,返回 null

1
2
3
4
5
6
7
8
9
class Solution:
def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> ListNode:
if not headA and not headB:
return headA
p, q = headA, headB
while p != q:
p = p.next if p else headB
q = q.next if q else headA
return p

给定两个 非空链表 l1l2 来代表两个非负整数。数字最高位位于链表开始位置。它们的每个节点只存储一位数字。将这两数相加会返回一个新的链表。

  • 从后往前加【反转链表初始化】、倒序添加
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
class Solution:
def addTwoNumbers(self, l1: ListNode, l2: ListNode) -> ListNode:
def reverseList(head: ListNode) -> ListNode:
if not head:
return head
pre, cur = None, head
while cur:
tmp = cur.next
cur.next = pre
pre = cur
cur = tmp
return pre

if not l1 and not l2:
return
l1 = reverseList(l1)
l2 = reverseList(l2)
more = 0
p = None
while l1 or l2:
# 倒序添加
val1 = l1.val if l1 else 0
val2 = l2.val if l2 else 0
total = val1 + val2 + more
more, cur = divmod(total, 10)
p = ListNode(cur, p)
l1 = l1.next if l1 else l1
l2 = l2.next if l2 else l2
if more != 0:
p = ListNode(more, p)
return p

给定一个单链表 L 的头节点 head ,单链表 L 表示为:

L0 → L1 → … → Ln-1 → Ln 请将其重新排列后变为:

L0 → Ln → L1 → Ln-1 → L2 → Ln-2 → …

不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。

  • 列表的中点、插入节点; 线性表存储
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
class Solution:
def reorderList(self, head: ListNode) -> None:
"""
Do not return anything, modify head in-place instead.
"""
def reverseList(head: ListNode) -> ListNode:
pre, cur = None, head
while cur:
tmp = cur.next
cur.next = pre
pre = cur
cur = tmp
return pre
fast, slow = head.next, head
while fast and fast.next:
slow, fast = slow.next, fast.next.next
mid, slow.next = slow.next, None
left, cur = head, reverseList(mid)
slow.next = None
while cur:
tmp = cur.next
cur.next = left.next
left.next = cur
left = cur.next
cur = tmp
return head

给定一个链表的 头节点 head 请判断其是否为回文链表。如果一个链表是回文,那么链表节点序列从前往后看和从后往前看是相同的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Solution:
def isPalindrome(self, head: ListNode) -> bool:
def reverseList(head: ListNode) -> ListNode:
pre, cur = None, head
while cur:
tmp = cur.next
cur.next = pre
pre = cur
cur = tmp
return pre
slow = fast = head
while fast.next and fast.next.next:
slow, fast = slow.next, fast.next.next
left, right = head, slow.next
slow.next = None
right = reverseList(right)
while left and right:
if left.val != right.val:
return False
left, right = left.next, right.next
return True

多级双向链表中,除了指向下一个节点和前一个节点指针之外,它还有一个子链表指针,可能指向单独的双向链表。这些子列表也可能会有一个或多个自己的子项,依此类推,生成多级数据结构,如下面的示例所示。

给定位于列表第一级的头节点,请扁平化列表,即将这样的多级双向链表展平成普通的双向链表,使所有结点出现在单级双链表中。

  • 深度优先搜索栈迭代
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
class Solution:
def flatten(self, head: 'Node') -> 'Node':
# 栈迭代
if head == None:
return head
dummy = Node(-1, None, None, None)
pre = dummy
stk = [head]
while stk:
x = stk.pop()
pre.next = x
x.prev = pre
if x.next:
stk.append(x.next)
if x.child:
stk.append(x.child)
x.child = None
pre = x
dummy.next.prev = None
return dummy.next
def flatten(self, head: 'Node') -> 'Node':
if head == None:
return head
dummy = Node(-1, None, None, None)
def dfs(pre: 'Node', cur: 'Node') -> 'Node':
if cur == None:
return pre
pre.next = cur
cur.prev = pre

nxt_head = cur.next #相当于4

tail = dfs(cur, cur.child) #相当于dfs(3, 7)
cur.child = None

return dfs(tail, nxt_head) #相当于dfs(12, 4)
dfs(dummy, head)
dummy.next.prev = None
return dummy.next

给定循环单调非递减列表中的一个点写一个函数向这个列表中插入一个新元素 insertVal ,使这个列表仍然是循环升序的。给定的可以是这个列表中任意一个顶点的指针,并不一定是这个列表中最小元素的指针。

如果有多个满足条件的插入位置,可以选择任意一个位置插入新的值,插入后整个列表仍然保持有序。如果列表为空(给定的节点是 null),需要创建一个循环有序列表并返回这个节点。否则。请返回原先给定的节点。

  • 循环链表边界判断
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Solution:
def insert(self, head: 'Node', insertVal: int) -> 'Node':
if not head:
node = Node(insertVal)
node.next = node
return node
cur = head
while True:
if cur.val <= insertVal <= cur.next.val:
cur.next = Node(insertVal, cur.next)
return head
elif cur.next.val < cur.val:
if insertVal >= cur.val or insertVal <= cur.next.val:
cur.next = Node(insertVal, cur.next)
return head
else:
# 走到原点了,还是没有
if cur.next == head:
cur.next = Node(insertVal, cur.next)
return head
cur = cur.next

列表反转二