Python描述数据结构之图实战篇

前言

  本篇章所涉及到的题目均来源于力扣(LeetCode),著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
  LeetCode中有关的题目。

1. LeetCode684:冗余连接

  LeetCode第684题:冗余连接

  题目 在本问题中, 树指的是一个连通且无环的无向图。
  输入一个图,该图由一个有着 N N N个节点 ( ( (节点值不重复 1 , 2 , . . . , N ) 1, 2, ..., N) 1,2,...,N)的树及一条附加的边构成。附加的边的两个顶点包含在 1 1 1 N N N中间,这条附加的边不属于树中已存在的边。
  结果图是一个以边组成的二维数组。每一个边的元素是一对 [ u , v ] [u, v] [u,v],满足 u < v u < v u<v,表示连接顶点 u u u v v v的无向图的边。
  返回一条可以删去的边,使得结果图是一个有着 N N N个节点的树。如果有多个答案,则返回二维数组中最后出现的边。答案边 [ u , v ] [u, v] [u,v]应满足相同的格式 u < v u < v u<v
  注意
  (1) 输入的二维数组大小在 3 3 3 1000 1000 1000
  (2) 二维数组中的整数在 1 1 1 N N N之间,其中 N N N是输入数组的大小。
  解题思路 这个题可以当做并查集来做,思路就是检查每一条边上两个顶点是否在一个集合中,如果在,那么加入这条边之后图中就会构成环;如果不在,就让它并入集合中。

  有关并查集的知识及操作请参阅这篇博客

  代码实现如下:

    def Find(self, S, x):
        while S[x] != -1:
            x = S[x]
        return x

    def Union(self, S, root1, root2, result):
        # 这里的减一是因为我设置的S是从0开始的
        x = self.Find(S, root1-1)
        y = self.Find(S, root2-1)
        if x != y:
            S[y] = x
        else:
            result.append([root1, root2])

    def findRedundantConnection(self, edges):
        length = len(edges)
        S = [-1] * length
        result = []
        for i, j in edges:
            self.Union(S, i, j, result)
        return result[-1]

  运行结果如下:

在这里插入图片描述

2. LeetCode841:钥匙和房间

  LeetCode第841题:钥匙和房间

  题目 N N N个房间,开始时你位于 0 0 0号房间。每个房间有不同的号码: 0 , 1 , 2 , . . . , N − 1 0,1,2,...,N-1 012...N1,并且房间里可能有一些钥匙能使你进入下一个房间。在形式上,对于每个房间 i i i都有一个钥匙列表 r o o m s [ i ] rooms[i] rooms[i],每个钥匙 r o o m s [ i ] [ j ] rooms[i][j] rooms[i][j] [ 0 , 1 , . . . , N − 1 ] [0,1,...,N-1] [0,1...N1]中的一个整数表示,其中 N = r o o m s . l e n g t h N = rooms.length N=rooms.length。 钥匙 r o o m s [ i ] [ j ] = v rooms[i][j] = v rooms[i][j]=v可以打开编号为 v v v的房间。
  最初,除 0 号房间外的其余所有房间都被锁住。你可以自由地在房间之间来回走动。如果能进入每个房间返回 t r u e true true,否则返回 f a l s e false false
  提示

1.	1 <= rooms.length <= 1000
2.	0 <= rooms[i].length <= 1000
3.	所有房间中的钥匙数量总计不超过 3000

  解题思路 这个题可以将房间看成图中的顶点,而房间里的钥匙则是连接另个一个顶点的弧,没错,是个有向图。所以这个问题的本质就是图的遍历,如果遍历一次结束,顶点全部被访问过,即能进入每个房间,返回 t r u e true true,否则就返回 f a l s e false false

  有关图遍历的操作,请参阅我的这篇博客

  深度优先遍历代码实现如下:

    def canVisitAllRooms(self, rooms):
        self.rooms = rooms
        length = len(rooms)
        visited = [False] * length
        self.DFS(visited, v=0)
        if len(set(visited)) == 1:
            return True
        else:
            return False

    def DFS(self, visited, v):
        visited[v] = True
        for k in self.rooms[v]:
            # k是房间v里面的钥匙
            if not visited[k]:
                self.DFS(visited, k)

  运行结果如下:

在这里插入图片描述

  广度优先遍历代码实现如下:

    def canVisitAllRooms(self, rooms):
        import collections

        self.rooms = rooms
        length = len(rooms)
        visited = [False] * length
        visited[0] = True
        # 把房间0加入队列
        queue = collections.deque(iterable=[0])
        self.BFS(visited, queue)
        if len(set(visited)) == 1:
            return True
        else:
            return False

    def BFS(self, visited, queue):
        while queue:
            v = queue.popleft()
            for k in self.rooms[v]:
                # k是房间v里面的钥匙
                if not visited[k]:
                    visited[k] = True
                    queue.append(k)

  运行结果如下:

