前端算法之图-找到小镇法官和课程表

多接点的拓扑关系

在算法中,图是一种数据结构,它由节点(顶点)和连接节点的边组成。图可以用来表示各种实际问题中的关系和连接方式。

图由两个主要部分组成:节点和边。节点表示实体或对象,而边表示节点之间的连接关系。这些连接关系可以是有向的(箭头指示方向)或无向的(没有箭头指示方向)。图可以是有权重的(边具有相关的数值或权重)或无权重的。

图可以有多种形式,包括有向图、无向图、加权图等。在算法中,图被广泛用于解决各种问题,如路径搜索、最短路径、网络流、最小生成树等。常用的图算法包括深度优先搜索(DFS)、广度优先搜索(BFS)、Dijkstra算法、Bellman-Ford算法、Prim算法、Kruskal算法等。

通过使用图这种数据结构和相关的图算法,我们可以更好地理解和处理复杂的关系和网络结构,从而解决各种实际问题。

更多详细内容,请微信搜索“前端爱好者戳我 查看

图的分类

在图论中,图可以根据不同的特性和属性进行分类。以下是常见的图的分类方式:

  1. 有向图(Directed Graph)和无向图(Undirected Graph):

    • 有向图中的边是有方向的,即从一个节点指向另一个节点。
    • 无向图中的边没有方向,表示节点之间是对等关系。
  2. 加权图(Weighted Graph)和非加权图(Unweighted Graph):

    • 加权图中的边具有相关联的权重或成本。
    • 非加权图中的边没有权重,只表示连接关系。
  3. 稠密图(Dense Graph)和稀疏图(Sparse Graph):

    • 稠密图指的是边的数量接近于节点数量的图。
    • 稀疏图指的是边的数量远小于节点数量的图。
  4. 连通图(Connected Graph)和非连通图(Disconnected Graph):

    • 连通图中任意两个节点之间都存在路径。
    • 非连通图中存在孤立的部分,某些节点之间没有路径相连。
  5. 无环图(Acyclic Graph)和有环图(Cyclic Graph):

    • 无环图中不存在形成环路的路径。
    • 有环图中存在形成环路的路径。
  6. 完全图(Complete Graph):

    • 完全图是指每一对不同的节点之间都有一条边相连的图。

这些是图的常见分类方式,而实际应用中的图可能同时符合多种分类标准。

例子:找到小镇的法官

leetcode地址 : https://leetcode.cn/problems/find-the-town-judge/

小镇里有 n 个人,按从 1 到 n 的顺序编号。传言称,这些人中有一个暗地里是小镇法官。

如果小镇法官真的存在,那么:

  1. 小镇法官不会信任任何人。
  2. 每个人(除了小镇法官)都信任这位小镇法官。
  3. 只有一个人同时满足属性 1 和属性 2 。

给你一个数组 trust ,其中 trust[i] = [ai, bi] 表示编号为 ai 的人信任编号为 bi 的人。

如果小镇法官存在并且可以确定他的身份,请返回该法官的编号;否则,返回 -1 。

示例 1:

输入:n = 2, trust = [[1,2]]
输出:2

示例 2

输入:n = 3, trust = [[1,3],[2,3]]
输出:3

示例 3:

输入:n = 3, trust = [[1,3],[2,3],[3,1]]
输出:-1

提示:

1 <= n <= 1000
0 <= trust.length <= 10^4
trust[i].length == 2
trust 中的所有trust[i] = [ai, bi] 互不相同
ai != bi
1 <= ai, bi <= n

实现代码

/**
 * @param {number} n
 * @param {number[][]} trust
 * @return {number}
 */
var findJudge = function(n, trust) {
    // 在多个拓扑节点关系中,是有方向的
    // 每个节点包括入度和处度

    // 用数组来代表一个图,长度是n+1,每个节点中都会有入度和出度
    let graph = Array.from({length:n+1},()=>({
        inDegree: 0,
        outDegree: 0,
    }))

    // 找到法官,入度为 n-1, 出度为 0 

    // 遍历数组,
    trust.forEach(([a,b]) => {
        graph[a].outDegree ++ 
        graph[b].inDegree ++ 
    })

    return graph.findIndex(({inDegree,outDegree},index) => {
        return index !== 0 &&  outDegree === 0 && inDegree === n -1
    })

};

例子:课程表

leetcode地址:https://leetcode.cn/problems/course-schedule/

你这个学期必须选修 numCourses 门课程,记为 0 到 numCourses - 1 。

在选修某些课程之前需要一些先修课程。 先修课程按数组 prerequisites 给出,其中 prerequisites[i] = [ai, bi] ,表示如果要学习课程 ai 则 必须 先学习课程 bi 。

例如,先修课程对 [0, 1] 表示:想要学习课程 0 ,你需要先完成课程 1 。

请你判断是否可能完成所有课程的学习?如果可以,返回 true ;否则,返回 false 。

示例 1:

输入:numCourses = 2, prerequisites = [[1,0]]
输出:true
解释:总共有 2 门课程。学习课程 1 之前,你需要完成课程 0 。这是可能的。

示例 2:

输入:numCourses = 2, prerequisites = [[1,0],[0,1]]
输出:false
解释:总共有 2 门课程。学习课程 1 之前,你需要先完成​课程 0 ;并且学习课程 0 之前,你还应先完成课程 1 。这是不可能的。

提示:

1 <= numCourses <= 2000
0 <= prerequisites.length <= 5000
prerequisites[i].length == 2
0 <= ai, bi < numCourses
prerequisites[i] 中的所有课程对 互不相同

实现代码

let canFinish = function(numCourses, prerequisites) {
    // 如果没有先决条件,即所有的课程均没有依赖关系
    // 直接返回 true
    if (prerequisites.length === 0) {
        return true
    }

    // 维护入度表
    let inDegree = new Array(numCourses).fill(0)
    // 维护临接表
    let adj = new Map()
    
    for (let e of prerequisites) {
        inDegree[e[0]]++
        if(!adj.has(e[1])) adj.set(e[1], [])
        let vEdge = adj.get(e[1])
        vEdge.push(e[0])
    }

    let queue = []
    // 首先加入入度为 0 的结点
    for (let i = 0; i < numCourses; i++) {
        if (inDegree[i] === 0) {
            queue.push(i)
        }
    }

    while (queue.length > 0) {
        // 从队首移除
        var v = queue.shift() 
        // 出队一门课程
        numCourses--
        if(!adj.has(v)) continue
        // 遍历当前出队结点的所有临接结点
        for(let w of adj.get(v)) {
            inDegree[w]--
            if (inDegree[w] === 0) {
                queue.push(w)
            }
        }
    }
    return numCourses === 0
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

前端布道人

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值