leetcode-拓扑排序算法

article/2025/10/1 7:29:30

拓扑排序原理

拓扑排序算法分析(通俗易懂)_hongjie_lin-CSDN博客_拓扑排序算法

207 课程表

bfs和dfs都可以。先来看一下bfs。
思路是:入度法,入度为0的时候,表示这门课程没有先修课程了,可以学习这门课程了。

1.存储每个课程的入度值
2.存储每个课程的入度节点

为什么?

#存储节点的先修课程,key是先修课程,value是这个先修课程的下一个节点。

edges=collections.defaultdict(list)。

答:因为是遍历到当前入度为0的课程时,他对应的下一个课程的入度可以减去1.如下图所示。

注意:会有节点没出现在列表里,但是也是存在的。

class Solution:from collections import dequedef canFinish(self, numCourses: int, prerequisites: List[List[int]]) -> bool:#入度法:当前节点没有先修课程。最后每个节点的先修可能为0,则说明可以完成。#存储节点的先修课程,key是先修课程,value是这个先修课程的下一个节点。edges=collections.defaultdict(list)#存储当前每个节点的入度indegree=[0]*numCourses#遍历赋予初始值for cur,pre in prerequisites:edges[pre].append(cur)indegree[cur]+=1#找到当前入度为0的节点# queen=collections.deque([u for u in range(numCourses) if indegree[u] == 0])queen=deque()# for u in indegreefor u in range(numCourses):if indegree[u]==0:queen.append(u)visited = 0while queen:visited +=1pre=queen.popleft()for cur in edges[pre]:indegree[cur]-=1if indegree[cur]==0:queen.append(cur)return visited==numCourses

851 喧闹和富有

 题解:

力扣https://leetcode-cn.com/problems/loud-and-rich/solution/tong-ge-lai-shua-ti-la-yi-ti-liang-jie-t-gses/

class Solution(object):from collections import dequedef loudAndRich(self, richer, quiet):""":type richer: List[List[int]]:type quiet: List[int]:rtype: List[int]"""#存储节点,key是富有的人,value是这个富人指向的下一个穷人edges=collections.defaultdict(list)#入度表indegree=[0]*len(quiet)#赋予初始值for info in richer:edges[info[0]].append(info[1])indegree[info[1]]+=1#ans赋予初始值ans=[i for i in range(len(quiet))]#找到入度为0的节点queen=deque()for i in range(len(quiet)):if indegree[i]==0:queen.append(i)while queen:pre=queen.popleft()for cur in edges[pre]:if quiet[ans[pre]]<quiet[ans[cur]]:ans[cur]=ans[pre]indegree[cur]-=1if indegree[cur]==0:queen.append(cur)return ansclass Solution {public int[] loudAndRich(int[][] richer, int[] quiet) {// 拓扑排序:取入度为0的先入队,减少它下游节点的入度,如果为0了也入队,直到队列中没有元素为止int n = quiet.length;// 先整理入度表和邻接表int[] inDegree = new int[n];List<Integer>[] g = new List[n];for (int i = 0; i < n; i++) {g[i] = new ArrayList<>();}for (int[] r : richer) {inDegree[r[1]]++;g[r[0]].add(r[1]);}// 初始化ans各位为自己// 题目说的是:在所有拥有的钱肯定不少于 person x 的人中,person y 是最安静的人// 注意这里的不少于,说明可以是自己int[] ans = new int[n];for (int i = 0; i < n; i++) {ans[i] = i;}// 拓扑排序开始Queue<Integer> queue = new LinkedList<>();for (int i = 0; i < n; i++) {if (inDegree[i] == 0) {queue.offer(i);}}while (!queue.isEmpty()) {int p = queue.poll();// q是p的下游,也就是p比q有钱for (int q : g[p]) {// 如果p的安静值比q小,更新p的安静值对应的那个人// 注意这里p的安静值,并不是原始的quiet数组中的quiet[p]// 而是已经更新后的安静值,所以,应该取quiet[ans[p]]// 这里没有改变原来数组的内容,而是通过ans[p]间接引用的,细细品一下// 想像一下上图中的3的安静值被更新成了5的1if (quiet[ans[p]] < quiet[ans[q]]) {ans[q] = ans[p];}if (--inDegree[q] == 0) {queue.offer(q);}}}return ans;}
}作者:tong-zhu
链接:https://leetcode-cn.com/problems/loud-and-rich/solution/tong-ge-lai-shua-ti-la-yi-ti-liang-jie-t-gses/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

