- 环形链表">环形链表
- 环形链表 II">环形链表 II
- 相交链表">相交链表
- 删除链表的倒数第N个结点">删除链表的倒数第N个结点
- 链表中倒数第k个节点">链表中倒数第k个节点
- 移除链表元素">移除链表元素
- 删除排序链表中的重复元素">删除排序链表中的重复元素
- 删除排序链表中的重复元素 II">删除排序链表中的重复元素 II
- 奇偶链表">奇偶链表
- 回文链表">回文链表
- 合并两个有序链表">合并两个有序链表
- 合并K个升序链表">合并K个升序链表
- 两数相加*">两数相加*
- 反转链表">反转链表
- 反转链表 II">反转链表 II
- 链表向右移动K个位置*">链表向右移动K个位置*
- 重排链表*">重排链表*
- 两两交换链表中的节点">两两交换链表中的节点
- 排序链表*">排序链表*
- K 个一组翻转链表*">K 个一组翻转链表*
环形链表
给定一个链表,判断链表中是否有环。
如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。
如果链表中存在环,则返回 true 。 否则,返回 false 。
输入:head = [3,2,0,-4], pos = 1
输出:true
解释:链表中有一个环,其尾部连接到第二个节点。
class Solution(object):
def hasCycle(self, head):
# 快慢指针,一个走一步,一个走两步
cur = head
pre = head
while cur and cur.next:
pre = pre.next
cur = cur.next.next
if cur is pre:
return True
return False
环形链表 II
给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。
为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。注意,pos 仅仅是用于标识环的情况,并不会作为参数传递到函数中。
输入:head = [3,2,0,-4], pos = 1
输出:返回索引为 1 的链表节点
解释:链表中有一个环,其尾部连接到第二个节点。
class Solution(object):
def detectCycle(self, head):
cur = head
pre = head
while cur and cur.next:
pre = pre.next
cur = cur.next.next
if cur == pre:
res = head # 此时res与pre距离环入口相等
while pre != res:
res = res.next
pre = pre.next
return res
return None
相交链表
编写一个程序,找到两个单链表相交的起始节点。
输入: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 个节点。
class Solution(object):
def getIntersectionNode(self, headA, headB):
cur = headA
pre = headB
while cur != pre:
if cur:
cur = cur.next
else:
cur = headB
if pre:
pre = pre.next
else:
pre = headA
return cur
删除链表的倒数第N个结点
给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。
输入:head = [1,2,3,4,5], n = 2
输出:[1,2,3,5]
输入:head = [1], n = 1
输出:[]
输入:head = [1,2], n = 1
输出:[1]
class Solution(object):
def removeNthFromEnd(self, head, n):
# 创建一个头,然后cur先跑n步,然后再一起跑
node = ListNode(0, head)
cur = node
pre = node
for _ in range(n):
if cur == None:
return []
cur = cur.next
while cur.next is not None:
cur = cur.next
pre = pre.next
pre.next = pre.next.next
return node.next
链表中倒数第k个节点
输入一个链表,输出该链表中倒数第k个节点。为了符合大多数人的习惯,本题从1开始计数,即链表的尾节点是倒数第1个节点。例如,一个链表有6个节点,从头节点开始,它们的值依次是1、2、3、4、5、6。这个链表的倒数第3个节点是值为4的节点。
给定一个链表: 1->2->3->4->5, 和 k = 2.
返回链表 4->5.
class Solution:
def getKthFromEnd(self, head):
# cur先跑n步,然后再一起跑
cur = head
pre = head
for _ in range(k):
if cur == None:
return cur
cur = cur.next
while cur != None:
cur = cur.next
pre = pre.next
return pre
移除链表元素
给你一个链表的头节点head和一个整数val,请你删除链表中所有满足Node.val == val的节点,并返回新的头节点。
输入:head = [1,2,6,3,4,5,6], val = 6
输出:[1,2,3,4,5]
输入:head = [], val = 1
输出:[]
class Solution(object):
def removeElements(self, head, val):
node = ListNode(0, head)
cur = head
pre = node
while cur:
if cur.val == val:
pre.next = cur.next
else:
pre = pre.next
cur = cur.next
return node.next
删除排序链表中的重复元素
存在一个按升序排列的链表,给你这个链表的头节点head,请你删除所有重复的元素,使每个元素只出现一次。
返回同样按升序排列的结果链表。
输入:head = [1,1,2]
输出:[1,2]
输入:head = [1,1,2,3,3]
输出:[1,2,3]
class Solution(object):
def deleteDuplicates(self, head):
# 方法一:用假头,II题是这种思路
if not head:
return head
node = ListNode(0)
node.next = head
cur = node
while cur.next and cur.next.next:
if cur.next.val == cur.next.next.val:
cur.next = cur.next.next
else:
cur = cur.next
return node.next
# 方法二:不用假头
if not head:
return head
cur = head
while cur.next:
if cur.val == cur.next.val:
cur.next = cur.next.next
else:
cur = cur.next
return head
删除排序链表中的重复元素 II
存在一个按升序排列的链表,给你这个链表的头节点 head ,请你删除链表中所有存在数字重复情况的节点,只保留原始链表中 没有重复出现 的数字。
返回同样按升序排列的结果链表。
输入:head = [1,2,3,3,4,4,5]
输出:[1,2,5]
输入:head = [1,1,1,2,3]
输出:[2,3]
class Solution(object):
def deleteDuplicates(self, head):
# 方法一:和I类似,就多了两行代码,需要多次去判断是否相等
if not head:
return head
node = ListNode(0)
node.next = head
cur = node
while cur.next and cur.next.next:
if cur.next.val == cur.next.next.val:
tmp = cur.next.val
while cur.next and cur.next.val == tmp:
cur.next = cur.next.next
else:
cur = cur.next
return node.next
奇偶链表
给定一个单链表,把所有的奇数节点和偶数节点分别排在一起。请注意,这里的奇数节点和偶数节点指的是节点编号的奇偶性,而不是节点的值的奇偶性。
请尝试使用原地算法完成。你的算法的空间复杂度应为 O(1),时间复杂度应为 O(nodes),nodes 为节点总数。
输入: 1->2->3->4->5->NULL
输出: 1->3->5->2->4->NULL
输入: 2->1->3->5->6->4->7->NULL
输出: 2->3->6->7->1->5->4->NULL
class Solution(object):
def oddEvenList(self, head):
if not head:
return head
# 奇数链
cur = head
# 偶数链
pre = cur.next
# 保存偶数链头
tmp = pre
while cur.next and cur.next.next:
# 分家,拆成奇数链和偶数链
cur.next = cur.next.next
pre.next = pre.next.next
cur = cur.next
pre = pre.next
# 合并,奇数链指向偶数链
cur.next = tmp
return head
回文链表
请判断一个链表是否为回文链表。
输入: 1->2
输出: false
输入: 1->2->2->1
输出: true
class Solution(object):
def isPalindrome(self, head):
# 这个方法性能太差了
lists = []
cur = head
while cur:
lists.append(cur.val)
cur = cur.next
if lists == lists[::-1]:
return True
else:
return False
合并两个有序链表
将两个升序链表合并为一个新的升序链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
输入:l1 = [1,2,4], l2 = [1,3,4]
输出:[1,1,2,3,4,4]
输入:l1 = [], l2 = []
输出:[]
class Solution(object):
def mergeTwoLists(self, l1, l2):
# 方法一:递归,下面那个合并k个借鉴这个
if l1 is None:
return l2
if l2 is None:
return l1
node1 = l1
node2 = l2
if node1.val < node2.val:
node1.next = self.mergeTwoLists(node1.next, node2)
return node1
else:
node2.next = self.mergeTwoLists(node1, node2.next)
return node2
# 方法二:双指针
cur = l1
pre = l2
node = ListNode(0)
mid = node
while cur and pre:
if cur.val <= pre.val:
mid.next = cur
cur = cur.next
else:
mid.next = pre
pre = pre.next
mid = mid.next
if pre == None:
mid.next = cur
else:
mid.next = pre
return node.next
合并K个升序链表
给你一个链表数组,每个链表都已经按升序排列。
请你将所有链表合并到一个升序链表中,返回合并后的链表。
输入:lists = [[1,4,5],[1,3,4],[2,6]]
输出:[1,1,2,3,4,4,5,6]
解释:链表数组如下:
[
1->4->5,
1->3->4,
2->6
]
将它们合并到一个有序链表中得到。
1->1->2->3->4->4->5->6
class Solution(object):
def mergeKLists(self, lists):
# 方法一:sort排序,暴力法
nodes = []
head = point = ListNode(0)
# 把所有元素都遍历出来放到列表中排序
for cur in lists:
while cur:
nodes.append(cur.val)
cur = cur.next
nodes.sort()
for x in nodes:
point.next = ListNode(x)
point = point.next
return head.next
# 方法一:不用sort,用堆排序
nodes = []
head = point = ListNode(0)
for cur in lists:
while cur:
heapq.heappush(nodes, cur.val)
cur = cur.next
while nodes:
point.next = ListNode(heappop(nodes))
point = point.next
return head.next
# 方法二:优先队列堆排序,每次只移动弹出元素所在列表,所以优先队列中不仅储存链表的元素,还要存储链表所在位置,以便弹出时知道该元素属于是哪一个链表。
head = ListNode(0)
pre = head
nodes = []
for key in range(len(lists)):
if lists[key]:
heapq.heappush(nodes, (lists[key].val, key))
lists[key] = lists[key].next
while nodes:
value, key = heapq.heappop(nodes)
pre.next = ListNode(value)
pre = pre.next
if lists[key]:
heapq.heappush(nodes, (lists[key].val, key))
lists[key] = lists[key].next
return head.next
# 方法三:分治法,最优解,每次将链表列表分为两个部分,并分别自底向上合并两部分的链表
if len(lists) == 0:
return None
if len(lists) == 1:
return lists[0]
mid = len(lists) // 2
# 递归
def mergeTwoLists(node1, node2):
if not node1:
return node2
if not node2:
return node1
if node1.val < node2.val:
node1.next = mergeTwoLists(node1.next, node2)
return node1
else:
node2.next = mergeTwoLists(node1, node2.next)
return node2
return mergeTwoLists(self.mergeKLists(lists[:mid]), self.mergeKLists(lists[mid:]))
两数相加*
给你两个 非空 的链表,表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的,并且每个节点只能存储 一位 数字。
请你将两个数相加,并以相同形式返回一个表示和的链表。
你可以假设除了数字 0 之外,这两个数都不会以 0 开头。
输入:l1 = [2,4,3], l2 = [5,6,4]输出:[7,0,8]
解释:342 + 465 = 807
输入:l1 = [0], l2 = [0]
输出:[0]
class Solution(object):
def addTwoNumbers(self, l1, l2):
count = 0
node = ListNode()
cur = node
while l1 or l2 or count:
num = 0
if l1:
num += l1.val
l1 = l1.next
if l2:
num += l2.val
l2 = l2.next
if count:
num += count
count -= 1
count, num = divmod(num, 10)
cur.next = ListNode(num)
cur = cur.next
return node.next
反转链表
反转一个单链表。
示例:
输入: 1->2->3->4->5->NULL输出: 5->4->3->2->1->NULL
class Solution(object):
def reverseList(self, head):
cur = head
pre = None
while cur:
# 记录当前节点的下一个节点
tmp = cur.next
# 然后将当前节点指向pre
cur.next = pre
# pre和cur节点都前进一位
pre = cur
cur = tmp
return pre
# Go语言版
func reverseList(head *ListNode) *ListNode {
var pre *ListNode
cur := head
for cur != nil {
tmp := cur.Next
cur.Next = pre
pre = cur
cur = tmp
}
return pre
}
反转链表 II
给你单链表的头指针 head 和两个整数 left 和 right ,其中 left <= right 。请你反转从位置 left 到位置 right 的链表节点,返回 反转后的链表 。
输入:head = [1,2,3,4,5], left = 2, right = 4
输出:[1,4,3,2,5]
输入:head = [5], left = 1, right = 1
输出:[5]
class Solution(object):
def reverseBetween(self, head, left, right):
# 方法一:比较好理解
dummy = ListNode()
dummy.next = head
pre = dummy
# 找到翻转链表部分的前一个节点, 1->2->3->4->5->NULL, m = 2, n = 4 指的是 节点值为1
for _ in range(left-1):
pre = pre.next
# 用双指针,进行链表翻转
node = None
cur = pre.next
# cur多走一步到5
for _ in range(right-left+1):
tmp = cur.next
cur.next = node
node = cur
cur = tmp
# 将翻转部分 和 原链表拼接
pre.next.next = cur # 也就是1.next.next = 5
pre.next = node # 1.next = 新反转的链表
return dummy.next
# 方法二:将节点3插入节点1和节点2之间,变成: 1->3->2->4->5->NULL,再将节点4插入节点1和节点3之间,变成:1->4->3->2->5->NULL
node = ListNode()
node.next = head
pre = node
for _ in range(left - 1):
pre = pre.next
# 用 pre, cur, tmp三指针实现插入操作
# tail 是插入pre,与pre.next的节点
cur = pre.next
tmp = cur.next
for _ in range(right - left):
cur.next = tmp.next
tmp.next = pre.next
pre.next = tmp
tmp = cur.next
return node.next
链表向右移动K个位置*
给你一个链表的头节点head,旋转链表,将链表每个节点向右移动 k个位置。
输入:head = [1,2,3,4,5], k = 2
输出:[4,5,1,2,3]
输入:head = [0,1,2], k = 4
输出:[2,0,1]
class Solution(object):
def rotateRight(self, head, k):
# 找到原链表的最后一个节点,将其与原链表的头结点相连(成环),并统计链表长度,更新有效 k 值
# 从原链表的头节点出发,找到需要断开的点,进行断开
if not head or k == 0:
return head
tmp = 0
cur = head
while cur.next:
cur = cur.next
tmp += 1
tmp += 1
k %= tmp
cur.next = head
k = tmp - k - 1
while k > 0:
head = head.next
k -= 1
ans = head.next
head.next = None
return ans
重排链表*
给定一个单链表 L:L0→L1→…→Ln-1→Ln ,将其重新排列后变为: L0→Ln→L1→Ln-1→L2→Ln-2→…
你不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。
给定链表 1->2->3->4, 重新排列为 1->4->2->3.
给定链表 1->2->3->4->5, 重新排列为 1->5->2->4->3.
class Solution(object):
def reorderList(self, head):
if not head:
return head
# 1 2 3 4 5
fast = head
pre_mid = head
# 找到中点, 偶数个找到时上界那个
while fast.next and fast.next.next:
pre_mid = pre_mid.next
fast = fast.next.next
# 翻转中点之后的链表,采用是pre, cur双指针方法
pre = None
cur = pre_mid.next
# 1 2 3 5 4
while cur:
tmp = cur.next
cur.next = pre
pre = cur
cur = tmp
# 翻转链表和前面链表拼接
pre_mid.next = pre
# 1 5 2 4 3
# 链表头
p1 = head
# 翻转头
p2 = pre_mid.next
while p1 != pre_mid:
# 建议这部分画图, 很容易理解
pre_mid.next = p2.next
p2.next = p1.next
p1.next = p2
p1 = p2.next
p2 = pre_mid.next
return head
两两交换链表中的节点
给定一个链表,两两交换其中相邻的节点,并返回交换后的链表。
你不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。
输入:head = [1,2,3,4]
输出:[2,1,4,3]
输入:head = []
输出:[]
class Solution(object):
def swapPairs(self, head):
# 方法一:双指针
node = ListNode(0)
node.next = head
tmp = node
while tmp.next and tmp.next.next:
cur = tmp.next.next
pre = tmp.next
tmp.next = cur
pre.next = cur.next
cur.next = pre
tmp = pre
return node.next
# 方法二:递归
if head == None or head.next == None:
return head
tmp = head.next
head.next = self.swapPairs(head.next.next)
tmp.next = head
return tmp
排序链表*
给你链表的头结点 head ,请将其按升序排列并返回排序后的链表。
输入:head = [4,2,1,3]
输出:[1,2,3,4]
输入:head = [-1,5,3,4,0]
输出:[-1,0,3,4,5]
class Solution(object):
def sortList(self, head):
# 方法一:自顶向下归并排序
if not head or not head.next:
return head
pre = head
cur = head
# 用快慢指针分成两部分
while cur.next and cur.next.next:
pre = pre.next
cur = cur.next.next
# 找到左右部分, 把右边置空
mid = pre.next
pre.next = None
# 递归下去
# 此时head已经变为原来的一般,左半边
left = self.sortList(head)
# mid为右半边
right = self.sortList(mid)
# 合并
return self.merge(left, right)
def merge(self, left, right):
node = ListNode(0)
cur = node
while left and right:
if left.val < right.val:
cur.next = left
left = left.next
cur = cur.next
else:
cur.next = right
right = right.next
cur = cur.next
if left:
cur.next = left
if right:
cur.next = right
return node.next
# 方法二:自下向上归并排序,最优解,但是过程复杂
intv = 1
length = 0
high = head
# 计算链表长度
while high:
high = high.next
length += 1
node = ListNode(0)
node.next = head
# merge the list in different intv.
while intv < length:
mid, high = node, node.next
while high:
# get the two merge head `h1`, `h2`
h1, i = high, intv
while i and high:
high, i = high.next, i - 1
if i:
break # no need to merge because the `h2` is None.
h2, i = high, intv
while i and high:
high, i = high.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:
mid.next, h1, c1 = h1, h1.next, c1 - 1
else:
mid.next, h2, c2 = h2, h2.next, c2 - 1
mid = mid.next
mid.next = h1 if c1 else h2
while c1 > 0 or c2 > 0:
mid, c1, c2 = mid.next, c1 - 1, c2 - 1
mid.next = high
intv *= 2
return node.next
# 方法三:都取出来排序再重新生成链表
if head is None or head.next is None:
return head
nums = []
cur = head
while cur:
nums.append(p.val)
cur = cur.next
nums.sort()
cur = head
while cur:
cur.val = nums.pop(0)
cur = cur.next
return head
K 个一组翻转链表*
给你一个链表,每 k 个节点一组进行翻转,请你返回翻转后的链表。
k 是一个正整数,它的值小于或等于链表的长度。
如果节点总数不是 k 的整数倍,那么请将最后剩余的节点保持原有顺序。
输入:head = [1,2,3,4,5], k = 2
输出:[2,1,4,3,5]
输入:head = [1,2,3,4,5], k = 3
输出:[3,2,1,4,5]
输入:head = [1,2,3,4,5], k = 1
输出:[1,2,3,4,5]
class Solution(object):
def reverseKGroup(self, head, k):
# 我们把 k 个数压入栈中,然后弹出来的顺序就是翻转的!
# 这里要注意几个问题:
# 第一,剩下的链表个数够不够 k 个(因为不够 k 个不用翻转)
# 第二,已经翻转的部分要与剩下链表连接起来。
# 方法一:栈
dummy = ListNode(0)
cur = 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 :
cur.next = head
break
# 翻转操作
while stack:
cur.next = stack.pop()
cur = cur.next
#与剩下链表连接起来
cur.next = tmp
head = tmp
return dummy.next
# 方法二:最优解,尾插法
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