1. 程式人生 > >資料結構之拓撲排序和關鍵路徑

資料結構之拓撲排序和關鍵路徑

拓撲排序

在一個表示工程的有向圖中,用頂點表示活動,用弧表示活動之間的優先關係,這樣的有向圖為頂點表示活動的網,稱為AOV網
在這裡插入圖片描述
基本思路:從AOV網中選擇一個入度為0的頂點輸出,然後刪去此頂點,並刪除以此頂點為尾的弧,繼續重複此步驟,直到輸出全部頂點或者AOV網中不存在入度為0的頂點為止。

在求最小生成樹和最短路徑時,我們用的都是鄰接矩陣,但由於拓撲排序的過程中,需要刪除頂點,顯然用鄰接表會更加方便。因此我們需要為AOV網建立一個鄰接表。考慮到演算法過程中始終要查詢入度為0的頂點,我們在原來頂點表結點結構中,增加一個入度域in:

入度 Vi 指標
in data firstedge

在這裡插入圖片描述
在這裡插入圖片描述

  1. 把入度為0的都入棧,{V0,V1,V3}
    在這裡插入圖片描述
  2. V3出棧,列印此頂點,輸出頂點個數count+1,將V3連線的兩個頂點V2和V13入度減1。
    在這裡插入圖片描述
  3. 再處理V1,出棧、列印,count+1,將V1連線的頂點V2、V4、V8入度減1,此時V2入度為0,將V2入棧……如此迴圈,列印V2、V6、V0、V4、V5、V8、V7、V12、V9、V10、V13、V11,所以最終列印的結果為:3->1->2->6->0->4->5->8->7->12->9->10->13->11
    。入棧的複雜度為O(n),入度減1執行e次,所以整個演算法的時間複雜度為O(n+e)
    在這裡插入圖片描述
#include "stdio.h"    
#include "stdlib.h"   
#include "io.h"  
#include "math.h"  
#include "time.h"

#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0
#define MAXEDGE 20
#define MAXVEX 14
#define INFINITY 65535

