图包括顶点和边,图分为有向图和无向图。边还可以根据顶点之间的关系设置不同的权重,默认权重相同皆为1。对有向图而言,图中有效信息除了从顶点“指出去”的信息,还包括从别的顶点“指进来”的信息。这里的“指出去”和“指进来”的数量可以用出度和入度来表示。关于图的解释可参考业余码农
图的存储方式:
1.邻接矩阵,通过顶点的二维矩阵来存储两个顶点之间是否连接。
2.邻接表可以只存储相连顶点关系。

1.克隆图(medium)

题目:给你无向 连通 图中一个节点的引用,请你返回该图的 深拷贝(克隆)。
图中的每个节点都包含它的值 val(int) 和其邻居的列表(list[Node])。
class Node {
public int val;
public List neighbors;
}

思路:采用深度优先搜索的方法,为了避免多次遍历同一个节点,采用哈希表来存储已经访问过的节点。哈希表中的key 是原始图中的节点,value 是克隆图中的对应节点。
从给定的节点开始遍历图,如果该节点已经遍历过则直接返回克隆图的对应的节点。如果没有,则创建克隆节点,并将其存在哈希表中。然后递归调用每个节点的邻接点,最后返回这些克隆邻接点的列表。

class Solution:
    def cloneGraph(self, node: 'Node') -> 'Node':
        tmp = {} #key 是原始图中的节点,value 是克隆图中的对应节点。
        if not node:
            return
        def dfs(node):
            if node in tmp:
                return tmp[node] #返回其克隆图中的对应节点。
            clone = Node(node.val, [])  #创建克隆节点
            tmp[node] = clone           #存储在哈希表中
            for i in node.neighbors:    #递归调用每个节点的邻接点
                clone.neighbors.append(dfs(i))
            return clone
        return dfs(node)
2.课程表(medium)

题目:你这个学期必须选修 numCourse 门课程,记为 0 到 numCourse-1 。
在选修某些课程之前需要一些先修课程。 例如,想要学习课程 0 ,你需要先完成课程 1 ,我们用一个匹配来表示他们:[0,1]
给定课程总量以及它们的先决条件,请你判断是否可能完成所有课程的学习?
示例 1:
输入: 2, [[1,0]]
输出: true
解释: 总共有 2 门课程。学习课程 1 之前,你需要完成课程 0。所以这是可能的。

思路:这是一道典型的拓扑排序的问题。
拓扑排序定义:给定一个有向无环图 G,将G中所有顶点排成一个线性序列,使得图G 中任意一对顶点u和v,u 在排列中都出现在 v的前面。
拓扑排序的解题思路:先统计所有节点的入度,对入度为0的先取出来,再把这个节点指向的其他节点的入度减一,不断重复,直到所有的节点都被分离出来。如果最后不存在入度为0的节点,说明不是有向无环图,则不存在拓扑排序

本题采用广度优先搜索和拓扑排序相结合。
1)用一个字典保存节点与其邻节点的关系,字典的键为当前节点,值是一个数组存放其相邻的节点。并采用一个数组来存储对应节点的入度。
2)先考虑拓扑排序中最前面的点,其入度为0,并将其加入队列中,即他没有任何先修课程; 在广度优先搜索的每一步中,我们取出队首的节点 u:当把u加入到答案中,则其相邻的节点v就少了一门先修课程的要求【即入度-1】,直到相邻节点的入度为0,则可以开始学习这门课程v。以此类推,将入度为0 的节点加入答案,知道答案中包含了所有的节点,或者不存在入度为0的节点(图中包含环)

class Solution:
    def canFinish(self, numCourses: int, prerequisites: List[List[int]]) -> bool:
        edges = collections.defaultdict(list)
        indeg = [0] * numCourses #记录要学习课程,需要先学习的课程数

        for cur,pre in prerequisites: #统计各节点的入度
            edges[cur].append(pre) 
            indeg[cur] += 1   

        q = collections.deque()
        for u in range(numCourses):  #将入度为0的节点入队列
            if indeg[u] == 0:
                q.append(u)

        while q:  
            numCourses-=1
            u = q.popleft() #从队列中取出节点
            for v in edges[u]: #将该节点的邻接节点入度-1
                indeg[v] -= 1 
                if indeg[v] == 0: #当其邻接节点入度为0时,将该节点入队
                    q.append(v)

        return not numCourses #当所有的节点都已经加入答案这说明所有课程都可以学
3.课程表 || (medium)

题目:现在你总共有 n 门课需要选,记为 0 到 n-1。
在选修某些课程之前需要一些先修课程。 例如,想要学习课程 0 ,你需要先完成课程 1 ,我们用一个匹配来表示他们: [0,1]
给定课程总量以及它们的先决条件,返回你为了学完所有课程所安排的学习顺序。
可能会有多个正确的顺序,你只要返回一种就可以了。如果不可能完成所有课程,返回一个空数组。
示例 1:
输入: 2, [[1,0]]
输出: [0,1]
解释: 总共有 2 门课程。要学习课程 1,你需要先完成课程 0。因此,正确的课程顺序为 [0,1] 。

思路:就是在上题的基础上将每次出队的节点保存下来

import collections
class Solution:
    def findOrder(self, numCourses: int, prerequisites: List[List[int]]) -> List[int]:
        edge = collections.defaultdict(list)
        indeg = [0] * numCourses
        
        for cur,pre in prerequisites:
            edge[pre].append(cur)
            indeg[cur]+=1

        q = collections.deque()
        for u in range(numCourses):
            if indeg[u] == 0:
                q.append(u)
        res = []
        while q:
            numCourses -=1
            u = q.popleft()
            res.append(u)
            for v in edge[u]:
                indeg[v]-=1
                if indeg[v] == 0:
                    q.append(v)
        if numCourses:
            return []
        else:
            return res
4.钥匙和房间(medium)

题目:有 N 个房间,开始时你位于 0 号房间。每个房间有不同的号码:0,1,2,…,N-1,并且房间里可能有一些钥匙能使你进入下一个房间。在形式上,对于每个房间 i 都有一个钥匙列表 rooms[i],每个钥匙 rooms[i][j] 由 [0,1,…,N-1] 中的一个整数表示,其中 N = rooms.length。 钥匙 rooms[i][j] = v 可以打开编号为 v 的房间。最初,除 0 号房间外的其余所有房间都被锁住。你可以自由地在房间之间来回走动。如果能进入每个房间返回 true,否则返回 false。
示例 1:
输入: [[1],[2],[3],[]]
输出: true
解释:
我们从 0 号房间开始,拿到钥匙 1。
之后我们去 1 号房间,拿到钥匙 2。
然后我们去 2 号房间,拿到钥匙 3。
最后我们去了 3 号房间。
由于我们能够进入每个房间,我们返回 true。

思路:第n个房间有第m个房间的钥匙时,就有从n–>m房间,这总个M间房间可看成是有向图中的M个节点,n–>m可看成是一条有向边。问题就转化为求从第一个节点开始是否能遍历完所有节点。采用深度优先搜索的方法遍历整张图,统计下到达的节点数,判断其是否等于节点总数。考虑到节点是否已经被访问过(重复),可以用一个集合记录访问过的节点

class Solution:
    def canVisitAllRooms(self, rooms: List[List[int]]) -> bool:
        def dfs(n):
            nonlocal num
            num+=1
            vis.add(n)
            for i in rooms[n]:
                if i not in vis:
                    dfs(i)
            
        vis = set()
        num = 0
        length = len(rooms)
        dfs(0)
        return num==length
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值