AOP-Chap25-Graphs

本文介绍了图在任务调度、资源分配、路径规划和社会网络中的应用,以及图的两种常见实现方式:邻接矩阵和邻接表。此外,还探讨了深度优先搜索、广度优先搜索以及Dijkstra算法在寻找最短路径中的作用。最后,讨论了Prim和Kruskal算法在构建最小生成树上的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

  • graph --> a collection of nodes(also called vertices顶点) and edges
  • 可能会在边上设置权重weights——表示节点之间连接的一些成本或其他度量(可以在边上附加其他信息,但权重是最常见的)
  • 在某些情况下,使用无向边,而在其他情况下可能需要有向边

1 Graph Applications

1.1 Task Scheduling

  • 任务的依赖性
  • critical path关键路径 --> constrain how quickly we can complete our entire work
  • If reduce the length of this particular path enough, then it becomes non-critical, and some other path becomes critical
  • tasks that are not on the critical path have some slack—could start them later without affecting the overall completion time
  • 在general cases中,调度图可能是一个有向无环图directed acylic graph(DAG)——它不包含有向环,尽管它可能包含无向环
  • 任务图中的有向循环将不可能进行调度,因为它将指示任务中的循环依赖关系——我们将需要在A之前完成B、C之前完成B和A之前完成C——没有一个任务可以先被调度
  • 无向循环是允许的(there is a cycle if we ignore the edge directions, but not if we pay attention to them)。完全可以B和C依赖于A,同时D依赖于B和C
    请添加图片描述

1.2 Resource Allocation

  • graph coloring是将一个图赋色给每个节点,使每个相邻的节点都不具有相同的颜色的过程
  • 在资源分配的情况下,创建一个interference graph干涉图——在这个图中,如果两个节点相互冲突(即同时需要一个资源),则通过一条边连接两个节点。在这样的图中,一个有效的着色代表一个有效的资源分配,其中每个颜色代表一个特定的资源
    请添加图片描述
  • 相邻的不一个颜色,一共四个屋子,所以四种颜色,平面图没有交叉
  • 四色定理four color theorem --> 每个平面图最多有4种颜色

1.3 Path Planning

  • edge weight表示穿越这段路所用的时间

1.4 Social Networks

2 Graph Implementations

  • 最简单表示graph的方法:用一个无符号整数标识每个节点,并用double表示权重(或者是整数,或者没有权重信息)
class Graph {
public:
	unsigned addNode();
	void removeNode (unsigned whichNode);
	void addEdge(unsigned from, unsigned to, double weight);
	void removeEdge(unsigned from, unsigned to);
	unsigned getNodeCount() const;
	set<pair<unsigned, double> > getAdjacencies(unsigned whichNode) const; 
	double getEdge(unsigned from, unsigned to) const;
	bool isAdjacent(unsigned from, unsigned to) const;
};	
  • 更通用的graph
template<typename N, typename E, bool directed>
class Graph {
public:
	void addNode(const N & nodeInfo);
	void removeNode (const N & nodeInfo);
	void addEdge(const N & fromNode, const N & toNode, const E & edgeInfo); 
	void removeEdge(const N & fromNode, const N & toNode);
	set<const N &> getNodes() const;
	set<pair<const N &, E &> > getAdjacencies(const N & whichNode);
	const E & getEdge(const N & fromNode, const N & toNode) const;
	bool isAdjacent(const N & fromNode, const N & toNode) const;	
};	

2.1 Adjacency Matrix

  • adjacency Matrix一个二维数组(或向量的向量),其中每个节点有一行和一列。存储在矩阵每个元素中的信息表明从“行”节点(即信息所在行对应的节点)到“列”节点是否存在边(以及与该边相关的任何信息,如果存在的话)
  • 添加一个节点就是在两个方向上展开矩阵。如果我们使用向量的向量,我们可以添加一个新的“行”,通过创建一个填充到适当大小的“无边”信息的向量,并将其添加到向量中。然后,可以遍历所有“行”,并通过在向量的末尾添加“无边”来添加“列”
  • 删除一个节点就是通过从向量的正确位置删除对应的行和列。但是,我们需要一种方法将n映射到它们在向量中的下标。如果N只是一个无符号int(并且节点id是连续的),那么这就很简单了。否则,我们可能会保留一个从n到unsigned int的映射
    请添加图片描述
  • 正无穷表示无权重,不相邻
  • 如果图是无向的,要确保在每次操作边缘信息时添加/删除相反方向的边

2.2 Adjacency List

  • adjacency list 邻接表 --> 保持一个map --> 从每个节点到邻接节点信息的映射
  • map<N, map<N, E> >
  • 将每个节点(即source node)映射到一个从节点(即destination node)到edge information的映射。如果能在第二个map中找到边缘信息,那么节点是相邻的;否则,它们不相邻(至少在那个方向上不相邻)
    请添加图片描述

2.3 Efficiency

请添加图片描述

  • adjacency matrix的addEdge是1因为add翻倍后平摊

3 Graph Searches

  • 返回bool(有无edge)或返回path(一个data structure,一个从source node到dest node的节点列表)

3.1 Depth-First Search: Recursively

  • 探索其他路径之前完全探索一条路径,可能会得到一条比需要的长得多的路径