310 最小高度树

力扣

力扣

暴力超时

class Solution:def findMinHeightTrees(self, n: int, edges: List[List[int]]) -> List[int]:if not edges:return [0]import collectionsgraph = collections.defaultdict(list)for i in range(len(edges)):graph[edges[i][0]].append(edges[i][1])graph[edges[i][1]].append(edges[i][0])def bfs(root):deq = collections.deque()d = 0deq.append((root, d))visited = set()visited.add(root)while deq:node, d = deq.popleft()next_nodes = graph[node]for next_node in next_nodes:if next_node not in visited:deq.append((next_node, d + 1))visited.add(next_node)# 都遍历完了之后,记录dreturn dres = float("inf")res_list = []res_list_root = []for root in graph.keys():cur_height = bfs(root)res_list.append(cur_height)res_list_root.append(root)res = min(res, cur_height)ans = []for i in range(len(res_list)):if res_list[i] == res:ans.append(res_list_root[i])return ans

利用入度

 

class Solution:def findMinHeightTrees(self, n: int, edges: List[List[int]]) -> List[int]:res = []if n==1:res.append(0)return resdegree = [0]*n #记录每个点的度map = defaultdict(list) #存储邻居节点# 初始化每个节点的度和邻居for edge in edges:degree[edge[0]]+=1degree[edge[1]]+=1map[edge[0]].append(edge[1])map[edge[1]].append(edge[0])# 记录度为1的叶子节点queue = deque()for i in range(n):if degree[i]==1:queue.append(i)#每次遍历叶子节点,每一轮将叶子节点从树上删除后将新的叶子节点入队进行下一轮遍历while len(queue)>0:res = []size = len(queue)# 遍历叶子节点for i in range(size):cur = queue.pop()res.append(cur)neighbors = map[cur]for neighbor in neighbors:# 将叶子节点的邻居节点的度减一,若是新的叶子节点,则入队degree[neighbor]-=1if degree[neighbor]==1:queue.appendleft(neighbor)# 返回最后一轮的叶子节点,就是最小高度树的根节点return res

269 火星词典