在这里插入图片描述

3. LeetCode1387:将整数按权重排序

  LeetCode第1387题:将整数按权重排序

  题目 我们将整数 x x x权重 定义为按照下述规则将 x x x 变成 1 1 1 所需要的步数:
  如果 x x x 是偶数,那么 x = x / 2 x = x / 2 x=x/2
  如果 x x x 是奇数,那么 x = 3 ∗ x + 1 x = 3 * x + 1 x=3x+1
  比方说, x = 3 x=3 x=3 的权重为 7 7 7 。因为 3 3 3 需要 7 7 7 步变成 1 ( 3 − − > 10 − − > 5 − − > 16 − − > 8 − − > 4 − − > 2 − − > 1 ) 1 (3 --> 10 --> 5 --> 16 --> 8 --> 4 --> 2 --> 1) 1(3>10>5>16>8>4>2>1)
  给你三个整数 l o lo lo h i hi hi k k k 。你的任务是将区间 [ l o , h i ] [lo, hi] [lo,hi] 之间的整数按照它们的权重 升序排序 ,如果大于等于 2 2 2 个整数有 相同 的权重,那么按照数字自身的数值 升序排序 。
  请你返回区间 [ l o , h i ] [lo, hi] [lo,hi] 之间的整数按权重排序后的第 k k k 个数。
  注意题目保证对于任意整数 x ( l o < = x < = h i ) x (lo <= x <= hi) x(lo<=x<=hi) ,它变成 1 1 1 所需要的步数是一个 32 32 32 位有符号整数。
  提示

1.	1 <= lo <= hi <= 1000
2.	1 <= k <= hi - lo + 1

  解题思路 这个题可以把它当做图,用深度优先遍历来做。就是把区间里的整数看成图中的顶点,用 R R R表示吧,计算每个 R R R的权重的过程又可以看成遍历图的过程,我举个栗子,就是下面这张图,对应着示例 1

在这里插入图片描述
  大致就是这么个意思,哈哈哈哈(ノ´▽`)ノ♪
  代码实现如下:

    def DFS(self, x):
        if x == 1:
            # 递归结束条件
            return 0
        # 统计步数
        elif x % 2 == 0:
            # 偶数
            return self.DFS(x / 2) + 1
        elif x % 2 != 0:
            # 奇数
            return self.DFS(3 * x + 1) + 1

    def getKth(self, lo, hi, k):
        result = []
        for x in range(lo, hi+1):
            count = self.DFS(x)
            result.append([x, count])
        result = sorted(result, key=lambda item: item[1])
        return result[k-1][0]

  运行结果如下:

在这里插入图片描述
  时间消耗较多,勉强算是跑完了ヾ(@^▽^@)ノ

4. LeetCode997:找到小镇的法官

  LeetCode第997题:找到小镇的法官

  题目 在一个小镇里,按从 1 1 1 N N N 标记了 N N N 个人。传言称,这些人中有一个是小镇上的秘密法官。
  如果小镇的法官真的存在,那么:

  • 小镇的法官不相信任何人。
  • 每个人(除了小镇法官外)都信任小镇的法官。
  • 只有一个人同时满足属性 1 1 1 和属性 2 2 2

  给定数组 t r u s t trust trust,该数组由信任对 t r u s t [ i ] = [ a , b ] trust[i] = [a, b] trust[i]=[a,b] 组成,表示标记为 a a a 的人信任标记为 b b b 的人。
  如果小镇存在秘密法官并且可以确定他的身份,请返回该法官的标记。否则,返回 − 1 -1 1
  提示

1.	1 <= N <= 1000
2.	trust.length <= 10000
3.	trust[i] 是完全不同的
4.	trust[i][0] != trust[i][1]
5.	1 <= trust[i][0], trust[i][1] <= N

  解题思路 画了一个图可以发现,这个题可以用有向图来做,即把每个人当做图中的顶点,如果 P e r s o n 1 Person1 Person1信任 P e r s o n 2 Person2 Person2,则 P e r s o n 1 Person1 Person1是弧尾, P e r s o n 2 Person2 Person2是弧头,由题目可知,所有人都信任法官,即法官所在顶点的入度为 N − 1 N-1 N1,法官不信任任何人,即法官所在顶点的出度为 0 0 0
  代码实现如下:

    def findJudge(self, N, trust):
        indegree = [0] * N
        outdegree = [0] * N
        for i, j in trust:
            outdegree[i-1] += 1
            indegree[j-1] += 1
        for index in range(N):
            if indegree[index] == N-1 and outdegree[index] == 0:
                return index + 1
        return -1

  运行结果如下:

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/qq_42730750/article/details/108847249