990. Satisfiability of Equality Equations
满足所有方程式,判断是否存在变量的值满足所有的等式与不等式。原题
解析:所有的相等的点,在图中是联通的。
1 | Input: ["a==b","b!=a"] |
方法一:set.
1 | def equationsPossible(self, equations: 'List[str]') -> 'bool': |
a==b, b==c
时fc={'a': 'b', 'b': 'c', 'c': 'c'}
比较a!=c
时就会最终找到fc['a'] == 'c'
;如a==b, c==a时,fc={'a': 'b', 'b': 'b', 'c': 'b'}
。
1 | def equationsPossible(self, equations: 'List[str]') -> 'bool': |
997. Find the Town Judge
找到小镇审判长。审判长被除自己以外的所有人信任,并且不信任任何人。根据信任列表找出审判长。原题
1 | Input: N = 4, trust = [[1,3],[1,4],[2,3],[2,4],[4,3]] |
方法一:brute force.
1 | def findJudge(self, N: int, trust: List[List[int]]) -> int: |
1 | def findJudge(self, N: int, trust: List[List[int]]) -> int: |
133. Clone Graph
深拷贝一个简单环。原题
1 | def cloneGraph(self, node: 'Node') -> 'Node': |
1334. Find the City With the Smallest Number of Neighbors at a Threshold Distance
找到距离范围内邻居最少的城市。原题
1 | Input: n = 4, edges = [[0,1,3],[1,2,1],[1,3,4],[2,3,1]], distanceThreshold = 4 |
1 | def findTheCity(self, n: int, edges: List[List[int]], distanceThreshold: int) -> int: |
方法二:弗洛伊德算法,这个时间复杂度为O(N^3),space: O(N^2)但是代码简单。把每个节点当成中转点k,如果dis[i][j] > dis[i][k] + dis[k][j]
说明从k走,i, j距离更短。
1 | def findTheCity(self, n: int, edges: List[List[int]], maxd: int) -> int: |
1267. Count Servers that Communicate
找到2个以上的服务器连接个数,服务器可以在一行或是一列算是连接上。原题
1 | Input: grid = [[1,1,0,0],[0,0,1,0],[0,0,1,0],[0,0,0,1]] |
方法一:和小岛问题不同,这个服务器可以隔空连接,AC时用的dfs方法,效率非常慢。实际上只需要记录横纵左边即可,遍历两次
1 | def countServers(self, g: List[List[int]]) -> int: |
1 | def countServers(self, g: List[List[int]]) -> int: |
886. Possible Bipartition
将不喜欢的人放在两组中,根据关系是否能将其分为2组。原题
1 | Input: N = 4, dislikes = [[1,2],[1,3],[2,4]] |
方法一:dfs。此题想了半个多点才想明白,等同于在一个无向图中,寻找一个奇数边的环。
1 | def possibleBipartition(self, N: int, dislikes: List[List[int]]) -> bool: |
方法二:染色思想。0代表红色,1代表蓝色,每次将其不喜欢的人染成另一种颜色。这个代码比较简洁,不过稍微慢了一丢丢,50ms左右。
1 | def possibleBipartition(self, N: int, dislikes: List[List[int]]) -> bool: |
207. Course Schedule
课程调度,课程有依赖关系,问是否能完成所有的课程。原题
1 | Input: numCourses = 2, prerequisites = [[1,0]] |
方法一:dfs。注意这里状态要用3中,1表示遍历过,-1表示正在遍历,0表未遍历。这样可以避免重复的遍历。
1 | def canFinish(self, n: int, prerequisites: List[List[int]]) -> bool: |
1 | def canFinish(self, n: int, prerequisites: List[List[int]]) -> bool: |
1462. Course Schedule IV
和上题差不多,问题不一样,问的是根据给定的依赖关系,判断两节课是否有依赖。原题
方法一:dfs. 这个方法想了超出比赛时间限制了。但是也没过多久就优化 出来了。1 | def checkIfPrerequisite(self, n: int, prerequisites: List[List[int]], queries: List[List[int]]) -> List[bool]: |
方法二:弗洛伊德算法,和求城市最小距离一样。时间复杂度是O(n3)
1 | def checkIfPrerequisite(self, n: int, prerequisites: List[List[int]], queries: List[List[int]]) -> List[bool]: |
1 | def checkIfPrerequisite(self, n: int, prerequisites: List[List[int]], queries: List[List[int]]) -> List[bool]: |
1466. Reorder Routes to Make All Paths Lead to the City Zero
有一个有向图,两个节点之间只有一条边,要求所有的边指向0,需要改多少条边的方向。原题
1 | Input: n = 6, connections = [[0,1],[1,3],[2,3],[4,0],[4,5]] |
方法一:此题竞赛时未作出。一个效率不是很高的方法。用父节点去重。set记录原始顺序,遍历无向图。
1 | def minReorder(self, n: int, connections: List[List[int]]) -> int: |
方法二:比赛时想的思路,dfs内容未想出来。
1 | def minReorder(self, n: int, connections: List[List[int]]) -> int: |
1210. Minimum Moves to Reach Target with Rotations
一个占位2格的蛇,从左上走到右下需要最少的步数,可以旋转。原题
1 | Input: grid = [[0,0,0,0,0,1], |
方法一:这题没想到在写完还有10分钟,其实思路很简单,就是拿两个点当成一个点。一个点的bfs就很容易了。
1 | def minimumMoves(self, g: List[List[int]]) -> int: |
1 | def minimumMoves(self, g: List[List[int]]) -> int: |
1202. Smallest String With Swaps
给定一组pairs表明索引对可以互换,求这个字符串能换的最小值时多少,同一对可以进行多次互换。原题
1 | Input: s = "dcab", pairs = [[0,3],[1,2]] |
方法一:看了几个例子想一想就明白了,实际上是一道连通器的题,将连通的索引单独排序就是最小的值。
竞赛时ac的方法用的是dfs. 内存用的有点多。
1 | def smallestStringWithSwaps(self, s: str, pairs: List[List[int]]) -> str: |
方法二:做了一点优化。
1 | def smallestStringWithSwaps(self, s: str, pairs: List[List[int]]) -> str: |
方法三:union-find。这个评论区里看到的方法写得很标准。
1 | def smallestStringWithSwaps(self, s: str, pairs: List[List[int]]) -> str: |
方法四:我自己的Union-Find。
1 | def smallestStringWithSwaps(self, s: str, pairs: List[List[int]]) -> str: |
787. Cheapest Flights Within K Stops
经过K个站点的最便宜的航班。原题
方法一:狄克斯特拉算法,只不过多了一个条件,经过K个站点。不需要用seen记录已经去过的点,因为该点可能有更少步数的到达方式。
1 | def findCheapestPrice(self, n: int, flights: List[List[int]], src: int, dst: int, K: int) -> int: |
332. Reconstruct Itinerary
重建行程,根据火车票来寻找行程,答案不唯一。原题
1 | Input: [["MUC", "LHR"], ["JFK", "MUC"], ["SFO", "SJC"], ["LHR", "SFO"]] |
方法一:首次ac的方法,直接修改g。
1 | def findItinerary(self, tickets: List[List[str]]) -> List[str]: |
1 | def findItinerary(self, tickets: List[List[str]]) -> List[str]: |
1519. Number of Nodes in the Sub-Tree With the Same Label
有一个数的图,每个节点有个字母,返回每个节点的子树中包含这个字母的总个数。原题
方法一:这个题作为竞赛的第二题是在是太恶心了,结束后我想了很长时间才想出来。dfs + counter, 每个节点都有它独立的一个counter。 因为字母不同、
1 | def countSubTrees(self, n: int, edges: List[List[int]], labels: str) -> List[int]: |
1 | def countSubTrees(self, n: int, edges: List[List[int]], labels: str) -> List[int]: |
478. Generate Random Point in a Circle
在给定圆的范围内随机生成点。原题
方法一:严格来说 这是一道几何题,为什么要将其开方,因为如果没有开方,对于一个圆来说,半径越小的地方点就越密集,因为在该周长上所有的点分布是一样的。开方可以让半径小的点降低分布。
1 | class Solution: |
1559. Detect Cycles in 2D Grid
2D 矩阵中判断是否有环。原题
方法一:这个题就差一点没想出来,就是怎么避免重复的路径,那就是需要和前一个点比对。看了这个提示后瞬间就完成了。时间空间待优化。
1 | def containsCycle(self, g: List[List[str]]) -> bool: |
310. Minimum Height Trees
有一个无向图,0~n-1这些节点,每两个节点只有一条边,找出这种节点,当以节点为根时,组成的树有最小的高度。原题
1 | Input: n = 6, edges = [[0, 3], [1, 3], [2, 3], [4, 3], [5, 4]] |
方法一:这个解法很棒,评论区的解法,以前看这种题的时候,想到的是bfs,这个解法的核心思想是剪枝。每次将叶子节点剪去。最后剩下的 一个或者2个节点 就是最小高度的根节点。
1 | def findMinHeightTrees(self, n: int, edges: List[List[int]]) -> List[int]: |
127. Word Ladder
每次可以改变单词中的一个字母,但必须在wordList中,问最少需要多少次能达到目标单词。原题
1 | Input: |
方法一:BFS很好想,但是要想如何去重,wordList可能非常大,所以不能遍历,因为只有26个字母可以变,所以可以变换完比较。
1 | def ladderLength(self, beginWord: str, endWord: str, wordList: List[str]) -> int: |
547. Friend Circles
朋友圈,一堆人拉帮结伙,朋友关系可以传递。通过二维数组表示i, j两个人是否是朋友。问N个人有多少个帮派。原题
1 | Input: |
方法一:典型的Union-Find。最后结果还要遍历一下,才是最终结果。[[1,0,0,1],[0,1,1,0],[0,1,1,1],[1,0,1,1]]
这个case在union之后,是[3,2,3,3]
因为2的朋友在1计算之后变成了3。最终点更新了。所以遍历之后是[3,3,3,3]
1 | def findCircleNum(self, M: List[List[int]]) -> int: |
方法二:评论中有个dfs 也不错,行列循环分开了。
1 | def findCircleNum(self, M: List[List[int]]) -> int: |
方法三:stefan的科学库和numpy写法。
1 | import scipy.sparse |
1584. Min Cost to Connect All Points
所有点最小的曼哈顿距离|xi - xj| + |yi - yj|和为多少。原题
方法一:贪心法。这题有很多方法,然而比赛时一种也没写出来。首先贪心可以,由于是n^2的复杂度,所以没有敢写。
毕竟n=1000。评论区的做法,为什么贪心可行,想象一下,最后所有的点相连,每2点只有一条边,而且是最小的边。
1 | def minCostConnectPoints(self, points: List[List[int]]) -> int: |
方法二:最小生成树。这是经典的最小生成树问题,有两种实现的方式,一种是Prim算法。一种是Kruskal算法。比贪心法慢了400ms,花费1580ms,还以为是我实现的问题,结果看评论区中的要2s多。空间则是贪心的5倍多。因为此题需要找到各个边的权重,所以即使是Prim算法,时间复杂度也是O(n^2)。Prim算法是每次以新的顶点找到最小的权重边。
1 | def minCostConnectPoints(self, p: List[List[int]]) -> int: |
方法三:Kruskal,克鲁斯卡尔算法。原理是Union-Find。按照所有的权重看,每次产生一条边,但不一定和已有的边构成环。理论上来说比Prim算法要慢。实际运行却比prim快了1、200ms。
1 | def minCostConnectPoints(self, p: List[List[int]]) -> int: |
684. Redundant Connection
多余的边,有一个无向图有N个节点,和N个边,对于一个连通器来说,刚好多了一条边。找到这条多余的边,所有的边给的[u, v]均满足u<v。原题
1 | Input: [[1,2], [1,3], [2,3]] |
方法一:使用传统的Union-Find。
1 | def findRedundantConnection(self, edges: List[List[int]]) -> List[int]: |
u<v
。 可以利用字符串的replace。将所有出现的点,替换成最大的。
1 | def findRedundantConnection(self, edges: List[List[int]]) -> List[int]: |
685. Redundant Connection II
同684一样,区别在于有向图,也是多出一条边,但这条边是最后一条无用的边。也就是说。删除这条边后,还是一个N个节点的有向图。并且图中只能有一个根节点。
方法一:花了不到2小时做出来,感觉方法很笨。因为有这样的例子,[[2,1],[3,1],[4,2],[1,4]]
这时不能删除[1,4]
,而是要删除[2,1]
因为4和3会成为2个根节点。如果我们找到了一个点有两个父节点,那么删除的一定是这两条边中的一条。所以此时我没有将其加入到联通图中;另一种情况,没有两个父节点的点时,就像684一样判断是否联通就好了,最后判断,如果所有节点都联通了,我删除第2次遇见的相同父节点的边就行了,如果没有,那么应该删除第一次的边,这里用了一个pare来记录关系。
1 | def findRedundantDirectedConnection(self, edges: List[List[int]]) -> List[int]: |
743. Network Delay Time
网络延迟,有N个网络节点,给出一个加权有向图,问从K点出发,遍历所有点需要多少时间,如果不可能返回-1。原题
1 | Input: times = [[2,1,1],[2,3,1],[3,4,1]], N = 4, K = 2 |
方法一:bfs。首次AC,960ms。效率不算很高吧,因为可能遍历到重复的节点更新值。
1 | def networkDelayTime(self, times, N, K): |
1 | def networkDelayTime(self, times, N, K): |
721. Accounts Merge
账号合并,一个人可能有多个账号,将具有相同账号的人合并到一起,返回所有的用户的所有的账号
方法一:做的时候想到了union-find,不过以开始的思路用集合暴力也解出来了。只要有一个账号相同,就是一个人。
1 | def accountsMerge(self, accounts: List[List[str]]) -> List[List[str]]: |
1 | def accountsMerge(self, accounts: List[List[str]]) -> List[List[str]]: |
1 | def accountsMerge(self, accounts: List[List[str]]) -> List[List[str]]: |
面试题 17.07. 婴儿名字
找到相同名字的数量,和721一样,也是合并婴儿。names中可能包含synonyms中没有的,synonyms中也可能包含names没有的。
1 | 输入:names = ["John(15)","Jon(12)","Chris(13)","Kris(4)","Christopher(19)"], synonyms = ["(Jon,John)","(John,Johnny)","(Chris,Kris)","(Chris,Christopher)"] |
方法一:Union-Find。改进,这里修改了union方法,将较小的直接作为索引键。因为后续也不需要再使用了。360ms, 35M.
1 | class Solution: |
方法二:dfs 方法。cnt 不能使用defaultdict,因为循环cnt的时候会发生改变报错。因为会存在cnt中没有的key,也就是说不在names中的名字。448ms, 40M.
1 | def trulyMostPopular(self, names: List[str], synonyms: List[str]) -> List[str]: |
1 | def trulyMostPopular(self, names: List[str], synonyms: List[str]) -> List[str]: |
851. Loud and Rich
啰里啰嗦一大堆,就是找到比当前人有钱的所有人中最低调的一个,quiet值最小,quiet没有重复值。
1 | Input: richer = [[1,0],[2,1],[3,1],[3,7],[4,3],[5,3],[6,3]], quiet = [3,2,5,4,6,1,7,0] |
方法一:dfs. 首次AC,算比较简单。
1 | def loudAndRich(self, richer: List[List[int]], quiet: List[int]) -> List[int]: |
1 | def loudAndRich(self, richer: List[List[int]], quiet: List[int]) -> List[int]: |
399. Evaluate Division
通过已知的除法,计算带有这些变量的除法。
1 | Input: equations = [["a","b"],["b","c"]], values = [2.0,3.0], queries = [["a","c"],["b","a"],["a","e"],["a","a"],["x","x"]] |
方法一:DFS。这题一开始想的是Union-Find方法,但是实现起来发现有点麻烦。需要注意一个问题,在遍历后未找到目标值时,需要添加-1。
1 | def calcEquation(self, equations: List[List[str]], values: List[float], queries: List[List[str]]) -> List[float]: |
方法二:BFS方法也很优雅。
1 | def calcEquation(self, equations: List[List[str]], values: List[float], queries: List[List[str]]) -> List[float]: |
方法三:Union-Find,自己想了个七八,也是用tuple返回一个比率,最后写着写着给自己绕蒙了,实现起来还是没有另两个方法简单。union方法其实融合了两个功能。然后就是将ratio
添加到模板方法中。
1 | def calcEquation(self, equations: List[List[str]], values: List[float], queries: List[List[str]]) -> List[float]: |
方法四:Stefan的弗洛伊德算法。这个没有想到。
1 | def calcEquation(self, equations: List[List[str]], values: List[float], queries: List[List[str]]) -> List[float]: |
1617. Count Subtrees With Max Distance Between Cities
给你一颗树,求任意子树中节点最大距离1~n-1的分别有多少个。最多有15个节点。
1 | 输入:n = 4, edges = [[1,2],[2,3],[2,4]] |
方法一:比赛时没有时间做,虽然hard但是比较简单,看了题解后写上了。就是暴力的枚举所有的子树,然后再bfs求最大距离,同时判断一下是否构成了子树(这个组合所有节点都出现了)。这里根据Lee215的竞赛写法做了一些调整。时间复杂度为2**15。
1 | def countSubgraphsForEachDiameter(self, n: int, edges: List[List[int]]) -> List[int]: |
1284. Minimum Number of Flips to Convert Binary Matrix to Zero Matrix
二维矩阵中, 每次翻拍“十”字,最小步数翻成0.原题
1 | Input: mat = [[0,0],[0,1]] |
方法一:看了答案才做出来,看到提示bfs,但是没有想到可以将这个二维矩阵转成一个二进制是它的数。
1 | def minFlips(self, mat: List[List[int]]) -> int: |
1293. Shortest Path in a Grid with Obstacles Elimination
最短路径,可以打通障碍k次。原题
1 | Input: |
方法一:一开始想到了正确思路,但是想错了最小步骤怎么求,其实不用求,第一个到达的就是最小的步数。
1 | def shortestPath(self, g: List[List[int]], k: int) -> int: |
1298. Maximum Candies You Can Get from Boxes
最多从盒子中拿到的糖果。从初始的盒子中往外取,盒子能嵌套盒子,盒子如果上锁还需要钥匙打开。原题
方法一:有一点没有想到,就是如果前面的盒子被拿出来,但是没有钥匙,知道后面的盒子才获取到钥匙。这种情况需要如何处理?这里参考了lee215的方式。q来表示所有已经打开的盒子,seen表示所有可见的盒子,这样当拿到钥匙时,其实需要做两个操作,如果这个盒子可见,将其放入q,如果盒子没见,将其锁打开(虽不符常理),这样在遇见盒子时可以直接将其添加到队列。
1 | def maxCandies(self, status: List[int], candies: List[int], keys: List[List[int]], containedBoxes: List[List[int]], initialBoxes: List[int]) -> int: |
1345. Jump Game IV
跳到末尾最小步数,可以向前向后或者跳到一样的值。原题
1 | Input: arr = [100,-23,-23,404,100,23,23,23,3,404] |
方法一:bfs。需要注意num_seen记录所有的同值,否则会超时。
1 | def minJumps(self, arr: List[int]) -> int: |
1391. Check if There is a Valid Path in a Grid
判断是否能到达右下点。原题
1 | Input: grid = [[2,4,3],[6,5,2]] |
方法一:dfs。需要注意的是(-x, -y) in directions[grid[ni][nj]])
是用来判断该块和下一块是否是联通的。
1 | def hasValidPath(self, grid: List[List[int]]) -> bool: |
1162. As Far from Land as Possible
离陆地最远的点的距离。原题
1 | Input: [[1,0,1],[0,0,0],[1,0,1]] |
方法一:bfs. 此题竞赛时想到了解法,但是出发点有点问题,是从水找陆地,这样的话有很多重复的循环,导致超时。正确的思路是从陆地遍历水。
1 | def maxDistance(self, grid: List[List[int]]) -> int: |
1091. Shortest Path in Binary Matrix
从左上到右下,只能走0,求最短路径长度,可以斜着走。原题
方法一:核心思想为,BFS,到终点的深度,使用两个数组。
1 | def shortestPathBinaryMatrix(self, grid: List[List[int]]) -> int: |
1 | def shortestPathBinaryMatrix(self, grid: List[List[int]]) -> int: |
1034. Coloring A Border
给边界染色,此题和733.flood fill很像,区别在于,那个是将所有区域染色,这个是只将边界染色。原题
方法一:写法稍作修改,将判断移到前面。四个方向只要有一个返回False就说明是边界的点。
1 | def colorBorder(self, grid: List[List[int]], r0: int, c0: int, color: int) -> List[List[int]]: |
1031. Number of Enclaves
求被困的地点个数。1表示陆地,不能够走到边界的陆地称为被困的陆地。原题
方法一:简单的DFS
1 | def numEnclaves(self, A: List[List[int]]) -> int: |
695. Max Area of Island
最大的岛屿面积。原题
方法一:dfs.
1 | def maxAreaOfIsland(self, grid: List[List[int]]) -> int: |
994. Rotting Oranges
腐烂的橘子,1表示新鲜,2表示腐烂,每分钟腐烂的会传染给四周上下左右的橘子,问所有的橘子腐烂最少需要几分钟。原题
方法一:竞赛时虽然做出来了,但有些点没想出来,其实是BFS,这样一想退出条件就很清楚了。
1 | def orangesRotting(self, g: 'List[List[int]]') -> 'int': |
733. Flood Fill
“洪水填充”,给定一个二维数组表示一张图片的像素,然后指定一个点和一个颜色,将这个点和上下左右四个点使用新的颜色填充,和这个点原来相同的颜色的点也受到影响,依次扩散,返回新的图片数组。原题
1 | Input: |
方法一:dfs. 需要注意的是,如果目标点与颜色相同,则原图不变。
1 | def floodFill(self, image, sr, sc, newColor): |
1 | def floodFill(self, image, sr, sc, newColor): |
1625. Lexicographically Smallest String After Applying Operations
给你一个字符串 s 以及两个整数 a 和 b 。其中,字符串 s 的长度为偶数,且仅由数字 0 到 9 组成。
你可以在 s 上按任意顺序多次执行下面两个操作之一:
累加:将 a 加到 s 中所有下标为奇数的元素上(下标从 0 开始)。数字一旦超过 9 就会变成 0,如此循环往复。例如,s = “3456” 且 a = 5,则执行此操作后 s 变成 “3951”。
轮转:将 s 向右轮转 b 位。例如,s = “3456” 且 b = 1,则执行此操作后 s 变成 “6345”。
请你返回在 s 上执行上述操作任意次后可以得到的 字典序最小 的字符串。
1 | 输入:s = "5525", a = 9, b = 2 |
方法一:这题比较难想,作为竞赛第二题一直卡住了,实际没有3题简单。BFS,暴力去做就行,但是比赛时写得有一点问题TLE了。
但是还是想到一些规律,如果b是偶数时,累加的位数只有奇数位;如果b是奇数,那么通过旋转每一位都可能累加,但是累加和旋转的顺序其实是不想关的,可以单独地操作。时间1700ms。
1 | def findLexSmallestString(self, s: str, a: int, b: int) -> str: |
方法二:dfs,也是可以,向BFS那样去重就行了。速度比方法一慢了一点2400ms。
1 | def findLexSmallestString(self, s: str, a: int, b: int) -> str: |
1627. Graph Connectivity With Threshold
给你1~n个点,如果两个点有一个约数大于Threshold,那么表明两点之间有边。问给你一些点对,判断这两个点是否相连。
方法一:简单的并查集问题,比赛可惜没时间做。这题的问题在于如何建图。枚举两个点一定会超时,所以从逆向考虑,枚举所有的约数,将这些点相连。
1 | def areConnected(self, n: int, threshold: int, queries: List[List[int]]) -> List[bool]: |
947. Most Stones Removed with Same Row or Column
最多可以移除多少块石头,每次移除时,下一块找到同行或者同列的进行移除。
1 | Input: stones = [[0,0],[0,1],[1,0],[1,2],[2,1],[2,2]] |
方法一:Union-Find,并查集方法。从一块石头开始,每次找同行或者同列的作为下一次石头。这些石头连通形成一个集合。
1 | def removeStones(self, stones: List[List[int]]) -> int: |
1654. Minimum Jumps to Reach Home
最少需要多少步能到达x,从坐标0点开始,每次可以向右跳a
步,或者向左跳b
步,但是不能连着两次向左跳。除此之外有些点不可达。
1 | Input: forbidden = [14,4,18,1,15], a = 3, b = 15, x = 9 |
方法一:BFS,还是很直观可以想到的,比赛时忽略了forbidden
错了一次。需要注意的点如果当前点大于最大范围后,就跳不回来了,这个终止条件。这里用一个bool
控制了是否可以回跳。
1 | def minimumJumps(self, forbidden: List[int], a: int, b: int, x: int) -> int: |
1203. Sort Items by Groups Respecting Dependencies
公司共有 n 个项目和 m 个小组,每个项目要不无人接手,要不就由 m 个小组之一负责。group[i] 表示第 i 个项目所属的小组,如果这个项目目前无人接手,那么 group[i] 就等于 -1。(项目和小组都是从零开始编号的)小组可能存在没有接手任何项目的情况。请你帮忙按要求安排这些项目的进度,并返回排序后的项目列表:
- 同一小组的项目,排序后在列表中彼此相邻。
- 项目之间存在一定的依赖关系,我们用一个列表 beforeItems 来表示,其中 beforeItems[i] 表示在进行第 i 个项目前(位于第 i 个项目左侧)应该完成的所有项目。
如果存在多个解决方案,只需要返回其中任意一个即可。如果没有合适的解决方案,就请返回一个 空列表 。
1 | Input: n = 8, m = 2, group = [-1,-1,1,0,0,1,0,-1], beforeItems = [[],[6],[5],[6],[3,6],[],[],[]] |
方法一:拓扑排序,hard题,首先以项目为拓扑排序很容易想到,这里还需要对组间进行拓扑排序。然后再进行一次组内排序。最后合并结果。需要注意的点比较多。
1 | def sortItems(self, n: int, m: int, group: List[int], beforeItems: List[List[int]]) -> List[int]: |
803. Bricks Falling When Hit
有一个 m x n 的二元网格,其中 1 表示砖块,0 表示空白。砖块 稳定(不会掉落)的前提是:
一块砖直接连接到网格的顶部,或者至少有一块相邻(4 个方向之一)砖块 稳定 不会掉落时给你一个数组 hits ,这是需要依次消除砖块的位置。
每当消除 hits[i] = (rowi, coli) 位置上的砖块时,对应位置的砖块(若存在)会消失,然后其他的砖块可能因为这一消除操作而掉落。一旦砖块掉落,它会立即从网格中消失(即,它不会落在其他稳定的砖块上)。返回一个数组 result ,其中 result[i] 表示第 i 次消除操作对应掉落的砖块数目。注意,消除可能指向是没有砖块的空白位置,如果发生这种情况,则没有砖块掉落。
1 | Input: grid = [[1,0,0,0],[1,1,1,0]], hits = [[1,0]] |
方法一:逆向思维+dfs。逆向思维还是挺难想的。总共流程可以分为4步。
- 按照hits顺序,将砖块敲掉,如果没有,需要敲成-1,为了将其周围的砖块分开。
- 对首行每个单元格进行dfs,统计每个点能够挂住的砖块个数,然后将这些砖块标记为2.
- 然后按照hits倒序,敲掉的砖块补回来,如果补回来的块能和2的砖块相连,则表示,这些砖块是掉落的。
- 统计每次敲击的结果。
1 | def hitBricks(self, grid: List[List[int]], hits: List[List[int]]) -> List[int]: |
1766. Tree of Coprimes
找到每个节点的最小祖先,并且祖先节点和其节点值互质。
1 | Input: nums = [2,3,3,2], edges = [[0,1],[1,2],[1,3]] |
方法一:dfs。比赛时没有时间看题。范围比较小节点值在1~50之间,所以可以将每个值互质的节点和深度放到一个列表中。
1 | def getCoprimes(self, nums: List[int], edges: List[List[int]]) -> List[int]: |
1368. Minimum Cost to Make at Least One Valid Path in a Grid
修改箭头方向最少次数能够到达右下角的点。
1 | Input: grid = [[1,1,1,1],[2,2,2,2],[1,1,1,1],[2,2,2,2]] |
方法一:狄克斯特拉,有点慢,4600ms。
1 | def minCost(self, grid: List[List[int]]) -> int: |
1 | def minCost(self, grid: List[List[int]]) -> int: |
1786. Number of Restricted Paths From First to Last Node
从头节点到尾节点的限制路径是多少个,限制路径指每次节点到尾节点的最短路径要递减。
1 | Input: n = 5, edges = [[1,2,3],[1,3,3],[2,3,1],[1,4,2],[5,2,2],[3,5,1],[5,4,10]] |
方法一:比赛时差一点做出来,最后一步没有想明白。首先肯定是需要求每个节点到尾节点的最短路径,利用狄克斯特拉算法简单求出。
然后使用记忆化,本质上是个动态规划来累加路径和。dfs方法没有想出来。
1 | def countRestrictedPaths(self, n: int, edges: List[List[int]]) -> int: |
331. Verify Preorder Serialization of a Binary Tree
判断一个字符串序列是否是前序遍历。
1 | Input: "9,3,4,#,#,1,#,#,2,#,6,#,#" |
方法一:栈。一开始想递归,发现递归并不好找条件return False。这个方法有点像不断地剪枝,当一个叶子节点满足子节点都是#
的时候,它也变成#
。
1 | def isValidSerialization(self, preorder: str) -> bool: |
1 | func isValidSerialization(preorder string) bool { |
方法二:槽。代码简单,思想不简单。把树中的节点想象成槽,当出现一个非空节点,就需要左右孩子节点2个槽。如果是一个#
表示消耗掉一个槽。全程槽的数不能为0,最后才能是0
1 | def isValidSerialization(self, preorder: str) -> bool: |
1 | func isValidSerialization(preorder string) bool { |
1857. Largest Color Value in a Directed Graph
有向图中包含颜色最多的路径,颜色的最大值是多少。
1 | Input: colors = "abaca", edges = [[0,1],[0,2],[2,3],[3,4]] |
方法一:拓扑排序+DP,比较难的一道题。两个都没有想到,首先为什么要拓扑排序,因为对于一个路径来说,从入度为0的点开始,才会让节点越多,颜色才可能最大。dp[i][j]
表示以i为终点的路径,j颜色最大有多少个
1 | def largestPathValue(self, colors: str, edges: List[List[int]]) -> int: |
1871. Jump Game VII
跳跃游戏,0位首的数组,每次跳跃步伐在mi,ma之间,且只能跳到0的位置,问是否能够跳到最后一个位置。
1 | Input: s = "011010", minJump = 2, maxJump = 3 |
方法一:比赛期间用了堆+无数次剪枝和TLE勉强通过。
1 | def canReach(self, s: str, minJump: int, maxJump: int) -> bool: |
方法二:bfs。没想到能用bfs。
1 | def canReach(self, s: str, minJump: int, maxJump: int) -> bool: |
1905. Count Sub Islands
找到图2中子岛屿的个数,如果图2的岛屿都在图一的同一个岛中,那么久叫作子岛屿。
1 | Input: grid1 = [[1,1,1,0,0],[0,1,1,1,1],[0,0,0,0,0],[1,0,0,0,0],[1,1,0,1,1]], grid2 = [[1,1,1,0,0],[0,0,1,1,1],[0,1,0,0,0],[1,0,1,1,0],[0,1,0,1,0]] |
方法一:这题竞赛居然没做出来,想着先把图一的所有岛屿都求出来然后再遍历图2算。其实可以同时进行的。
1 | def countSubIslands(self, g1: List[List[int]], g2: List[List[int]]) -> int: |
2092. Find All People With Secret
找到知道秘密的人,一个人可以同时开多个会,而且同一时间点的事件同时发生。
方法一:比赛的时候想并查集,但是没有完全想出来。这里是答案的bfs方法。
1 | def findAllPeople(self, n: int, meetings: List[List[int]], firstPerson: int) -> List[int]: |
2097. Valid Arrangement of Pairs
找到一个有效的首位相连的路径,数组元素表示一条有向边。
1 | Input: pairs = [[5,1],[4,5],[11,9],[9,4]] |
方法一:和332题一样,是求欧拉路径的问题,区别在于要先找到开始的点。比赛的时候没有想出来
1 | def validArrangement(self, pairs: List[List[int]]) -> List[List[int]]: |
2360. Longest Cycle in a Graph
图中的最长环。每个节点最多只有一个出度。
1 | Input: edges = [3,3,4,2,3] |
方法一:常规方法,拓扑排序+bfs。逐渐删除入度为0的点,最后剩下所有的环。找出最大的环。
1 | def longestCycle(self, edges: List[int]) -> int: |
方法二:时钟从0点找没有走过的点,如果有环的话,时差就是环长。
1 | def longestCycle(self, edges: List[int]) -> int: |
827. Making A Large Island
修改一个0变成1,最大的岛屿面积是多少?
1 | 输入: grid = [[1, 0], [0, 1]] |
方法一:首先用独立的编号标记每个岛屿,并记录岛屿面积。然后遍历0的地方,将上下左右的岛屿相连,计算面积。
1 | def largestIsland(self, g: List[List[int]]) -> int: |
934. Shortest Bridge
最短的桥,图中有两个岛屿,求两个岛屿间的最短距离。
方法一:首先用dfs将岛屿编号,并保存第一个岛屿的边。然后遍历其边使用bfs去找第二个岛屿。
1 | def shortestBridge(self, grid: List[List[int]]) -> int: |
2477. Minimum Fuel Cost to Report to the Capital
给你一棵 n
个节点的树(一个无向、连通、无环图),每个节点表示一个城市,编号从 0
到 n - 1
,且恰好有 n - 1
条路。0
是首都。给你一个二维整数数组 roads
,其中 roads[i] = [ai, bi]
,表示城市 ai
和 bi
之间有一条 双向路 。
每个城市里有一个代表,他们都要去首都参加一个会议。
每座城市里有一辆车。给你一个整数 seats
表示每辆车里面座位的数目。
城市里的代表可以选择乘坐所在城市的车,或者乘坐其他城市的车。相邻城市之间一辆车的油耗是一升汽油。
请你返回到达首都最少需要多少升汽油。
1 | 输入:roads = [[0,1],[0,2],[0,3]], seats = 5 |
方法一:这题比赛时竟然没做出来,陷入了思维误区,老想用bfs。实际上需要dfs。首先需要将问题转化。
1 | def minimumFuelCost(self, roads: List[List[int]], s: int) -> int: |