1、链表
链表有单链表,双链表,环形链表;链表主要功能是将离散的地址通过指针连接起来。
2、模板
链表相对比较灵活,无具体的模板可以套用;可以考虑如下几点
2.1、新建链表时,可以引入一个哨兵节点head=Node(-1),返回head.next;简化操作
2.2、环、删除第k个节点,均可使用快慢指针
3.3、修改下一个指向时,先备份,以免节点丢失
3、实例
19. 删除链表的倒数第N个节点
难度:中等
题目描述
给定一个链表,删除链表的倒数第 n 个节点,并且返回链表的头结点。
示例 1:
给定一个链表: 1->2->3->4->5, 和 n = 2.
当删除了倒数第二个节点后,链表变为 1->2->3->5.
说明:
给定的 n 保证是有效的。
解题思路
- 快慢指针
- 删除后,空指针问题。
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def removeNthFromEnd(self, head: ListNode, n: int) -> ListNode:
# 防止,删除变为空指针。
_ = ListNode(0)
_.next = head
# 快慢指针。
slow, fast = _ , _
# 快指针,后移n个节点。
for i in range(n):
fast = fast.next
# 同时移动快慢指针。当快指针到末尾时,慢指针正好在倒数第n-1个节点位置。
while (fast is not None) and (fast.next is not None) :
fast = fast.next
slow = slow.next
# 删除目标节点
slow.next = slow.next.next
return _.next
24. 两两交换链表中的节点
难度:中等
题目描述
给定一个链表,两两交换其中相邻的节点,并返回交换后的链表。
你不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。
示例:
给定 1->2->3->4, 返回 2->1->4->3.
class Solution:
def swapPairs(self, head: ListNode) -> ListNode:
dummyHead = ListNode(0)
dummyHead.next = head
temp = dummyHead
while temp.next and temp.next.next:
node1 = temp.next
node2 = temp.next.next
temp.next = node2
node1.next = node2.next
node2.next = node1
temp = node1
return dummyHead.next
25. K 个一组翻转链表
难度:中等
题目描述
给你一个链表,每 k 个节点一组进行翻转,请你返回翻转后的链表。
k 是一个正整数,它的值小于或等于链表的长度。
如果节点总数不是 k 的整数倍,那么请将最后剩余的节点保持原有顺序。
示例:
给你这个链表:1->2->3->4->5
当 k = 2 时,应当返回: 2->1->4->3->5
当 k = 3 时,应当返回: 3->2->1->4->5
思路一:
用栈,我们把 k 个数压入栈中,然后弹出来的顺序就是翻转的!
这里要注意几个问题:
第一,剩下的链表个数够不够 k 个(因为不够 k 个不用翻转);
第二,已经翻转的部分要与剩下链表连接起来。
class Solution:
def reverseKGroup(self, head: ListNode, k: int) -> ListNode:
dummy = ListNode(0)
p = dummy
while True:
count = k
stack = []
tmp = head
while count and tmp:
stack.append(tmp)
tmp = tmp.next
count -= 1
# 注意,目前tmp所在k+1位置
# 说明剩下的链表不够k个,跳出循环
if count :
p.next = head
break
# 翻转操作
while stack:
p.next = stack.pop()
p = p.next
#与剩下链表连接起来
p.next = tmp
head = tmp
return dummy.next
思路二:
尾插法。
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def reverseKGroup(self, head: ListNode, k: int) -> ListNode:
dummy = ListNode(0)
dummy.next = head
pre = dummy
tail = dummy
while True:
count = k
while count and tail:
count -= 1
tail = tail.next
if not tail: break
head = pre.next
while pre.next != tail:
cur = pre.next # 获取下一个元素
# pre与cur.next连接起来,此时cur(孤单)掉了出来
pre.next = cur.next
cur.next = tail.next # 和剩余的链表连接起来
tail.next = cur #插在tail后面
# 改变 pre tail 的值
pre = head
tail = head
return dummy.next
链接:https://leetcode-cn.com/problems/reverse-nodes-in-k-group/solution/kge-yi-zu-fan-zhuan-lian-biao-by-powcai/
61. 旋转链表
难度:中等
题目描述
给定一个链表,旋转链表,将链表每个节点向右移动 k 个位置,其中 k 是非负数。
示例 1:
输入: 1->2->3->4->5->NULL, k = 2
输出: 4->5->1->2->3->NULL
解释:
向右旋转 1 步: 5->1->2->3->4->NULL
向右旋转 2 步: 4->5->1->2->3->NULL
示例 2:
输入: 0->1->2->NULL, k = 4
输出: 2->0->1->NULL
解释:
向右旋转 1 步: 2->0->1->NULL
向右旋转 2 步: 1->2->0->NULL
向右旋转 3 步: 0->1->2->NULL
向右旋转 4 步: 2->0->1->NULL
解题思路
由于k可以大于整个链表的长度,因此该链表可看做一个循环链表,为了简化问题,这里先求解了链表的长度n,则在不超过链表的长度的移动距离为k%n;
分析题意,每个节点向前移动k个位置相当于将倒数第k个节点之后的节点移到头结点之前。
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def rotateRight(self, head: ListNode, k: int) -> ListNode:
if not head or not head.next or not k: return head
tmp, count= head, 0
while tmp:
tmp = tmp.next
count += 1
k = k % count
if k == 0:
return head
p = head
fast = slow = head
for i in range(k):
fast = fast.next
while fast.next:
fast = fast.next
slow = slow.next
newHead = slow.next
slow.next = None
fast.next = head
return newHead
https://leetcode-cn.com/problems/rotate-list/solution/kuai-man-zhi-zhen-xi-jie-yao-gao-hao-by-lilychao/
83. 删除排序链表中的重复元素
难度:简单
题目描述
给定一个排序链表,删除所有重复的元素,使得每个元素只出现一次。
示例 1:
输入: 1->1->2
输出: 1->2
示例 2:
输入: 1->1->2->3->3
输出: 1->2->3
class Solution:
def deleteDuplicates(self, head: ListNode) -> ListNode:
node=head
while node and node.next:
if node.val==node.next.val:
node.next=node.next.next
else:
node=node.next
return head
206. 反转链表
难度:简单
题目描述
反转一个单链表。
示例 1:
输入: 1->2->3->4->5->NULL
输出: 5->4->3->2->1->NULL
class Solution:
def reverseList(self, head: ListNode) -> ListNode:
pre = None
cur = head
while cur:
temp = cur.next # 先把原来cur.next位置存起来
cur.next = pre
pre = cur
cur = temp
return pre
92. 反转链表 II
难度:中等
题目描述
反转从位置 m 到 n 的链表。请使用一趟扫描完成反转。
示例 1:
输入: 1->2->3->4->5->NULL, m = 2, n = 4
输出: 1->4->3->2->5->NULL
说明:
- 1 ≤ m ≤ n ≤ 链表长度。
class Solution:
def reverseBetween(self, head, m, n):
"""
:type head: ListNode
:type m: int
:type n: int
:rtype: ListNode
"""
# Empty list
if not head:
return None
# Move the two pointers until they reach the proper starting point
# in the list.
cur, prev = head, None
while m > 1:
prev = cur
cur = cur.next
m, n = m - 1, n - 1
# The two pointers that will fix the final connections.
tail, con = cur, prev
# Iteratively reverse the nodes until n becomes 0.
while n:
third = cur.next
cur.next = prev
prev = cur
cur = third
n -= 1
# Adjust the final connections as explained in the algorithm
if con:
con.next = prev
else:
head = prev
tail.next = cur
return head
https://leetcode-cn.com/problems/reverse-linked-list-ii/solution/fan-zhuan-lian-biao-ii-by-leetcode/
142. 环形链表 II
难度:
题目描述
给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。
为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。
示例 1:
输入:head = [3,2,0,-4], pos = 1
输出:tail connects to node index 1
解释:链表中有一个环,其尾部连接到第二个节点。
示例 2:
输入:head = [1,2], pos = 0
输出:tail connects to node index 0
解释:链表中有一个环,其尾部连接到第一个节点
class Solution(object):
def detectCycle(self, head):
fast, slow = head, head
while True:
if not (fast and fast.next): return
fast, slow = fast.next.next, slow.next
if fast == slow: break
fast = head
while fast != slow:
fast, slow = fast.next, slow.next
return fast
148. 排序链表
难度:中等
题目描述
在 O(n log n) 时间复杂度和常数级空间复杂度下,对链表进行排序。
示例:
输入: 4->2->1->3 输出: 1->2->3->4
class Solution:
def sortList(self, head: ListNode) -> ListNode:
h, length, intv = head, 0, 1
while h: h, length = h.next, length + 1
res = ListNode(0)
res.next = head
# merge the list in different intv.
while intv < length:
pre, h = res, res.next
while h:
# get the two merge head `h1`, `h2`
h1, i = h, intv
while i and h: h, i = h.next, i - 1
if i: break # no need to merge because the `h2` is None.
h2, i = h, intv
while i and h: h, i = h.next, i - 1
c1, c2 = intv, intv - i # the `c2`: length of `h2` can be small than the `intv`.
# merge the `h1` and `h2`.
while c1 and c2:
if h1.val < h2.val: pre.next, h1, c1 = h1, h1.next, c1 - 1
else: pre.next, h2, c2 = h2, h2.next, c2 - 1
pre = pre.next
pre.next = h1 if c1 else h2
while c1 > 0 or c2 > 0: pre, c1, c2 = pre.next, c1 - 1, c2 - 1
pre.next = h
intv *= 2
return res.next
https://leetcode-cn.com/problems/sort-list/solution/sort-list-gui-bing-pai-xu-lian-biao-by-jyd/
160. 相交链表
难度:简单
题目描述
编写一个程序,找到两个单链表相交的起始节点。
示例 1:
输入:intersectVal = 8, listA = [4,1,8,4,5], listB = [5,0,1,8,4,5], skipA = 2, skipB = 3
输出:Reference of the node with value = 8
输入解释:相交节点的值为 8 (注意,如果两个链表相交则不能为 0)。从各自的表头开始算起,链表 A 为 [4,1,8,4,5],链表 B 为 [5,0,1,8,4,5]。在 A 中,相交节点前有 2 个节点;在 B 中,相交节点前有 3 个节点。
计算两个链表各自长度,长度之差为 diff
让较长链表先走 diff 步
长短链表再同步往前走,在第一个相同节点停下
如果走完了链表没有相遇,说明两个链表不相交
class Solution:
def getIntersectionNode(self, L1: ListNode, L2: ListNode) -> ListNode:
def length(L):
n = 0
while L:
n += 1; L = L.next
return n
len1, len2 = length(L1), length(L2)
if len1 > len2:
L1, L2 = L2, L1
for _ in range(abs(len1 - len2)):
L2 = L2.next
while L1 and L2 and L1 is not L2:
L1, L2 = L1.next, L2.next
return L1
https://leetcode-cn.com/problems/intersection-of-two-linked-lists/solution/chang-lian-biao-xian-zou-ji-bu-chang-duan-lian-bia/
237. 删除链表中的节点
难度:简单
题目描述
请编写一个函数,使其可以删除某个链表中给定的(非末尾)节点,你将只被给定要求被删除的节点。
现有一个链表 -- head = [4,5,1,9],它可以表示为:
示例 1:
输入: head = [4,5,1,9], node = 5
输出: [4,1,9]
解释: 给定你链表中值为 5 的第二个节点,那么在调用了你的函数之后,该链表
- 将本节点的值改为下一个节点的值
- 本节点的下一个节点改为下下一个节点
class Solution:
def deleteNode(self, node):
"""
:type node: ListNode
:rtype: void Do not return anything, modify node in-place instead.
"""
node.val = node.next.val
node.next = node.next.next