秋招 hot100 刷题记录【3】

好难啊。。。有的题做了好几遍了还是一点思路没有。。。

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
        
  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值