class Solution:def alienOrder(self, words: List[str]) -> str:# 将词典中字符串的字符两两对比,只有第一个不同的字符才是正确的排序,如ert和wrf,只能推断出e的优先级高于w,剩余字符的优先级不能推断。将字符串的优先级构建为图,然后进行拓扑排序。如果图中无环,则将拓扑排序输出,否则顺序是非法的。# 每个字母代表图的一个顶点; 邻接表表示有向图. graph = {}indegree = {}# 初始化graph 和 入度表. 注意这个初始化是要对每个出现的字符都进行初始化for word in words:for char in word:if char not in graph:graph[char] = []indegree[char] = 0# 两两单词中的每个字母进行比较,确定图的方向. for i in range(len(words) - 1):j = 0while j < len(words[i]) and j < len(words[i + 1]):if words[i][j] != words[i + 1][j]:# 当前的两个字母按这门新语言的字母顺序进行了排序, 所以优先级高 -> 优先级低。 加入graph。graph[words[i][j]].append(words[i + 1][j])# 为什么这里需要break?当前的两个字母不同,字母顺序也不同;所以单词在字典中的排序顺序只和这两个字母有关,而和后面的无关。因此我们需要看下一个单词对比而不是当前单词的字母对比。breakj += 1# 填充入度表for nexts in graph.values():for next_char in nexts:indegree[next_char] += 1# 把入度为0的点装进queue里queue = collections.deque()for key in indegree.keys():if indegree[key] == 0:queue.append(key)res = []while queue:pre = queue.popleft()res.append(pre)# 删除了pre节点,那么这个pre所对应的next,全部的入度都要-1. 然后顺带检查next的入度是否为0, 如果为0的话那么要把next加入queue. for next in graph[pre]:indegree[next] -= 1if indegree[next] == 0:queue.append(next)is_bad_dag = False# 判断有向图是否有环: 出队元素个数不等于图顶点个数,说明有环if len(res) != len(graph):is_bad_dag = True# abc 排在 ab前面,也属于非法输入。这种情况,a-> a b->b 不可能,c -> "" 也不可能for i in range(len(words) - 1):if len(words[i]) > len(words[i + 1]) and words[i][:len(words[i + 1])] == words[i + 1]:is_bad_dag = Truebreakreturn "" if is_bad_dag else "".join(res)
class Solution:def alienOrder(self, words: List[str]) -> str:import collections# 存储节点的先修课程,key是先修课程,value是这个先修课程的下一个节点。graph = collections.defaultdict(list)# 存储当前每个节点的入度indegree = collections.defaultdict(int)# 初始化入度表. 因为入度为0的节点在下面遍历不到# 初始化graph,为了后面判断图是否有环for word in words:for char in word:if char not in graph:graph[char] = []indegree[char] = 0# 先建图,当前char,当前char的下一个char,#  两两单词中的每个字母进行比较,确定图的方向.for i in range(len(words)-1):j = 0while j < len(words[i]) and j < len(words[i + 1]):if words[i][j] != words[i + 1][j]:# 当前的两个字母按这门新语言的字母顺序进行了排序, 所以优先级高 -> 优先级低。 加入graph。graph[words[i][j]].append(words[i + 1][j])# 入度加一indegree[words[i + 1][j]] += 1# 为什么这里需要break?当前的两个字母不同,字母顺序也不同;所以单词在字典中的排序顺序只和这两个字母有关,而和后面的无关。因此我们需要看下一个单词对比而不是当前单词的字母对比。breakj += 1# 把入度为0的点装进queue里queue = collections.deque()for key in indegree.keys():if indegree[key] == 0:queue.append(key)res = []while queue:pre = queue.popleft()res.append(pre)# 删除了pre节点,那么这个pre所对应的next,全部的入度都要-1. 然后顺带检查next的入度是否为0, 如果为0的话那么要把next加入queue.for next in graph[pre]:indegree[next] -= 1if indegree[next] == 0:queue.append(next)# abc 排在 ab前面,也属于非法输入。这种情况,a-> a b->b 不可能,c -> "" 也不可能is_bad_dag = False# 判断有向图是否有环: 出队元素个数不等于图顶点个数,说明有环if len(res) != len(graph):is_bad_dag = Truefor i in range(len(words) - 1):if len(words[i]) > len(words[i + 1]) and words[i][:len(words[i + 1])] == words[i + 1]:is_bad_dag = Truebreakif is_bad_dag:return ""return "".join(res)

6163. 给定条件下构造矩阵

 

class Solution(object):def buildMatrix(self, k, rowConditions, colConditions):""":type k: int:type rowConditions: List[List[int]]:type colConditions: List[List[int]]:rtype: List[List[int]]"""import collectionsgraph_row = collections.defaultdict(list)indegree_row = [0] * k# 遍历赋予初始值for abovei, belowi in rowConditions:graph_row[abovei-1].append(belowi-1)indegree_row[belowi-1] += 1graph_col = collections.defaultdict(list)indegree_col = [0] * k# 遍历赋予初始值for lefti, righti in colConditions:graph_col[lefti-1].append(righti-1)indegree_col[righti-1] += 1print(indegree_row,indegree_col)from collections import dequequeen1 = deque()for i in range(len(indegree_row)):if indegree_row[i] == 0:queen1.append(i)row = []while queen1:pre = queen1.popleft()row.append(pre)for cur in graph_row[pre]:indegree_row[cur] -= 1if indegree_row[cur] == 0:queen1.append(cur)queen2 = deque()for i in range(len(indegree_col)):if indegree_col[i] == 0:queen2.append(i)col = []while queen2:pre = queen2.popleft()col.append(pre)for cur in graph_col[pre]:indegree_col[cur] -= 1if indegree_col[cur] == 0:queen2.append(cur)print(row)print(col)# 不满足条件if len(row) != k or len(col) != k:return []# 确定每个数值放置的位置进行赋值row_ind = {row[i]: i for i in range(k)}col_ind = {col[i]: i for i in range(k)}res = [[0] * k for _ in range(k)]for i in range(k):res[row_ind[i]][col_ind[i]] = i+1return res


