【Leetcode】 top100 round2 直接开背版

2024-05-10 23:36

本文主要是介绍【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 headp = ListNode(0)  #创建一个虚拟的头结点# p.next = headold_head, left, right = p, head, headwhile right:for _ in range(k-1):right = right.nextif not right: return p.next     old_tail = right.nextnew_head, new_tail = self.reverse(left, right)old_head.next = new_headnew_tail.next = old_tailold_head, left, right = new_tail, old_tail, old_tailreturn p.nextdef reverse(self, head, tail):pre, cur = head, head.nextwhile 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 Nonep = headwhile p:                 # 插入复制的新结点p.next = Node(p.val, p.next, None)p = p.next.nextp = headwhile p:                 # 设置random指针if p.random: p.next.random = p.random.nextp = p.next.nextp = headnew_head = Node(0)q = new_headwhile p:                 # 分离两链表q.next = p.nextq = q.nextp.next = q.next      # 不直接用p.next.next是因为要恢复原链表p = p.nextreturn new_head.next

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

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

class Solution(object):def copyRandomList(self, head):""":type head: Node:rtype: Node"""if not head:return Nonehash = {}p = headwhile p:# hash[p] = Node(p.val, p.next, p.random)  next和random需要下一轮指定,因为此时结点还未完全创建hash[p] = Node(p.val)p = p.nextp = headwhile p:if p.next: hash[p].next = hash[p.next]if p.random: hash[p].random = hash[p.random]p = p.nextreturn 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 headhead0 = ListNode(0)head0.next = headtail = Nonewhile head0.next != tail:pre, cur, nxt = head0, head0.next, head0.next.nextwhile nxt != tail:                   # 一次冒泡将当前最大值移到末尾if cur.val > nxt.val:pre.next, cur.next, nxt.next = nxt, nxt.next, curpre, nxt = nxt, cur.nextelse:pre, cur, nxt = pre.next, cur.next, nxt.nexttail = curreturn head0.next# 只能通过25/30,一个巨长的案例会超出时间限制;

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

class Solution:def sortList(self, head: Optional[ListNode]) -> Optional[ListNode]:def merge(head1, head2):dummy = ListNode(0)p = dummywhile head1 and head2:if head1.val<head2.val:p.next = head1p, head1 = p.next, head1.nextelse:p.next = head2p, head2 = p.next, head2.nextif head1: p.next = head1elif head2: p.next = head2return dummy.nextdef mergeSort(head):if not (head and head.next): return headslow, fast = head, head.nextwhile fast and fast.next:slow = slow.nextfast = fast.next.nextslow_head, fast_head = head, slow.nextslow.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 = dummywhile head1 and head2:if head1.val<head2.val:p.next = head1p, head1 = p.next, head1.nextelse:p.next = head2p, head2 = p.next, head2.nextif head1: p.next = head1elif head2: p.next = head2return dummy.nextn = len(lists)if n==0: return Noneelif 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 = dummywhile tmp:node = heapq.heappop(tmp)if node.next: heapq.heappush(tmp, node.next)p.next = nodep = p.nextreturn 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.nextheapq.heapify(tmp)dummy = ListNode(0)p = dummywhile tmp:val = heapq.heappop(tmp)p.next = ListNode(val)p = p.nextreturn 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 = capacityself.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 -1def 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.tailself.tail.prev = self.headself.capacity = capacityself.size = 0def get(self, key):if key not in self.cache: return -1node = self.cache[key]      # 如果key存在,先通过哈希表定位,再移到头部self.moveToHead(node)return node.valuedef put(self, key, value):if key not in self.cache:node = DLinkedNode(key, value)   # 如果key不存在,创建一个新的节点 self.cache[key] = node        #  添加进哈希表self.addToHead(node)         # 添加至双向链表的头部self.size += 1if self.size > self.capacity:removed = self.removeTail()  # 如果超出容量,删除双向链表的尾部节点self.cache.pop(removed.key)  # 删除哈希表中对应的项self.size -= 1else:node = self.cache[key]    # 如果key存在,先通过哈希表定位,再修改value,并移到头部node.value = valueself.moveToHead(node)def addToHead(self, node):node.prev = self.headnode.next = self.head.nextself.head.next.prev = nodeself.head.next = nodedef removeNode(self, node):node.prev.next = node.nextnode.next.prev = node.prevdef moveToHead(self, node):self.removeNode(node)self.addToHead(node)def removeTail(self):node = self.tail.prevself.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 rootleft=self.lowestCommonAncestor(root.left,p,q)right=self.lowestCommonAncestor(root.right,p,q)if left and right:return rootif left and right==None:return leftif 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 0left = partPathSum(node.left)   # 左侧最大路径right = partPathSum(node.right) # 右侧最大路径global max_summax_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 = Falseclass Trie:def __init__(self):self.root = TrieNode()   # 虚拟根节点def insert(self, word: str) -> None:node = self.rootfor strr in word:if strr not in node.child:node.child[strr] = TrieNode()node = node.child[strr]node.end = Truedef search(self, word: str) -> bool:node = self.rootfor strr in word:if strr not in node.child:return Falsenode = node.child[strr]return node.enddef startsWith(self, prefix: str) -> bool:node = self.rootfor strr in prefix:if strr not in node.child:return Falseelse: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, 0while 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 + 1index1 = newIndex1 + 1else:k -= newIndex2 - index2 + 1index2 = newIndex2 + 1m, n = len(nums1), len(nums2)totalLength = m + nif 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]*nstack = []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 = 0for 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]*nmaxlen = 0for i in range(n):if s[i] == '(': continueelse: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]+2maxlen = 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, 1stack.pop()length, maxlen = 0, 0for num in tmp:if num: length += numelse:maxlen = max(maxlen, length)length = 0maxlen = 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, 1for 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] = Trueif dp[i][j]:              # 更新起点和长度if j-i+1>maxlen:maxlen = j-i+1start = ireturn s[start:start+maxlen]

