在LeetCode的题目中,图有多种表示方法。
1. 题目133一类,用一个顶点表示整张图,比较少见,和二叉树的情形类似,因此需要整张图顶点是连通的;
2. 题目997一类,还有后面207,210,310等题目用边来表示图,不用邻接表是因为邻接表会一下暴露答案吧,或者基于一定的实际含义;
2. 题目841一类,典型的邻接表的表示方法,也很常见。
133. Clone Graph
Given a reference of a node in a connected undirected graph.
Return a deep copy (clone) of the graph.
Each node in the graph contains a val (int
) and a list (List[Node]
) of its neighbors.
题解:
1. 注意题目的入参和出参,都是Node结构,而题目说明及例子解释绕了个弯。2. 题目的解法就是遍历的过程中,进行深拷贝,和二叉树相关内容类似,要重新新建图的顶点并按原来连接。
看方法一,通过队列进行广度优先的搜索,按遍历顺序复制顶点即邻居;visit存放已经完成复制的点,dic存放顶点值和复制后的顶点的映射;在遍历的过程中,要把cur当前顶点的所有邻居添加到self.neighbors , 邻居如果已经新建则直接引用,未新建则新建一个,注意新建点的时候,他的self.neighbors是空的,并没有完成该点的复制,只有在while 循环完成cur点才是复制完的。
"""
# Definition for a Node.
class Node:
def __init__(self, val = 0, neighbors = None):
self.val = val
self.neighbors = neighbors if neighbors is not None else []
"""
class Solution:
def cloneGraph(self, node: 'Node') -> 'Node':
dic = dict()
visit = set()
dq = []
if not node:
return None
dq.append(node)
head = Node(node.val)
dic[node.val] = head
while len(dq) > 0:
cur = dq.pop()
if cur.val in visit:
continue
tmp = dic[cur.val]
for node_ in cur.neighbors:
if node_.val in dic.keys():
chi = dic[node_.val]
else:
chi = Node(node_.val)
dic[node_.val] = chi
tmp.neighbors.append(chi)
dq.append(node_)
visit.add(cur.val)
return head
方法二,递归实现的深度优先遍历,代码量较少,dic既用于查找也起到了visit访问查找的作用,dfs两个参数,新旧两张图的相同顶点,主要就是把所有邻居的复制品的引用加入到该顶点的邻居表。
class Solution:
def cloneGraph(self, node: 'Node') -> 'Node':
if not node:
return None
dic = {}
head = Node(node.val)
dic[node.val] = head
def dfs(x, copy):
for chl in x.neighbors:
if chl.val in dic.keys():
cur = dic[chl.val]
copy.neighbors.append(cur)
else:
cp = Node(chl.val)
copy.neighbors.append(cp)
dic[chl.val] = cp
dfs(chl, cp)
tmp = head
dfs(node, tmp)
return head
997 Find the Town Judge
In a town, there are N
people labelled from 1
to N
. There is a rumor that one of these people is secretly the town judge.
If the town judge exists, then:
- The town judge trusts nobody.
- Everybody (except for the town judge) trusts the town judge.
- There is exactly one person that satisfies properties 1 and 2.
You are given trust
, an array of pairs trust[i] = [a, b]
representing that the person labelled a
trusts the person labelled b
.
If the town judge exists and can be identified, return the label of the town judge. Otherwise, return -1
.
题解:
该题用边表示图,读题意其实是让我们找到一个点,该点的出度(信任别人)为0,入度(被人信任)为n-1,因此鄙人的解法就是直接统计每个点的入度和出度,存储在列表里,下标对应1-N每个人,最后线性过一遍即可。解法的时空效率都很不错。
class Solution:
def findJudge(self, N: int, trust: List[List[int]]) -> int:
if N == 1:
return 1
totrust = [0] * N
betrust = [0] * N
for edge in trust:
totrust[edge[0]-1] += 1
betrust[edge[1]-1] += 1
ret = []
for i, n in enumerate(betrust):
if n == N-1 and totrust[i] == 0:
return i+1
return -1
841. Keys and Rooms
There are N
rooms and you start in room 0
. Each room has a distinct number in 0, 1, 2, ..., N-1
, and each room may have some keys to access the next room.
Formally, each room i
has a list of keys rooms[i]
, and each key rooms[i][j]
is an integer in [0, 1, ..., N-1]
where N = rooms.length
. A key rooms[i][j] = v
opens the room with number v
.
Initially, all the rooms start locked (except for room 0
).
You can walk back and forth between rooms freely.
Return true
if and only if you can enter every room.
题解:
我觉得这应该算简单级别题目,最直接的邻接表表示图,然后我们验证从顶点0开始图是连通的就行。用队列直接一个bfs看一下就行。
class Solution:
def canVisitAllRooms(self, rooms: List[List[int]]) -> bool:
visit = set()
n = len(rooms)
dq = [0]
while len(visit) < n and len(dq) > 0:
cur = dq.pop()
if cur in visit:
continue
visit.add(cur)
dq.extend(rooms[cur])
return len(visit) == n
这一篇是图相关的最基本题目,只考察简单的遍历。下面一篇的题目会稍有难度
9.2.3 图的遍历及路径 —— Reconstruct Itinerary & All Paths From Source to Target