【Leetcode】 top100 round2 直接开背版

25 K个一组翻转数组

给你链表的头节点 head ,每 k 个节点一组进行翻转,请你返回修改后的链表。

k 是一个正整数,它的值小于或等于链表的长度。如果节点总数不是 k 的整数倍,那么请将最后剩余的节点保持原有顺序。

思路:先按k个分段,标记段头段尾,写一个翻转k个节点的链表的函数,传回新的段头段尾,完成到下一段的切换;

class Solution(object):
    def reverseKGroup(self, head, k):
        """
        :type head: ListNode
        :type k: int
        :rtype: ListNode
        """
        if head.next==None or k==1: return head

        p = ListNode(0)  #创建一个虚拟的头结点
        # p.next = head

        old_head, left, right = p, head, head
        while right:
            for _ in range(k-1):
                right = right.next
                if not right: return p.next     

            old_tail = right.next
            new_head, new_tail = self.reverse(left, right)
            old_head.next = new_head
            new_tail.next = old_tail
            old_head, left, right = new_tail, old_tail, old_tail
            
        return p.next
    
    def reverse(self, head, tail):
        pre, cur = head, head.next
        while pre != tail:
            cur.next, cur, pre = pre, cur.next, cur  
         #必须同步更新,不然更改指向后无法切换到下一结点(或者用一个新结点记录)
        return pre, head   # 新头和新尾
138 随机链表的复制

给你一个长度为 n 的链表,每个节点包含一个额外增加的随机指针 random ,该指针可以指向链表中的任何节点或空节点。

构造这个链表的 深拷贝。 深拷贝应该正好由 n 个 全新 节点组成,其中每个新节点的值都设为其对应的原节点的值。新节点的 next 指针和 random 指针也都应指向复制链表中的新节点,并使原链表和复制链表中的这些指针能够表示相同的链表状态。复制链表中的指针都不应指向原链表中的节点 。返回复制链表的头节点。你的代码  接受原链表的头节点 head 作为传入参数。

思路一:一次遍历节点,在当前节点后创建一个相同的节点,完成next指针的复制;

          再次遍历节点,完成random指针的复制,复制节点(当前节点下一结点)的随机指针指向当前节点随机指针的下一结点(复制的随机节点),p.next.random = p.random.next

          最后遍历分离两链表;

class Solution(object):
    def copyRandomList(self, head):
        """
        :type head: Node
        :rtype: Node
        """
        if not head:return None
        
        p = head
        while p:                 # 插入复制的新结点
            p.next = Node(p.val, p.next, None)
            p = p.next.next

        p = head
        while p:                 # 设置random指针
            if p.random: p.next.random = p.random.next
            p = p.next.next
        
        p = head
        new_head = Node(0)
        q = new_head
        while p:                 # 分离两链表
            q.next = p.next
            q = q.next
            p.next = q.next      # 不直接用p.next.next是因为要恢复原链表
            p = p.next
            
        return new_head.next

思路二:一次遍历用hashmap存放{当前节点:复制节点};

              再次遍历复制next/random指针;

class Solution(object):
    def copyRandomList(self, head):
        """
        :type head: Node
        :rtype: Node
        """
        if not head:return None
        
        hash = {}
        p = head
        while p:
            # hash[p] = Node(p.val, p.next, p.random)  next和random需要下一轮指定,因为此时结点还未完全创建
            hash[p] = Node(p.val)
            p = p.next

        p = head
        while p:
            if p.next: hash[p].next = hash[p.next]
            if p.random: hash[p].random = hash[p.random]
            p = p.next
        return hash[head]
148 排序链表

给你链表的头结点 head ,请将其按 升序 排列并返回 排序后的链表 。

思路一:冒泡排序—偷懒的方法是直接改变节点的值