http://chatgpt.dhexx.cn/article/LQI14Znp.shtml

相关文章

2022.3.24 图论——拓扑排序算法

文章目录 一、拓扑排序简介二、例题1.题目2.分析3.代码 一、拓扑排序简介 1.Topological Sorting&#xff0c;指的是一个DAG(Directed Acyclic Graph)即有向图所有顶点满足一定条件的线性序列。 拓扑序列应满足两个条件&#xff1a; 每个点都只出现一次 如果存在一条从A指向B…

数据结构——图——拓扑排序算法

数据结构——图——拓扑排序算法 对AOV网进行拓扑排序的基本思路是:从AOV网中选择一个入度为0的顶点输出&#xff0c;然后删去此顶点&#xff0c;并删除以此顶点为尾的弧&#xff0c;继续重复此步骤&#xff0c;直到输出全部顶点或者AOV 网中不存在入度为0的顶点为止。 首先我…

拓扑排序详解(包含算法原理图解、算法实现过程详解、算法例题变式全面讲解等)

前置知识 有向无环图 在图论中&#xff0c;如果一个有向图无法从某个顶点出发经过若干条边回到该点&#xff0c;则这个图是一个有向无环图&#xff08;DAG图&#xff09;。 如图所示。 入度 对于一个有向图&#xff0c;若x点指向y点&#xff0c;则称x点为y点的入度。 出度…

拓扑排序算法分析(通俗易懂)

拓扑排序&#xff08;其实是一种依赖关系&#xff09;&#xff1a;对于有向且无环的图来说&#xff0c;当前这个节点的依赖来其之前已经完成了。 下面附上一个图让大伙更好的理解&#xff1a; 比如这个图&#xff1a;B需要依赖A才能完成&#xff0c;A需要依赖C和D才能完成&…

拓扑排序算法详讲

经过一天的专研,终于明白了拓扑排序算法,写篇博客记录一下心得. 一.拓扑排序介绍 在一个表示工程的有向图中,用顶点表示活动,用弧表示活动之间的优先关系,这样的有向图为顶点表示活动的网,我们称为AOV网. 设G(V,E)是一个具有n个顶点的有向图,v中的顶点序列v1,v2…,vn,满足若从…

C++ 拓扑排序算法

拓扑排序 有向无环图 如果一个有向图的任意顶点都无法通过一些有向边回到自身&#xff0c;那么称这个有向图为有向无环图。 拓扑排序 拓扑排序是将有向无环图G的所有顶点排成一个线性序列&#xff0c;使得对图G中的任意两个顶点u、v&#xff0c;如果存在边u->v&#xff0c;那…

拓扑排序

拓扑排序 一、拓扑排序的定义&#xff1a; 先引用一段百度百科上对于拓扑排序的定义&#xff1a; 对一个有向无环图 ( Directed Acyclic Graph 简称 DAG ) G 进行拓扑排序&#xff0c;是将 G 中所有顶点排成一个线性序列&#xff0c;使得图中任意一对顶点 u 和 v &#xff0c…

拓扑排序算法

拓扑排序介绍 拓扑排序(Topological Order)是指&#xff0c;将一个有向无环图(Directed Acyclic Graph简称DAG)进行排序进而得到一个有序的线性序列。 这样说&#xff0c;可能理解起来比较抽象。下面通过简单的例子进行说明&#xff01; 例如&#xff0c;一个项目包括A、B、C…

经典算法之拓扑排序

定义&#xff1a; 把AOV网&#xff08;用定点表示活动&#xff0c;用弧表示活动间优先关系的有向图&#xff09;络中各个顶点按照它们互相之间的优先关系排列成一个线性序列的过程叫做拓扑排序。 方法&#xff1a; 在有向图中选一个没有前驱的顶点并且输出从图中删除该顶点和…

拓扑排序(topological sorting)介绍及Python实现

