tasks for today:
1. 图论理论基础
2. 深搜理论基础
3. 98. 所有可达路径
4. 广搜理论基础
-----------------------------------------------------------------------------------
1. 图论理论基础
directed / undirected
weighted / unweighted
degree / (in/out degree)
connectivity: connected / unconnected / strong connected (only in directed graph)
connected component (in undirected graph) [maximum undirected child graph] / strong connected component (in directed graph) [maximum strong connected directed child graph]
how to express a graph in code format:
adjcent matrix: g[2][5] = 6 imply the connection points out from 2 to 5, with weight 6. If in an undirected graph, the g[2][5] = g[5][2] = 6;
therefore, for a graph with n nodes, the adjcent matrix would be with size of (n, n)
this adjcent matrix is suitable for representing a condence graph (more connections), whereas not suitable for sparse graph (less connections).
adjcent table: this use "array + linked list" data structure, the array records the start node, while each connection points to another node in linked list format.
This method is suitable for sparse graph with less connections, which is effecient on memory.
Two ways to traverse a graph for finding all the path from one node to another node: BFS vs DFS
2. 深搜理论基础 (DFS)
need 2 global varibales
vector<vector<int>> result; // 保存符合条件的所有路径
vector<int> path; // 起点到终点的路径
dfs structure
void dfs(参数) {
if (终止条件) {
存放结果;
return;
}
for (选择:本节点所连接的其他节点) {
处理节点;
dfs(图,选择的节点); // 递归
回溯,撤销处理结果
}
}
3. 98. 所有可达路径 (leetcode 797)
For graph, two types of representation should be commanded, which are adjcent matrix and adjcent table.
here is the code for using adjcent matrix.
def dfs(graph, x, n, path, result):
if x == n:
result.append(path.copy())
return
for i in range(1, n + 1):
if graph[x][i] == 1:
path.append(i)
dfs(graph, i, n, path, result)
path.pop()
def main():
n, m = map(int, input().split())
graph = [[0] * (n + 1) for _ in range(n + 1)]
for _ in range(m):
s, t = map(int, input().split())
graph[s][t] = 1
result = []
dfs(graph, 1, n, [1], result)
if not result:
print(-1)
else:
for path in result:
print(' '.join(map(str, path)))
if __name__ == "__main__":
main()
Here is the code for using adjcent table.
from collections import defaultdict
result = [] # 收集符合条件的路径
path = [] # 1节点到终点的路径
def dfs(graph, x, n):
if x == n: # 找到符合条件的一条路径
result.append(path.copy())
return
for i in graph[x]: # 找到 x指向的节点
path.append(i) # 遍历到的节点加入到路径中来
dfs(graph, i, n) # 进入下一层递归
path.pop() # 回溯,撤销本节点
def main():
n, m = map(int, input().split())
graph = defaultdict(list) # 邻接表
for _ in range(m):
s, t = map(int, input().split())
graph[s].append(t)
path.append(1) # 无论什么路径已经是从1节点出发
dfs(graph, 1, n) # 开始遍历
# 输出结果
if not result:
print(-1)
for pa in result:
print(' '.join(map(str, pa)))
if __name__ == "__main__":
main()
4. 广搜理论基础 (BFS)
BFS is suitable for finding the shortest path between two nodes, because the search mode for BFS is based on one node and searching by circles.
we need one container to record the elements that has been traversed, it can be queue, stack or array. Usually, in binary tree problems, queue is selected as the container.
code structure
int dir[4][2] = {0, 1, 1, 0, -1, 0, 0, -1}; // 表示四个方向
// grid 是地图,也就是一个二维数组
// visited标记访问过的节点,不要重复访问
// x,y 表示开始搜索节点的下标
void bfs(vector<vector<char>>& grid, vector<vector<bool>>& visited, int x, int y) {
queue<pair<int, int>> que; // 定义队列
que.push({x, y}); // 起始节点加入队列
visited[x][y] = true; // 只要加入队列,立刻标记为访问过的节点
while(!que.empty()) { // 开始遍历队列里的元素
pair<int ,int> cur = que.front(); que.pop(); // 从队列取元素
int curx = cur.first;
int cury = cur.second; // 当前节点坐标
for (int i = 0; i < 4; i++) { // 开始想当前节点的四个方向左右上下去遍历
int nextx = curx + dir[i][0];
int nexty = cury + dir[i][1]; // 获取周边四个方向的坐标
if (nextx < 0 || nextx >= grid.size() || nexty < 0 || nexty >= grid[0].size()) continue; // 坐标越界了,直接跳过
if (!visited[nextx][nexty]) { // 如果节点没被访问过
que.push({nextx, nexty}); // 队列添加该节点为下一轮要遍历的节点
visited[nextx][nexty] = true; // 只要加入队列立刻标记,避免重复访问
}
}
}
}