class Solution(object):
    def sortList(self, head):
        """
        :type head: ListNode
        :rtype: ListNode
        """
        if head is None or head.next is None:
            return head
        
        head0 = ListNode(0)
        head0.next = head
        tail = None

        while head0.next != tail:
            pre, cur, nxt = head0, head0.next, head0.next.next
            while nxt != tail:                   # 一次冒泡将当前最大值移到末尾
                if cur.val > nxt.val:
                    pre.next, cur.next, nxt.next = nxt, nxt.next, cur
                    pre, nxt = nxt, cur.next
                else:
                    pre, cur, nxt = pre.next, cur.next, nxt.next

            tail = cur

        return head0.next

# 只能通过25/30,一个巨长的案例会超出时间限制;

思路二:归并排序—时间复杂度下降到O(nlogn)

class Solution:
    def sortList(self, head: Optional[ListNode]) -> Optional[ListNode]:

        def merge(head1, head2):
            dummy = ListNode(0)
            p = dummy
            while head1 and head2:
                if head1.val<head2.val:
                    p.next = head1
                    p, head1 = p.next, head1.next
                else:
                    p.next = head2
                    p, head2 = p.next, head2.next
            
            if head1: p.next = head1
            elif head2: p.next = head2

            return dummy.next

        def mergeSort(head):
            if not (head and head.next): return head

            slow, fast = head, head.next
            while fast and fast.next:
                slow = slow.next
                fast = fast.next.next

            slow_head, fast_head = head, slow.next
            slow.next = None    # 断开连接

            return merge(mergeSort(slow_head), mergeSort(fast_head))

        return mergeSort(head)
23 合并K个升序链表

给你一个链表数组,每个链表都已经按升序排列。请你将所有链表合并到一个升序链表中,返回合并后的链表。

思路一:两两合并直至成为一个链表—自底向上;

              将链表二分,先完成左侧排序,再完成右侧排序,然后两侧合并—自顶向下;