目录 1. 拓扑排序 2. 拓扑排序存在的前提 3. 拓扑排序的唯一性问题 4. 拓扑排序算法原理 4.1 广度优先遍历 4.2 深度优先遍历 5. 代码实现 5.1 Graph类的实现 5.2 广度优先搜索 5.3 深度优先搜索简易版&#xff08;无loop检测&#xff09; 5.4 深度优先搜索完整版 …

【算法】拓扑排序

今天学习拓扑排序。如果一个有向图的任意顶点都无法通过一些有向边回到自身&#xff0c;那么称这个有向图为有向无环图&#xff08;Directed Acyclic Graph&#xff0c;DAG&#xff09;。拓扑排序就是将有向无环图的所有顶点排序&#xff0c;使得图中任意两个点 u、v&#xff0…

拓扑排序及算法实现

一、拓扑排序概念 对一个有向无环图(Directed Acyclic Graph简称DAG)G进行拓扑排序&#xff0c;是将G中所有顶点排成一个线性序列&#xff0c;使得图中任意一对顶点u和v&#xff0c;若边<u,v>∈E(G)&#xff0c;则u在线性序列中出现在v之前。通常&#xff0c;这样的线性…

利用迭代法求平方根——迭代法开平方运算

一、题目 用迭代法求&#xff1a; 的值。要求精度为0.00001&#xff0c;即 二、迭代公式 求平方根的迭代公式为&#xff1a; 当满足 时&#xff0c;这时的即是求得的根。如果 得到是精确值。如果不为0&#xff0c;则是近似值。 三、C代码 先给出开平方运算的函数。再给出主…

【算法】牛顿迭代法求平方根的原理和误差分析

前言 在《算法(第四版)》中的P23页&#xff0c;给出了经典的利用牛顿迭代法求平方根的算法&#xff0c;牛顿迭代法在数值计算中应用十分广泛&#xff0c;但是在看书中的代码时&#xff0c;我最困惑的是其中对收敛条件的判断&#xff0c;经过查阅资料和论坛&#xff0c;找到…

使用迭代法来求a的平方根

今天朋友问我一道使用迭代法求a的平方根的题&#xff0c;感觉受益匪浅&#xff0c;与诸君相分享 首先我们来看一下题目 我们也无需了解迭代法是什么原理&#xff0c;根据这个题目可以分析得到&#xff0c;需要使用while循环&#xff0c;下面是我的代码实践 #define _CRT_SEC…

利用牛顿迭代法求平方根

求n的平方根&#xff0c;先假设一猜测值X0 1&#xff0c;然后根据以下公式求出X1&#xff0c;再将X1代入公式右边&#xff0c;继续求出X2…通过有效次迭代后即可求出n的平方根&#xff0c;Xk1 先让我们来验证下这个巧妙的方法准确性&#xff0c;来算下2的平方根 (Computed b…

牛顿迭代法求解平方根

一个实例迭代简介牛顿迭代法 牛顿迭代法简介简单推导泰勒公式推导延伸与应用 一个实例 //java实现的sqrt类和方法 public class sqrt {public static double sqrt(double n){if (n<0) return Double.NaN;double err 1e-15;double t n;while (Math.abs(t - n/t) > err…

牛顿迭代法求一个数的平方根(python)

# !/usr/bin/env python # -*- coding: utf-8 -*- """ Author: P♂boy License: (C) Copyright 2013-2017, Node Supply Chain Manager Corporation Limited. Contact: 17647361832163.com Software: Pycharm File: sqrt.py.py Time: 2018/11/19 16:22 Desc:牛顿…

c++迭代法求平方根

用迭代法求x &#xff0c;要求前后两次求出的x的差的绝对值小于10-5&#xff0c;求平方根的迭代公式为&#xff1a; 输入 一个数a。 输出 的值。 样例输入 2.0样例输出 The square root of 2.00 is 1.41421 这道题我无语了...&#xff1a; #include<bits/stdc.h> …

牛顿迭代法求平方根原理

牛顿迭代法可以求解n次方的根&#xff0c;但这里只讨论用它来求平方根。 牛顿迭代法求平方根过程 Java代码实现 /*** 求一个数的平方根* param number* return*/public static double squareRoot(double number){if(number < 0){ //小于0的数无法开平方return Double.NaN;}e…