searchForExit(room, visited) {
  if (room is the exit) {
    return the path [room]
  }
  if (room is in visited) {
    return no valid path
  }
  visited = visited + room
  for each passageway leaving the room {
    go down the passageway to a new room R
    if (searchForExit(R) yields a path P) {
      return the path R + room
    }
  }
  return no valid path
}
  • 其他应用:strongly connected components , topological sort

3.2 Depth-First Search: Explicit Stack 显式堆栈

  • 构建路径并将它们压入一个堆栈。堆栈的后进先出使得始终沿着一条路径探索,在考虑different options closer to the start之前
    请添加图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
  • stack可以被queue或priority queue替换
  • 这个算法是worklist algorithm的一个example,它keep a list of items to work on,从列表中获取一个项目,处理它,并在这个过程中可能生成新的work(然后添加到工作列表中)

3.3 Breadth-First Search

  • 从源头向外探索
  • 得到“跳数”最少的路径(即遍历其他节点最少的路径),但不一定是最短的total path weight
  • 把stack变为queue --> DFS变为BFS
bfs(from, to) {
  Queue todo;   //only change Stack -> Queue
  Set visited
  todo.push(the path [from])
  as long as todo is not empty {
    currentPath = todo.pop()
    currentNode = currentPath.lastNode()
    if (currentNode == to) {
      return currentPath
    }
    if (currentNode is not in visited) {
      visited.add(currentNode)
      for each (x adjacent to currentNode) {
        todo.push(currentPath.addNode(x))
      }
	}
  }
  return no valid path exists
} 	
  • template
template<typename WorkList>
search(from, to) {
  WorkList todo;   //generic in WorkList type
  Set visited
  todo.push(the path [from])
  as long as todo is not empty {
    currentPath = todo.pop()
    currentNode = currentPath.lastNode()
    if (currentNode == to) {
      return currentPath
    }
    if (currentNode is not in visited) {
      visited.add(currentNode)
      for each (x adjacent to currentNode) {
        todo.push(currentPath.addNode(x))
      }
	}
  }
  return no valid path exists
}

3.4 Dijkstra’s Shortest Path Algorithm

  • 在no edges of negative weight的前提下,找到最短路径
  • 如果有向图且负weight边,则Bellman-Ford算法可以使用,只要no cycles with a net negative cost(在这种情况下,没有最短路径,因为任何路径都可以变短,通过遍历negative cost cycle多次)
  • Dijkstra算法的原始公式是keep一个表来记录从源节点到每个可能目的地的最佳距离。该表初始化时,所有item是正无穷(到目前为止还没有已知的路径),除了源节点,它被初始化为距离为0(它可以从自身到达而不需要任何代价)。然后,算法通过选择表中距离最小(从尚未完成的节点中选择)的尚未标记完成的节点继续进行。然后,算法根据当前节点的邻接关系更新表,因为可能已经发现了一个新的更好的距离
  • 可以用来找到最短路径from one source to one destination(它ends when destination node选为当前节点)或找到最短路径从一个source到every possible destination, it ends after all nodes have been marked completed
    请添加图片描述
  • 表列出从节点A到图中每个node的最短路径的距离
  • 执行Dijkstra算法的一种更简单的方法是使用上一节介绍的通用搜索算法,但对worklist使用优先队列。优先级队列必须按照总路径长度排序,使得长度最短的路径具有最高的优先级

4 Minimum Spanning Trees

  • select the subset of the edges that connect all of the nodes together with the minimum total cost (sum of edge weights)–>minimum spanning tree最小生成树(它被称为“生成树”,因为它是一个横跨图的所有节点的树;在所有可能的生成树中,我们想要代价最小的那棵)

4.1 Prim’s Algorithm

  • 从任何一个节点(与哪个节点无关)开始,然后从它构建一个连接树。该算法在任何时候都保持一个连接的树,并通过选择可以以最小代价(从树中当前任何节点的最小边权值)添加的节点,一次“增长”一个节点。当所有节点都包含在最小生成树中时,算法终止
  • 使用一个由边组成的优先队列,按照每条边的权重排序,权重最低的边优先级最高。该算法首先选择图中的一个节点,并将其边添加到优先队列中。然后,该算法从队列中取出权值最低的边,检查它是否形成了一个循环——也就是说,看看边的两个“端点”是否都已经在树中,这很容易通过保留一组在树中的节点来完成。如果这条边将形成一个循环,它将被从优先队列中移除,不需要其他操作。如果它不能形成一个循环,那么它就向树中添加一个新节点,因此从该节点开始的边被添加到优先队列中(并且刚刚被使用的边被从优先队列中删除)
    在这里插入图片描述
    在这里插入图片描述
    请添加图片描述
    请添加图片描述
    在这里插入图片描述
    请添加图片描述

4.2 Kruskal’s Algorithm

  • 图中的所有边都被放入一个优先级队列(按照权重最低的边拥有最高优先级的顺序排列)。该算法从优先队列中取出优先级最高的边,并测试它是否形成一个循环。如果这条边确实形成了一个循环,它将被忽略。如果没有形成循环,则包含在最小生成树中
    请添加图片描述
    请添加图片描述
    请添加图片描述
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值