typedef int Status;	/* Status是函式的型別,其值是函式結果狀態程式碼,如OK等 */
/* 鄰接矩陣結構 */ typedef struct { int vexs[MAXVEX]; int arc[MAXVEX][MAXVEX]; int numVertexes, numEdges; }MGraph; /* 鄰接表結構****************** */ typedef struct EdgeNode /* 邊表結點 */ { int adjvex; /* 鄰接點域,儲存該頂點對應的下標 */ int weight; /* 用於儲存權值,對於非網圖可以不需要 */ struct EdgeNode *next; /* 鏈域,指向下一個鄰接點 */ }EdgeNode; typedef struct VertexNode /* 頂點表結點 */ { int in; /* 頂點入度 */ int data; /* 頂點域,儲存頂點資訊 */ EdgeNode *firstedge;/* 邊表頭指標 */ }VertexNode, AdjList[MAXVEX]; typedef struct { AdjList adjList; int numVertexes, numEdges; /* 圖中當前頂點數和邊數 */ }graphAdjList, *GraphAdjList; /* **************************** */ void CreateMGraph(MGraph *G)/* 構件圖 */ { int i, j; /* printf("請輸入邊數和頂點數:"); */ G->numEdges = MAXEDGE; G->numVertexes = MAXVEX; for (i = 0; i < G->numVertexes; i++)/* 初始化圖 */ { G->vexs[i] = i; } for (i = 0; i < G->numVertexes; i++)/* 初始化圖 */ { for (j = 0; j < G->numVertexes; j++) { G->arc[i][j] = 0; } } G->arc[0][4] = 1; G->arc[0][5] = 1; G->arc[0][11] = 1; G->arc[1][2] = 1; G->arc[1][4] = 1; G->arc[1][8] = 1; G->arc[2][5] = 1; G->arc[2][6] = 1; G->arc[2][9] = 1; G->arc[3][2] = 1; G->arc[3][13] = 1; G->arc[4][7] = 1; G->arc[5][8] = 1; G->arc[5][12] = 1; G->arc[6][5] = 1; G->arc[8][7] = 1; G->arc[9][10] = 1; G->arc[9][11] = 1; G->arc[10][13] = 1; G->arc[12][9] = 1; } /* 利用鄰接矩陣構建鄰接表 */ void CreateALGraph(MGraph G, GraphAdjList *GL) { int i, j; EdgeNode *e; *GL = (GraphAdjList)malloc(sizeof(graphAdjList)); (*GL)->numVertexes = G.numVertexes; (*GL)->numEdges = G.numEdges; for (i = 0; i <G.numVertexes; i++) /* 讀入頂點資訊,建立頂點表 */ { (*GL)->adjList[i].in = 0; (*GL)->adjList[i].data = G.vexs[i]; (*GL)->adjList[i].firstedge = NULL; /* 將邊表置為空表 */ } for (i = 0; i<G.numVertexes; i++) /* 建立邊表 */ { for (j = 0; j<G.numVertexes; j++) { if (G.arc[i][j] == 1) { e = (EdgeNode *)malloc(sizeof(EdgeNode)); e->adjvex = j; /* 鄰接序號為j */ e->next = (*GL)->adjList[i].firstedge; /* 將當前頂點上的指向的結點指標賦值給e */ (*GL)->adjList[i].firstedge = e; /* 將當前頂點的指標指向e */ (*GL)->adjList[j].in++; } } } } /* 拓撲排序,若GL無迴路,則輸出拓撲排序序列並返回1,若有迴路返回0。 */ Status TopologicalSort(GraphAdjList GL) { EdgeNode *e; int i, k, gettop; int top = 0; /* 用於棧指標下標 */ int count = 0;/* 用於統計輸出頂點的個數 */ int *stack; /* 建棧將入度為0的頂點入棧 */ stack = (int *)malloc(GL->numVertexes * sizeof(int)); for (i = 0; i<GL->numVertexes; i++) if (0 == GL->adjList[i].in) /* 將入度為0的頂點入棧 */ stack[++top] = i; while (top != 0) { gettop = stack[top--]; printf("%d -> ", GL->adjList[gettop].data); count++; /* 輸出i號頂點,並計數 */ for (e = GL->adjList[gettop].firstedge; e; e = e->next) { k = e->adjvex; if (!(--GL->adjList[k].in)) /* 將i號頂點的鄰接點的入度減1,如果減1後為0,則入棧 */ stack[++top] = k; } } printf("\n"); if (count < GL->numVertexes) return ERROR; else return OK; } int main(void) { MGraph G; GraphAdjList GL; int result; CreateMGraph(&G); CreateALGraph(G, &GL); result = TopologicalSort(GL); printf("result:%d", result); system("pause"); return 0; }

最終列印的結果為:3->1->2->6->0->4->5->8->7->12->9->10->13->11

關鍵路徑

拓撲排序主要是為了解決一個工程能否順利進行的問題,但有時我們還需要解決工程完成需要的最短時間問題。

在一個表示工程的帶權有向圖中,用頂點表示事件,用有向邊表示活動,用邊上的權值表示活動的持續時間,這種網稱為AOE網
在這裡插入圖片描述

就像寫家庭作業這一事件的最早開始時間為現在,為0,最晚開始時間為兩小時後,為2,最早開始時間不等於最晚開始時間就意味著有空閒。為此,我們需要定義幾個引數:

  1. 事件的最早發生時間etv:頂點Vk的最早發生時間
  2. 事件的最晚發生時間ltv:頂點Vk的最晚發生時間
  3. 活動的最早開工時間ete:即弧Ak的最早發生時間
  4. 活動的最晚開工時間lte:即弧Ak的最晚發生時間

在這裡插入圖片描述

  1. 拓撲排序求每個事件的最早發生時間etv
    在這裡插入圖片描述
  2. 初始化全域性變數ltv,因為etv[9]=27,所以陣列ltv當前的值為{27,27,27,27,27,27,27,27,27,27}
  3. 將V9出棧,由於V9沒有弧,所以繼續往後執行。
  4. gettop=8,V8的弧只有一條<V8,V9>,ltv[8]=min{ltv[9]-3,ltv[8]}=24
  5. gettop=7,6,5時同理可得ltv的相應值為19,25,13,此時ltv值為{27,27,27,27,27,13,25,19,24,27}
  6. 當gettop=4時,V4有兩條弧<V4,V6>、<V4,V7>,ltv[4]=min{ltv[7]-4,ltv[6]-9}=min{19-4,25-9}=15。同理得到ltv[0-3]的值:
    在這裡插入圖片描述
  7. ete=etv則是關鍵活動,沒有任何空閒時間,列印;否則不是關鍵活動,不列印。如下<V0,V2>是關鍵活動,列印;<V0,V1>不列印。
    在這裡插入圖片描述
  8. 依次列印關鍵路徑:
    在這裡插入圖片描述

