好难啊。。。有的题做了好几遍了还是一点思路没有。。。
1.合并两个有序链表
- 思路:联想双指针问题 注意链表往往需要一个虚拟头节点
- 代码链接
# 1.双指针方法 时间o(m+n) 空间 o(1)——双指针只需要常数级的变量存储数据
prehead = ListNode(0) # 构建虚拟的结点
prev = prehead
# 虚拟头节点
while list1 and list2:
if list1.val <= list2.val:
prev.next = list1
list1 = list1.next
else:
prev.next = list2
list2 = list2.next
prev = prev.next
prev.next = list1 if list1 is not None else list2
return prehead.next
2.两数相加
# Definition for singly-linked list.
# class ListNode(object):
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution(object):
def addTwoNumbers(self, l1, l2):
"""
:type l1: ListNode
:type l2: ListNode
:rtype: ListNode
"""
# 链表是倒序存储的
# 相加的时候我们也是从末尾开始加的 注意十位数的存储
num_0 = 0
num_1 = 0
cur = head = ListNode(0)
while l1 or l2:
if not l1:
l1 = ListNode(0) # 对于较少位数的列表 相当于高位数 用0补齐
if not l2:
l2 = ListNode(0)
num_0 = (l1.val + l2.val + num_1) % 10
num_1 = (l1.val + l2.val + num_1) / 10
cur.next = ListNode(val = num_0) # 新建节点
cur = cur.next # 移动节点
l1 = l1.next
l2 = l2.next
# 如果最高位大于0 相当于比原本l1或l2更长 需要新增节点
if num_1 > 0:
cur.next = ListNode(val = num_1)
return head.next
3.删除链表的第N个节点
- 代码链接
- 思路:快慢指针!
# Definition for singly-linked list.
# class ListNode(object):
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution(object):
def removeNthFromEnd(self, head, n):
"""
:type head: ListNode
:type n: int
:rtype: ListNode
"""
# 双指针使用快慢指针 快指针应该比慢指针多走n-1步(指向的是要删除的节点的上一个结点)
dum = ListNode(next = head)
slow = fast = dum
# 1. 先让快指针走n步(从头节点出发是n-1步,从虚拟头节点出发是n步
for i in range(n + 1):
fast = fast.next
# 2. 挪动快慢指针,直到快指针走到链表结尾
while fast:
fast = fast.next
slow = slow.next
# 3. 此时slow.next即要删除的结点
slow.next = slow.next.next
return dum.next
4.两两交换链表中的节点
# Definition for singly-linked list.
# class ListNode(object):
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution(object):
def swapPairs(self, head):
"""
:type head: ListNode
:rtype: ListNode
"""
# 思路:模拟交换的过程
# 注意容易混淆的节点可以用变量存储
dum = ListNode(next = head)
cur = dum
while cur.next != None and cur.next.next != None:
# 下一个开始要交换的节点
next_head = cur.next.next.next
# 保留要交换的节点
change_node = cur.next
# 进行交换
cur.next = change_node.next
change_node.next.next = change_node
change_node.next = next_head
# 挪到下一个位置
cur = change_node
return dum.next
5.k个一组翻转链表
- 代码链接
- 思路 双指针+用栈来存储需要翻转的节点
# Definition for singly-linked list.
# class ListNode(object):
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution(object):
def reverseKGroup(self, head, k):
"""
:type head: ListNode
:type k: int
:rtype: ListNode
"""
# 1.用栈依次存储k个元素
dum = ListNode(next = head)
cur = dum
while True:
count = k # 计入压入栈的数目
stack = [] # 初始化栈
tmp = head # 用于记录每次要压入栈的元素
while count and tmp:
stack.append(tmp)
count -= 1
tmp = tmp.next
# 弹出后此时两种情况,tmp已经是下一个要压入栈的头节点,即剩下的节点
# 1.链表长度 > k
# 2.链表长度 < k
# 如果不够k个那么链表不需要翻转
if count:
cur.next = head
break
# 如果足够k个,那么用cur的节点进行链接出栈的节点
while stack:
cur.next = stack.pop()
cur = cur.next
# 链接剩余的链表
cur.next = tmp
head = tmp
return dum.next
6.随机链表的复制-深拷贝
- 思路:在原始链表后复制一个节点,然后再依次复制,最后再分离两个链表,注意的是还原原始链表
- 代码链接
"""
# Definition for a Node.
class Node:
def __init__(self, x, next=None, random=None):
self.val = int(x)
self.next = next
self.random = random
"""
class Solution(object):
def copyRandomList(self, head):
"""
:type head: Node
:rtype: Node
"""
# 思路:在原始链表后面新建一个节点然后复制指针后再分开两个链表
if not head:
return None
cur = head
while cur:
new_node = Node(cur.val,None, None)
# new_node.random = cur.random # 随机指针不能同时复制
new_node.next = cur.next
cur.next = new_node
cur = new_node.next
cur = head
while cur:
if cur.random:
cur.next.random = cur.random.next # 此时random指针需要指向的是新节点 而不是原始节点
cur = cur.next.next
dum = Node(0, None, None)
new_cur = dum
cur = head
while cur:
new_cur.next = cur.next
new_cur = new_cur.next
cur.next = cur.next.next # 复原原始列表
cur = cur.next
return dum.next
7.排序列表
- 思路:归并排序 从底向上 时间复杂度o(n) 空间复杂度o(1)
- 代码链接
# Definition for singly-linked list.
# class ListNode(object):
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution(object):
def sortList(self, head):
"""
:type head: ListNode
:rtype: ListNode
"""
# 链表排序 时间复杂度 o(nlogn) 空间复杂度 o(1)——归并排序+从下向上
# 排序完成的条件 cut的长度intv大于链表长度length 即排序完成
length, intv = 0, 1
# 1.统计长度
cur = head
while cur:
length += 1
cur = cur.next
dum = ListNode(0)
dum.next = head
# 开始merge
while intv < length:
pre, h = dum, dum.next # pre用于链接每次合并的结果 h用于控制每次合并
while h:
h1, i = h, intv
while i and h:
h, i = h.next, i - 1 # 找到长度为intv的下一个要合并的链表为h
if i:
break # 说明后面的h2长度不够intv 那就不用merge
h2, i = h, intv
while h and i:
h, i = h.next, i - 1 # 保留下一个要合并的h1
c1, c2 = intv, intv - i # 要合并的子串长度 c2的长度可能小于intv
while c1 and c2:
if h1.val < h2.val:
pre.next = h1
h1 = h1.next
c1 -= 1
else:
pre.next = h2
h2 = h2.next
c2 -= 1
pre = pre.next
pre.next = h1 if c1 else h2 # 链接剩下的链表
# 挪动pre指针到合并后的子串后
while c1 > 0 or c2 > 0:
pre = pre.next
c1 -= 1
c2 -= 1
pre.next = h #下一个要合并的子串
intv *= 2
return dum.next
8.合并k个升序列表
- 思路:用最小堆来维护k个列表中的最小值 注意python使用的方法中 如果最小堆的节点是链表型 那么就要重写 ListNode.__it__方法来当节点可比较大小
- 代码链接
# Definition for singly-linked list.
# class ListNode(object):
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
# 使用Python的heapq模块时,堆默认需要能够比较元素的大小。
# 如果你想将自定义对象(如链表节点)放入堆中,则需要定义这些对象的比较规则。通过重载__lt__方法,可以实现这个功能。
ListNode.__lt__ = lambda a, b: a.val < b.val # 让堆可以比较节点大小
class Solution(object):
def mergeKLists(self, lists):
"""
:type lists: List[ListNode]
:rtype: ListNode
"""
# 时间复杂度 o(nlogk) n为所有链表长度
# 空间复杂度 o(k)
# 思路:最小堆 合并k个链表 那就每次都把链表头节点放入堆中
# 注意这里的k个链表都有序
dum =ListNode()
cur = dum
# 初始化把所有链表的头节点均放入堆中
h = [head for head in lists if head]
heapify(h) # 最小堆化该列表
while h:
node = heappop(h) # pop出堆的最小节点
if node.next: #如果该最小节点下一个节点非空 则该next有可能是最小的
heappush(h, node.next) # 下一个节点可能是最小的 放入堆中
cur.next = node # 合并到新链表中
cur = cur.next # 挪到下一个节点
return dum.next
9.LRU缓存(好难——要多刷几次)
- 代码链接
- LRU(最近最少使用)缓存,选择最近最久未使用的页面进行淘汰。算法赋予每一个页面的一个访问字段,用于记录一个页面自从上次被访问以来所经历的时间t,当需要淘汰页面的时候就选择现有页面中t值最大的,即最近最少使用的页面
- 先进先出置换算法(FIFO)
最简单的页面置换算法是先入先出(FIFO)法。这种算法的实质是,总是选择在主存中停留时间最长(即最老)的一页置换,即先进入内存的页,先退出内存。 - 最少使用(LFU)置换算法
在采用最少使用置换算法时,应为在内存中的每个页面设置一个移位寄存器,用来记录该页面被访问的频率。该置换算法选择在之前时期使用最少的页面作为淘汰页。
class Node:
# 提高访问属性的速度,并节省内存
# Python 默认是用 dict 存储属性的,每次用 . 访问属性都需要查字典。如果声明 __slots__ 就不会创建字典,而是改用指针偏移量直接拿到属性对象。所以既节省了内存(没有字典)又节省了时间(省去查字典的过程)。
__slots__ = 'prev', 'next', 'key', 'value'
def __init__(self, key = 0, value = 0):
self.key = key
self.value = value
class LRUCache(object):
def __init__(self, capacity):
"""
:type capacity: int
"""
self.capacity = capacity
self.dummy = Node()
self.dummy.prev = self.dummy
self.dummy.next = self.dummy
self.key_to_node = dict()
def get_node(self, key):
if key not in self.key_to_node: #没有这本书
return None
node = self.key_to_node[key]
self.remove(node)
self.push_front(node)
return node
def get(self, key):
"""
:type key: int
:rtype: int
"""
node = self.get_node(key)
return node.value if node else -1
def put(self, key, value):
"""
:type key: int
:type value: int
:rtype: None
"""
node = self.get_node(key)
if node:
node.value = value
return
self.key_to_node[key] = node = Node(key, value) # 新建一个节点
self.push_front(node)
if len(self.key_to_node) > self.capacity: # 太多书了
back_node = self.dummy.prev
del self.key_to_node[back_node.key]
self.remove(back_node)
def remove(self, x):
x.prev.next = x.next
x.next.prev = x.prev
def push_front(self, x):
x.prev = self.dummy
x.next = self.dummy.next
x.prev.next = x
x.next.prev = x
# 时间复杂度:所有操作均为 O(1)。
# 空间复杂度:O(min(p,capacity)),其中 p 为 put 的调用次数。
# Your LRUCache object will be instantiated and called as such:
# obj = LRUCache(capacity)
# param_1 = obj.get(key)
# obj.put(key,value)
5.二叉树中序遍历(递归 / 栈)
# Definition for a binary tree node.
# class TreeNode(object):
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution(object):
def inorderTraversal(self, root):
"""
:type root: TreeNode
:rtype: List[int]
"""
# 中序遍历 左子树-根节点-右子树
if not root:
return []
# 1. 递归
# left = self.inorderTraversal(root.left)
# right = self.inorderTraversal(root.right)
# return left + [root.val] + right
# 2.栈
stack = []
res = []
node = root
while node or stack:
if node:
stack.append(node)
node = node.left
else:
# 如果当前节点没有左子树了
node = stack.pop()
res.append(node.val)
node = node.right
return res