class Solution:
    def mergeKLists(self, lists: List[Optional[ListNode]]) -> Optional[ListNode]:

        def merge(head1, head2):
            dummy = ListNode(0)
            p = dummy
            while head1 and head2:
                if head1.val<head2.val:
                    p.next = head1
                    p, head1 = p.next, head1.next
                else:
                    p.next = head2
                    p, head2 = p.next, head2.next
            
            if head1: p.next = head1
            elif head2: p.next = head2

            return dummy.next
        
        n = len(lists)
        if n==0: return None
        elif n==1: return lists[0]
        else:
            left = self.mergeKLists(lists[:n//2])
            right = self.mergeKLists(lists[n//2:])
            return merge(left, right)

思路二:在比较k个链表未合并的节点值时可以利用堆排序来找到当前的最小值,而不是全部遍历;甚至于将全部节点入堆;

import heapq
class Solution:
    def mergeKLists(self, lists: List[Optional[ListNode]]) -> Optional[ListNode]:
        # 需要设定节点比较,否则在堆化时会报错
        # 也可以只取node.val构造堆,然后根据pop出的值构造新节点即可,这种方法需要在初始化时将全部节点入堆
        setattr(ListNode, "__lt__", lambda a, b: a.val < b.val)

        tmp = [head for head in lists if head]   # 头结点入堆
        heapq.heapify(tmp)

        dummy = ListNode(0)
        p = dummy
        while tmp:
            node = heapq.heappop(tmp)
            if node.next: heapq.heappush(tmp, node.next)
            p.next = node
            p = p.next
        
        return dummy.next

# 全部节点入堆
import heapq
class Solution:
    def mergeKLists(self, lists: List[Optional[ListNode]]) -> Optional[ListNode]:
        tmp = []
        for head in lists:
            while head:
                tmp.append(head.val)
                head = head.next

        heapq.heapify(tmp)

        dummy = ListNode(0)
        p = dummy
        while tmp:
            val = heapq.heappop(tmp)
            p.next = ListNode(val)
            p = p.next
        
        return dummy.next
146 LRU缓存

请你设计并实现一个满足  LRU (最近最少使用) 缓存 约束的数据结构。

实现 LRUCache 类:

  • LRUCache(int capacity) 以 正整数 作为容量 capacity 初始化 LRU 缓存
  • int get(int key) 如果关键字 key 存在于缓存中,则返回关键字的值,否则返回 -1 。
  • void put(int key, int value) 如果关键字 key 已经存在,则变更其数据值 value ;如果不存在,则向缓存中插入该组 key-value 。如果插入操作导致关键字数量超过 capacity ,则应该 逐出 最久未使用的关键字。

函数 get 和 put 必须以 O(1) 的平均时间复杂度运行。

考察:双向链表+哈希           在python中可以使用OrderedDict( )

# 有序字典
class LRUCache:

    def __init__(self, capacity: int):
        self.capacity = capacity
        self.lru = collections.OrderedDict()
        
    def get(self, key: int) -> int:
        if key in self.lru:
            self.lru.move_to_end(key)
            return self.lru[key]
        return -1

    def put(self, key: int, value: int) -> None:
        if self.get(key) == -1 and len(self.lru) == self.capacity:
            self.lru.popitem(last=False)
        self.lru[key] = value

# 双向链表+哈希
class LRUCache:
    def __init__(self, capacity):
        self.cache = dict()
        self.head = DLinkedNode()  # 使用伪头部和伪尾部节点  
        self.tail = DLinkedNode()
        self.head.next = self.tail
        self.tail.prev = self.head
        self.capacity = capacity
        self.size = 0

    def get(self, key):
        if key not in self.cache: return -1
        node = self.cache[key]      # 如果key存在,先通过哈希表定位,再移到头部
        self.moveToHead(node)
        return node.value

    def put(self, key, value):
        if key not in self.cache:
            node = DLinkedNode(key, value)   # 如果key不存在,创建一个新的节点 
            self.cache[key] = node        #  添加进哈希表
            self.addToHead(node)         # 添加至双向链表的头部
            self.size += 1
            if self.size > self.capacity:
                removed = self.removeTail()  # 如果超出容量,删除双向链表的尾部节点
                self.cache.pop(removed.key)  # 删除哈希表中对应的项
                self.size -= 1
        else:
            node = self.cache[key]    # 如果key存在,先通过哈希表定位,再修改value,并移到头部
            node.value = value
            self.moveToHead(node)
    
    def addToHead(self, node):
        node.prev = self.head
        node.next = self.head.next
        self.head.next.prev = node
        self.head.next = node
    
    def removeNode(self, node):
        node.prev.next = node.next
        node.next.prev = node.prev

    def moveToHead(self, node):
        self.removeNode(node)
        self.addToHead(node)

    def removeTail(self):
        node = self.tail.prev
        self.removeNode(node)
        return node
236 二叉树的最近公共祖先

给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。

百度百科中最近公共祖先的定义为:“对于有根树 T 的两个节点 p、q,最近公共祖先表示为一个节点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”

三种情况:p是q的最近公共祖先;q是p的最近公共祖先;p和q位于某个节点node的左右子树中,node为p和q的最近公共祖先;

即判断node是否为p,node是否为q,然后切换到node.left/node.right;

class Solution:
    def lowestCommonAncestor(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> 'TreeNode':
        if root==p or root==q or root==None: return root
        
        left=self.lowestCommonAncestor(root.left,p,q)
        right=self.lowestCommonAncestor(root.right,p,q)
        
        if left and right:return root
        if left and right==None:return left
        if right and left==None:return right
124 二叉树的最大路径和

二叉树中的 路径 被定义为一条节点序列,序列中每对相邻节点之间都存在一条边。同一个节点在一条路径序列中 至多出现一次 。该路径 至少包含一个 节点,且不一定经过根节点。路径和 是路径中各节点值的总和。

给你一个二叉树的根节点 root ,返回其 最大路径和 。

含有当前节点的最大路径和=left+right+node.val

当前节点能给其父节点提供的最大路径和=max(left, right)+node.val  若该值为负则清零

class Solution:
    def maxPathSum(self, root: Optional[TreeNode]) -> int:
        global max_sum 
        max_sum = float('-inf')
        def partPathSum(node):
            if node is None:return 0

            left = partPathSum(node.left)   # 左侧最大路径
            right = partPathSum(node.right) # 右侧最大路径

            global max_sum
            max_sum = max(max_sum, node.val+left+right)  # 当前节点的最大路径

            return max(node.val+max(left, right), 0)     # 单侧的最大路径(负数归零)
        
        partPathSum(root)
        return max_sum
208 实现前缀树

Trie(发音类似 "try")或者说 前缀树 是一种树形数据结构,用于高效地存储和检索字符串数据集中的键。这一数据结构有相当多的应用情景,例如自动补完和拼写检查。

请你实现 Trie 类:

  • Trie() 初始化前缀树对象。
  • void insert(String word) 向前缀树中插入字符串 word 。
  • boolean search(String word) 如果字符串 word 在前缀树中,返回 true(即,在检索之前已经插入);否则,返回 false 。
  • boolean startsWith(String prefix) 如果之前已经插入的字符串 word 的前缀之一为 prefix ,返回 true ;否则,返回 false 。

前缀树中一个父节点会对应多个子节点,且涉及到查找——hashmap

class TrieNode:
    def __init__(self):
        self.child = {}
        self.end = False

class Trie:

    def __init__(self):
        self.root = TrieNode()   # 虚拟根节点

    def insert(self, word: str) -> None:
        node = self.root
        for strr in word:
            if strr not in node.child:
                node.child[strr] = TrieNode()
            node = node.child[strr]
        node.end = True

    def search(self, word: str) -> bool:
        node = self.root
        for strr in word:
            if strr not in node.child:
                return False
            node = node.child[strr]
        return node.end

    def startsWith(self, prefix: str) -> bool:
        node = self.root
        for strr in prefix:
            if strr not in node.child:
                return False
            else:
                node = node.child[strr]
        return True
4 寻找两个正序数组的中位数

给定两个大小分别为 m 和 n 的正序(从小到大)数组 nums1 和 nums2。请你找出并返回这两个正序数组的 中位数 。算法的时间复杂度应该为 O(log (m+n)) 。

不断以二分的方式剔除不可能为中位数的值;

class Solution:
    def findMedianSortedArrays(self, nums1: List[int], nums2: List[int]) -> float:
        def getKthElement(k):
            index1, index2 = 0, 0
            while True:
                # 特殊情况
                if index1 == m:
                    return nums2[index2 + k - 1]
                if index2 == n:
                    return nums1[index1 + k - 1]
                if k == 1:
                    return min(nums1[index1], nums2[index2])

                # 正常情况
                newIndex1 = min(index1 + k // 2 - 1, m - 1)        # 防止越界
                newIndex2 = min(index2 + k // 2 - 1, n - 1)
                pivot1, pivot2 = nums1[newIndex1], nums2[newIndex2]
                if pivot1 <= pivot2:
                    k -= newIndex1 - index1 + 1
                    index1 = newIndex1 + 1
                else:
                    k -= newIndex2 - index2 + 1
                    index2 = newIndex2 + 1
        
        m, n = len(nums1), len(nums2)
        totalLength = m + n
        if totalLength % 2 == 1:
            return getKthElement((totalLength + 1) // 2)
        else:
            return (getKthElement(totalLength // 2) + getKthElement(totalLength // 2 + 1)) / 2.
84 柱状图中的最大矩形

给定 n 个非负整数,用来表示柱状图中各个柱子的高度。每个柱子彼此相邻,且宽度为 1 。求在该柱状图中,能够勾勒出来的矩形的最大面积。

class Solution:
    def largestRectangleArea(self, heights: List[int]) -> int:
        n = len(heights)
        left, right = [-1]*n, [n]*n
        stack = []
        for i in range(n):
            while stack and heights[stack[-1]] >= heights[i]: # 找到val的左界
                stack.pop()
            if stack: left[i] = stack[-1]
            stack.append(i)
        print(left)

        stack = []
        for i in range(n-1, -1, -1):         
            while stack and heights[stack[-1]] >= heights[i]: # 找到val的右界
                stack.pop()
            if stack: right[i] = stack[-1]
            stack.append(i)
        print(right)
        
        out = 0
        for i in range(n):
            ans = heights[i]*(right[i]-left[i]-1)
            out = max(ans, out)
        return out
堆 215/347/295

【Leetcode】top 100 堆-CSDN博客

32 最长有效括号

给你一个只包含 '(' 和 ')' 的字符串,找出最长有效(格式正确且连续)括号子串的长度。

方法一:动态规划:dp[i]代表长度为i的字符串的最长有效括号子串的长度

当s[i] = '('时,不考虑有效情况;

当s[i] = ')'时,若dp[i-1]=k且s[i-k-1]='(',dp[i]=dp[i-1]+2

                      如果此时能与dp[i-k-2]连接起来,dp[i]=dp[i-1]+2+dp[i-k-2]

class Solution:
    def longestValidParentheses(self, s: str) -> int:
        n = len(s)
        dp = [0]*n
        maxlen = 0
        for i in range(n):
            if s[i] == '(': continue
            else:
                if i-dp[i-1]-1>=0 and s[i-dp[i-1]-1]=='(':   # 需要下标i-dp[i-1]-1有效
                    if i-dp[i-1]-2>=0:                       # 需要下标i-dp[i-1]-2有效
                        dp[i]=dp[i-1]+2+dp[i-dp[i-1]-2]
                    else:
                        dp[i] = dp[i-1]+2
            maxlen = max(maxlen, dp[i])
        return maxlen

方法二:栈来判断匹配是否成功,开辟数组记录匹配成功的括号,最后统计连续的最长长度;

class Solution:
    def longestValidParentheses(self, s: str) -> int:

        stack, tmp = [], [0]*len(s)

        for i, strr in enumerate(s):
            if strr=='(': stack.append(i)
            else:
                if stack and s[stack[-1]]=='(':
                    tmp[i], tmp[stack[-1]] = 1, 1
                    stack.pop()

        length, maxlen = 0, 0
        for num in tmp:
            if num: length += num
            else:
                maxlen = max(maxlen, length)
                length = 0
        maxlen = max(maxlen, length)
        return maxlen
5 最长回文子串

给你一个字符串 s,找到 s 中最长的回文子串。如果字符串的反序与原始字符串相同,则该字符串称为回文字符串。

方法一:直接模拟  一次遍历以当前字符为回文中心进行扩展,更新最长回文子串;

方法二:动态规划  区间dp

dp[i][j]代表s[i:j+1]的字符串的回文情况,最长回文子串选择记录起始点和长度

状态转移:当s[i]==s[j]时且dp[i+1][j-1]为回文子串时,dp[i][j]也为回文子串;

因为dp[i][j]状态依赖于dp[i+1][j-1],所以需要从下往上/从左往右遍历来更新dp;

class Solution:
    def longestPalindrome(self, s: str) -> str:
        n = len(s)
        dp = [[False]*n for _ in range(n)]
        start, maxlen = 0, 1

        for i in range (n-1,-1,-1):
            for j in range (i, n):      # 注意j的范围
                # 奇长度的回文串中心
                if j==i: dp[i][j] = True  #i=0/j=0仍为False
                # 偶长度的回文串中心
                elif j==i+1: 
                    if s[i]==s[j]: dp[i][j] = True
                # 长度扩展
                else:
                    if s[i]==s[j] and dp[i+1][j-1]: dp[i][j] = True
                
                if dp[i][j]:              # 更新起点和长度
                    if j-i+1>maxlen:
                        maxlen = j-i+1
                        start = i
        return s[start:start+maxlen]

  • 16
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值