这篇关于【Leetcode】 top100 round2 直接开背版的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/977883

相关文章

哈希leetcode-1

目录 1前言 2.例题  2.1两数之和 2.2判断是否互为字符重排 2.3存在重复元素1 2.4存在重复元素2 2.5字母异位词分组 1前言 哈希表主要是适合于快速查找某个元素(O(1)) 当我们要频繁的查找某个元素,第一哈希表O(1),第二,二分O(log n) 一般可以分为语言自带的容器哈希和用数组模拟的简易哈希。 最简单的比如数组模拟字符存储,只要开26个c

leetcode-24Swap Nodes in Pairs

带头结点。 /*** Definition for singly-linked list.* public class ListNode {* int val;* ListNode next;* ListNode(int x) { val = x; }* }*/public class Solution {public ListNode swapPairs(L

leetcode-23Merge k Sorted Lists

带头结点。 /*** Definition for singly-linked list.* public class ListNode {* int val;* ListNode next;* ListNode(int x) { val = x; }* }*/public class Solution {public ListNode mergeKLists

C++ | Leetcode C++题解之第393题UTF-8编码验证

题目: 题解: class Solution {public:static const int MASK1 = 1 << 7;static const int MASK2 = (1 << 7) + (1 << 6);bool isValid(int num) {return (num & MASK2) == MASK1;}int getBytes(int num) {if ((num &

【每日一题】LeetCode 2181.合并零之间的节点(链表、模拟)

【每日一题】LeetCode 2181.合并零之间的节点(链表、模拟) 题目描述 给定一个链表,链表中的每个节点代表一个整数。链表中的整数由 0 分隔开,表示不同的区间。链表的开始和结束节点的值都为 0。任务是将每两个相邻的 0 之间的所有节点合并成一个节点,新节点的值为原区间内所有节点值的和。合并后,需要移除所有的 0,并返回修改后的链表头节点。 思路分析 初始化:创建一个虚拟头节点

C语言 | Leetcode C语言题解之第393题UTF-8编码验证

题目: 题解: static const int MASK1 = 1 << 7;static const int MASK2 = (1 << 7) + (1 << 6);bool isValid(int num) {return (num & MASK2) == MASK1;}int getBytes(int num) {if ((num & MASK1) == 0) {return

【JavaScript】LeetCode:16-20

文章目录 16 无重复字符的最长字串17 找到字符串中所有字母异位词18 和为K的子数组19 滑动窗口最大值20 最小覆盖字串 16 无重复字符的最长字串 滑动窗口 + 哈希表这里用哈希集合Set()实现。左指针i,右指针j,从头遍历数组,若j指针指向的元素不在set中,则加入该元素,否则更新结果res,删除集合中i指针指向的元素,进入下一轮循环。 /*** @param

LeetCode:64. 最大正方形 动态规划 时间复杂度O(nm)

64. 最大正方形 题目链接 题目描述 给定一个由 0 和 1 组成的二维矩阵,找出只包含 1 的最大正方形,并返回其面积。 示例1: 输入: 1 0 1 0 01 0 1 1 11 1 1 1 11 0 0 1 0输出: 4 示例2: 输入: 0 1 1 0 01 1 1 1 11 1 1 1 11 1 1 1 1输出: 9 解题思路 这道题的思路是使用动态规划

LeetCode 第414场周赛个人题解

目录 Q1. 将日期转换为二进制表示 原题链接 思路分析 AC代码 Q2. 范围内整数的最大得分 原题链接 思路分析 AC代码 Q3. 到达数组末尾的最大得分 原题链接 思路分析 AC代码 Q4. 吃掉所有兵需要的最多移动次数 原题链接 思路分析 AC代码 Q1. 将日期转换为二进制表示 原题链接 Q1. 将日期转换为二进制表示 思路分析

【JavaScript】LeetCode:21-25

文章目录 21 最大子数组和22 合并区间23 轮转数组24 除自身以外数组的乘积25 缺失的第一个正数 21 最大子数组和 贪心 / 动态规划贪心:连续和(count)< 0时,放弃当前起点的连续和,将下一个数作为新起点,这里提供使用贪心算法解决本题的代码。动态规划:dp[i]:以nums[i]为结尾的最长连续子序列(子数组)和。 dp[i] = max(dp[i - 1]