分析整個關鍵路徑的演算法,其時間複雜度為O(n+e)。這裡是唯一一條關鍵路徑,並不等於不存在多條關鍵路徑的有向無環圖。如果是多條關鍵路徑,則單是提高一條關鍵路徑上的關鍵活動速度並不能導致整個工程縮短工期,而必須同時提高几條關鍵路徑上的活動速度。這就像不僅僅需要有事業的成功,還需要有健康的身體以及快樂的生活!

#include "stdio.h"    
#include "stdlib.h"   
#include "io.h"  
#include "math.h"  
#include "time.h"

#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0

#define MAXEDGE 30
#define MAXVEX 30
#define INFINITY 65535

typedef int Status;	/* Status是函式的型別,其值是函式結果狀態程式碼,如OK等 */

int *etv, *ltv; /* 事件最早發生時間和最遲發生時間陣列,全域性變數 */
int *stack2;   /* 用於儲存拓撲序列的棧 */
int top2;	   /* 用於stack2的指標 */

			   /* 鄰接矩陣結構 */
typedef struct
{
	int vexs[MAXVEX];
	int arc[MAXVEX][MAXVEX];
	int numVertexes, numEdges;
}MGraph;

/* 鄰接表結構****************** */
typedef struct EdgeNode /* 邊表結點  */
{
	int adjvex;    /* 鄰接點域,儲存該頂點對應的下標 */
	int weight;		/* 用於儲存權值,對於非網圖可以不需要 */
	struct EdgeNode *next; /* 鏈域,指向下一個鄰接點 */
}EdgeNode;

typedef struct VertexNode /* 頂點表結點 */
{
	int in;	/* 頂點入度 */
	int data; /* 頂點域,儲存頂點資訊 */
	EdgeNode *firstedge;/* 邊表頭指標 */
}VertexNode, AdjList[MAXVEX];

typedef struct
{
	AdjList adjList;
	int numVertexes, numEdges; /* 圖中當前頂點數和邊數 */
}graphAdjList, *GraphAdjList;
/* **************************** */


void CreateMGraph(MGraph *G)/* 構件圖 */
{
	int i, j;
	/* printf("請輸入邊數和頂點數:"); */
	G->numEdges = 13;
	G->numVertexes = 10;

	for (i = 0; i < G->numVertexes; i++)/* 初始化圖 */
	{
		G->vexs[i] = i;
	}

	for (i = 0; i < G->numVertexes; i++)/* 初始化圖 */
	{
		for (j = 0; j < G->numVertexes; j++)
		{
			if (i == j)
				G->arc[i][j] = 0;
			else
				G->arc[i][j] = INFINITY;
		}
	}

	G->arc[0][1] = 3;
	G->arc[0][2] = 4;
	G->arc[1][3] = 5;
	G->arc[1][4] = 6;
	G->arc[2][3] = 8;
	G->arc[2][5] = 7;
	G->arc[3][4] = 3;
	G->arc[4][6] = 9;
	G->arc[4][7] = 4;
	G->arc[5][7] = 6;
	G->arc[6][9] = 2;
	G->arc[7][8] = 5;
	G->arc[8][9] = 3;

}

/* 利用鄰接矩陣構建鄰接表 */
void CreateALGraph(MGraph G, GraphAdjList *GL)
{
	int i, j;
	EdgeNode *e;

	*GL = (GraphAdjList)malloc(sizeof(graphAdjList));

	(*GL)->numVertexes = G.numVertexes;
	(*GL)->numEdges = G.numEdges;
	for (i = 0; i <G.numVertexes; i++) /* 讀入頂點資訊,建立頂點表 */
	{
		(*GL)->adjList[i].in = 0;
		(*GL)->adjList[i].data = G.vexs[i];
		(*GL)->adjList[i].firstedge = NULL; 	/* 將邊表置為空表 */
	}

	for (i = 0; i<G.numVertexes; i++) /* 建立邊表 */
	{
		for (j = 0; j<G.numVertexes; j++)
		{
			if (G.arc[i][j] != 0 && G.arc[i][j]<INFINITY)
			{
				e = (EdgeNode *)malloc(sizeof(EdgeNode));
				e->adjvex = j;					/* 鄰接序號為j */
				e->weight = G.arc[i][j];
				e->next = (*GL)->adjList[i].firstedge;	/* 將當前頂點上的指向的結點指標賦值給e */
				(*GL)->adjList[i].firstedge = e;		/* 將當前頂點的指標指向e  */
				(*GL)->adjList[j].in++;

			}
		}
	}

}


