1. 剑指 Offer 22. 链表中倒数第k个节点
输入一个链表,输出该链表中倒数第k个节点。为了符合大多数人的习惯,本题从1开始计数,即链表的尾节点是倒数第1个节点。例如,一个链表有6个节点,从头节点开始,它们的值依次是1、2、3、4、5、6。这个链表的倒数第3个节点是值为4的节点。
示例:
给定一个链表: 1->2->3->4->5, 和 k = 2.
返回链表 4->5.
解法1: 暴力
class ListNode:
def __init__(self, x):
self.val = x
self.next = None
class Solution:
def getKthFromEnd(self, head: ListNode, k: int) -> ListNode:
p = head # 第k个节点
node_list = []
while p: # 暴力遍历
node_list.append(p)
p = p.next
return node_list[-k]
解法2: 快慢指针
class Solution:
def getKthFromEnd(self, head: ListNode, k: int) -> ListNode:
# 快慢指针, fast 比快指针先多走k, 然后同步前进,
# fast到达 最后一个节点, slow到达目标节点的前一个节点。
slow = fast = head
count = 0
while fast and fast.next:
fast = fast.next
count += 1
if count > k:
slow = slow.next
if count == k - 1: # 特殊情况 k为头节点
return head
request = slow.next
return request
解法3: 快慢指针 注意和解法2的区别
class ListNode:
def __init__(self, x):
self.val = x
self.next = None
class Solution:
def getKthFromEnd(self, head: ListNode, k: int) -> ListNode:
# 快慢指针, fast 比快指针先多走k, 然后同步前进,
# fast到达 最后一个节点, slow到达目标节点的前一个节点。
slow = fast = head
count = 0
while fast and fast.next:
fast = fast.next
count += 1
if count >= k:
slow = slow.next
request = slow
return request
2. 141. 环形链表
给定一个链表,判断链表中是否有环。
如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。
如果链表中存在环,则返回 true 。 否则,返回 false 。
解法1: 快慢指针
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def hasCycle(self, head: ListNode) -> bool:
fast = head
slow = head
while fast and fast.next:
slow = slow.next
fast = fast.next.next
if slow == fast:
return True
return False
3. 142. 环形链表 II
给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。
为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。注意,pos 仅仅是用于标识环的情况,并不会作为参数传递到函数中。
说明:不允许修改给定的链表。
进阶:你是否可以不用额外空间解决此题?
解法1: 快慢指针,当快慢指针相遇后, 慢指针 和 从head出发指针, 必定会相遇。
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:
while head != slow:
head = head.next
slow = slow.next
return head
return None
4. 19. 删除链表的倒数第N个节点
给定一个链表,删除链表的倒数第 n 个节点,并且返回链表的头结点。
示例:
给定一个链表: 1->2->3->4->5, 和 n = 2.
当删除了倒数第二个节点后,链表变为 1->2->3->5.
说明:
给定的 n 保证是有效的。
进阶:
你能尝试使用一趟扫描实现吗?
解法1: 快慢指针
class Solution:
def removeNthFromEnd(self, head: ListNode, n: int) -> ListNode:
# 一次遍历, 快慢指针遍历, fast走到最后一个节点, slow走到目标节点的前置节点, 好思路
# fast 和 slow 差 n个节点。
fast = slow = head
count = 0
while fast and fast.next: # fast 非空
fast = fast.next # fast 首先比 slow 多走n
count += 1
if count > n:
slow = slow.next
# 几种特殊情况
if count == n - 1: # 删除头节点
head = head.next
else:
# fast 到结尾了
if slow.next:
slow.next = slow.next.next
else:
head = None
return head
5. 24. 两两交换链表中的节点
给定一个链表,两两交换其中相邻的节点,并返回交换后的链表。
你不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。
示例 1:
输入:head = [1,2,3,4]
输出:[2,1,4,3]
示例 2:
输入:head = []
输出:[]
示例 3:
输入:head = [1]
输出:[1]
解法1: 递归
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def swapPairs(self, head: ListNode) -> ListNode:
# 递归, 先背一种解法
if not head or not head.next: # None 或者 一个节点
return head
newhead = head.next # 新的头节点
head.next = self.swapPairs(newhead.next) # 递归 交换 剩余 节点, 一次交换两个
newhead.next = head
return newhead
6. 148. 排序链表
给你链表的头结点 head ,请将其按 升序 排列并返回 排序后的链表 。
进阶:
你可以在 O(n log n) 时间复杂度和常数级空间复杂度下,对链表进行排序吗?
示例 1:
输入:head = [4,2,1,3]
输出:[1,2,3,4]
示例 2:
输入:head = [-1,5,3,4,0]
输出:[-1,0,3,4,5]
示例 3:
输入:head = []
输出:[]
解法1: O(n)遍历出所有节点值, O(nlogn)排序, O(n)将排序后数据写进节点 (不管黑猫白猫,能逮着耗子就是好猫)
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def sortList(self, head: ListNode) -> ListNode:
# # 超时 O(n*n)
# flag = True
# while flag:
# flag =False
# p = head
# while p and p.next:
# if p.val > p.next.val:
# p.val, p.next.val = p.next.val, p.val
# flag = True
# p = p.next
# return head
temp = []
p = head
while p:
temp.append(p.val)
p = p.next
temp = sorted(temp) # O(nlogn) 快速排序
p = head
i = 0
while p:
p.val = temp[i]
i = i + 1
p = p.next
return head
解法2: 归并排序(快慢指针 找中点) + 合并链表(合并两个有序链表方法) 这题得背下来
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def sortList(self, head: ListNode) -> ListNode:
# 归并排序(快慢指针 找中点) + 合并链表(合并两个有序链表方法) # 考点太多了
# 归并排序
def sortFunc(head, tail): # 需要知道 头 尾
if not head: # 为null
return head
if head.next == tail: # 两个节点时, 返回头节点就行,因为mid
head.next = None
return head
# 快慢指针
fast=slow=head
while fast != tail: # 这个快慢指针 用法 也特殊, 总结快慢指针的用法形式;
slow = slow.next
fast = fast.next
if fast != tail:
fast = fast.next
# slow 就是中点
mid = slow
return merge(sortFunc(head, mid), sortFunc(mid, tail))
# 合并两个有序链表
def merge(head1, head2):
p = q = ListNode(0, None)
l1 = head1
l2 = head2
while l1 and l2:
if l1.val < l2.val:
p.next = l1
l1 = l1.next
else:
p.next = l2
l2 = l2.next
p = p.next
if l1:
p.next = l1
if l2:
p.next = l2
return q.next
return sortFunc(head, None)
7. 147. 对链表进行插入排序
对链表进行插入排序。
插入排序的动画演示如上。从第一个元素开始,该链表可以被认为已经部分排序(用黑色表示)。
每次迭代时,从输入数据中移除一个元素(用红色表示),并原地将其插入到已排好序的链表中。
插入排序算法:
插入排序是迭代的,每次只移动一个元素,直到所有元素可以形成一个有序的输出列表。
每次迭代中,插入排序只从输入数据中移除一个待排序的元素,找到它在序列中适当的位置,并将其插入。
重复直到所有输入数据插入完为止。
示例 1:
输入: 4->2->1->3
输出: 1->2->3->4
解法1: 插入排序定义
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def insertionSortList(self, head: ListNode) -> ListNode:
# 插入排序
if not head:
return head
dummyHead = ListNode(0) # 头节点 前一个节点, 用来方便在 头节点前 插入 节点
dummyHead.next = head
lastedsorted = head # 有序区域的最后一个节点
cur = head.next # 下一个待排序的无序节点
while cur:
if lastedsorted.val <= cur.val: # 有序 无需插入
lastedsorted = lastedsorted.next
else:
# 需要插入,从头遍历
pre = dummyHead
while pre.next.val <= cur.val:
pre = pre.next
lastedsorted.next = cur.next # 将但前无序节点之后的节点 给 最后一个有序的next
cur.next = pre.next # cur插入到 pre 和 pre.next之间
pre.next = cur
cur = lastedsorted.next # 待排序节点
return dummyHead.next