/* 拓撲排序 */
Status TopologicalSort(GraphAdjList GL)
{    /* 若GL無迴路,則輸出拓撲排序序列並返回1,若有迴路返回0。 */
	EdgeNode *e;
	int i, k, gettop;
	int top = 0;  /* 用於棧指標下標  */
	int count = 0;/* 用於統計輸出頂點的個數 */
	int *stack;	/* 建棧將入度為0的頂點入棧  */
	stack = (int *)malloc(GL->numVertexes * sizeof(int));
	for (i = 0; i<GL->numVertexes; i++)
		if (0 == GL->adjList[i].in) /* 將入度為0的頂點入棧 */
			stack[++top] = i;

	top2 = 0;
	etv = (int *)malloc(GL->numVertexes * sizeof(int)); /* 事件最早發生時間陣列 */
	for (i = 0; i<GL->numVertexes; i++)
		etv[i] = 0;    /* 初始化 */
	stack2 = (int *)malloc(GL->numVertexes * sizeof(int));/* 初始化拓撲序列棧 */

	printf("TopologicalSort:\t");
	while (top != 0)
	{
		gettop = stack[top--];
		printf("%d -> ", GL->adjList[gettop].data);
		count++;        /* 輸出i號頂點,並計數 */

		stack2
            
           

相關推薦

資料結構排序關鍵路徑

拓撲排序 在一個表示工程的有向圖中,用頂點表示活動,用弧表示活動之間的優先關係,這樣的有向圖為頂點表示活動的網,稱為AOV網。 基本思路:從AOV網中選擇一個入度為0的頂點輸出,然後刪去此頂點,並刪除以此頂點為尾的弧,繼續重複此步驟,直到輸出全部頂點或者AOV網中不存在入度為0的頂

資料結構與演算法------排序關鍵路徑

一.拓撲排序 這裡請結合參考部落格學習(在後面) 拓撲排序(無環圖的應用) 在一個表示工程的有向圖中,有頂點表示活動,用弧表示活動之間的優先關係,這樣的有向圖為頂點表示活動的網,我們稱為AOV(Activity On Vertex)網。 AOV網中的弧表示活動之間存在的某種制約關

[從今天開始修煉資料結構]無環圖的應用 —— 排序關鍵路徑演算法

上一篇文章我們學習了最短路徑的兩個演算法。它們是有環圖的應用。下面我們來談談無環圖的應用。   一、拓撲排序     博主大學學的是土木工程,在老本行,施工時很關鍵的節約人力時間成本的一項就是流水施工,鋼筋沒綁完,澆築水泥的那幫兄弟就得在那等著,所以安排好流水施工,讓工作週期能很好地銜接就很關鍵。這樣的工程活

資料結構作業16—排序關鍵路徑(選擇題)

2-1已知有向圖G=(V, E),其中V = {v1, v2, v3, v4, v5, v6},E = {<v1,v2>, <v1,v4>, <v2,v6>, <v3,v1>, <v3,v4>, <v4,v5>, <

第七章-圖-排序關鍵路徑-計算機17級

解析在下面  解析: x2-1:這個就是定義,最長的路徑 x2-2: 這個補充一個知識: 一個小補充: 分別用佇列和堆疊作為容器,對計算機專業課程進行拓撲排序,得到的序列有什麼區別?用哪種容器排課更合理? 答案: 根據棧和佇列的特性可以

圖:圖的應用(最小生成樹、排序關鍵路徑

一:求最小生成樹 應用場景:例如要在n個城市之間鋪設光纜,主要目標是要使這 n 個城市的任意兩個之間都可以通訊,但鋪設光纜的費用很高,且各個城市之間鋪設光纜的費用不同,因此另一個目標是要使鋪設光纜的總費用最低。這就需要找到帶權的最小生成樹。 普里姆演算法:

有向圖_排序_AOE關鍵路徑_判斷圖中是否存在環

目錄 0.圖——判環 0.1.1無向圖判斷是否存在環 0.1.1無向圖,若深度優先遍歷過程中遇到回邊(即指向已訪問過的頂點的邊),則該無向圖必定存在環路。 參考圖的關節點與重連通分量,圖的深度優先生成樹。 定義visi

[資料結構]Graph排序BFS&DFS實現

什麼是拓撲排序在這裡就不說了。直接講講拓撲排序的DFS和BFS實現演算法。 一、DFS實現拓撲排序 演算法描述:遞迴實現 利用了一個輔助函式實現遞迴,下面先對這個輔助函式進行分析: base case:當所有的“鄰居”點都被訪問過後結束遞迴,並將當前節點加入到佇列的0號位

數據結構10——排序

tar 結構 www arch font art logs ref ive 一、前人種樹 博客:拓撲排序 博客:拓撲排序數據結構10——拓撲排序

資料結構最大子列

#include <stdlib.h> #include <stdio.h> int MaxSubseqSum(int a[],int N) {     int i,ThisSum = 0,MaxSum = 0;    &nb

【Python排序搜尋基本演算法】排序

   拓撲排序是對有向無環圖的一種排序,滿足如下兩個條件: 1.每個頂點出現且只出現一次; 2.若A在序列中排在B的前面,則在圖中不存在從B到A的路徑。 如上的無環有向圖,v表示頂點:v=['a','b','c','d','e'],e表示有向邊:e=[('a

資料結構 筆記:歸併排序快速排序

歸併排序的基本思想 -將兩個或兩個以上的有序序列合併成一個新的有序序列 template < typename T> static void Merge(T src[] , T helper[],int begin,int mid,int end, bool mi

資料結構 筆記:氣泡排序希爾排序

氣泡排序的基本思想 -每次從後向前進行(假設為第i次),j = n -1 ,n- 2,...,i,兩輛比較 V[j-1]和V[j]的關鍵字;如果發生逆序,則交換V[j-1]和V[j]. template <typename T> static void Bubble(T

資料結構 筆記:選擇排序插入排序

選擇排序的基本思想 -每次(例如底 i 次,i = 0 ,1,...,n-2)從後面n-i個待排的資料元素中選出關鍵字最小的元素,作為有序元素序列底 i 個元素. template <typename T> static void Select(T array[] , i

資料結構靜態順序表動態順序表

@Sock對靜態順序表和動態順序表的總結 簡單概括他們的異同點 相同點:記憶體空間連續, 資料順序儲存 不同點:它們所佔記憶體空間的位置不同, 靜態定義一個順序表, 順序表所佔的記憶體空間開闢在記憶體的靜態區, 即所謂的函式棧上, 隨著函式呼叫的結束, 這塊記憶體區域會被系統自動

java資料結構二叉排序

binary sort tree / binary search tree 性質: 1.若左子樹不為空,則左子樹上所有節點的值均小於它的根節點的值。 2.若右子樹不為空,則右子樹上所有節點的值均大於它的根節點的值。 3.左右子樹也是二叉排序樹。 4.沒有值相同的節點

資料結構二叉排序

上一節我們介紹了二分(折半)查詢,也瞭解了它的優缺點。 二分查詢的特點:二分查詢能夠提高有序表中資料元素的查詢速度;二分查詢的時間複雜度為O(log2n);二分查詢是一種靜態查詢二分查詢的不足:當查詢表經常變化時,二分查詢的整體效能急劇下降。 二分查詢的硬傷:二分查詢基於有序表。

Java版資料結構二叉排序

簡介 新增結點 查詢結點 刪除結點 程式碼實現 public class MyBinarySortTree { int data;//結點權值 MyBinarySortTree leftTree;//左子樹 MyBinarySort

溫習Algs4 (四):有向圖, 排序強連通分量

有向圖, 拓撲排序和強連通分量 有向圖 Digraph.java 有向環 DiCycle.java 深度優先搜尋序列 DFSOrder.java 拓撲排序 Topo

資料結構二叉排序樹(C語言實現)

一、基本概念1.二叉排序樹        二叉排序樹(Binary sort tree,BST),又稱為二叉查詢樹,或者是一棵空樹;或者是具有下列性質的二叉樹:        (1)若它的左子樹不為空,則左子樹上所有節點的值均小於它的根節點的值;        (2)若它的右