拓扑排序

来源:互联网 发布:面板数据和截面数据 编辑:程序博客网 时间:2024/06/15 22:10

《数据结构与算法分析——C语言描述》  第九章


第九章不像以前那样直接给出代码了。是伪代码。很多具体实现需要自己去想。图的声明和实现想了挺久的。


graph.h

#ifndef _Graph_H#define _Graph_Hstruct EdgeNode;typedef struct EdgeNode* EdgeNodePtr;struct GraphRecord;typedef struct GraphRecord* Graph;typedef int Vertex;#define NOTAVERTEX -1Graph initialize_Graph(int size);void insertEdge(int edge_i, int edge_j, double weight, Graph g);//插入边int getEdgeNum(Graph g);//获取 图的边数EdgeNodePtr advance(EdgeNodePtr p);double getWeight(EdgeNodePtr p);int getVex(EdgeNodePtr p);EdgeNodePtr getEdgeNodePtr(int vex, Graph g);int indegree(Vertex v, Graph g);int * indegreeArray(Graph g, int vexNum);#endif




graph.cpp

#include"graph.h"#include<stdlib.h>#include"fatal.h"#include"hash.h"#include<string.h>struct EdgeNode {int adjvex;EdgeNodePtr nextedge;double weight;};struct VexNode {EdgeNodePtr firstedge;int indegree;};struct GraphRecord {struct VexNode* vexs;int maxVexNum;int edgeNum;};Graph initialize_Graph(int size) {Graph g = (Graph)malloc(sizeof(struct GraphRecord));if (g == NULL)Error("OUT OF MEMORY");g->vexs =(VexNode*) malloc(sizeof(struct VexNode)*size);if (g->vexs == NULL)Error("OUT OF MEMORY");for (int i = 0; i < size; i++) {g->vexs[i].firstedge = NULL;g->vexs[i].indegree = 0;}g->maxVexNum = size;g->edgeNum = 0;return g;}static void extend(Graph g) {//插入的点太多,要申请2倍的空间容纳更多的点struct VexNode * new_vexs =(VexNode*) malloc(sizeof(struct VexNode)*(g->maxVexNum) * 2);//申请两倍的指针数组if (new_vexs == NULL)Error("OUT OF MEMORY");int i;for (i = 0; i < g->maxVexNum; i++) {new_vexs[i] = g->vexs[i];//记录指向原来的链表的首节点的地址}for (; i < (g->maxVexNum) * 2; i++) {new_vexs[i].firstedge = NULL;new_vexs[i].indegree = 0;}free(g->vexs);g->vexs = new_vexs;g->maxVexNum *= 2;}void insertEdge(int edge_i, int edge_j, double weight, Graph g) {if (edge_i < 0 || edge_j < 0)//判断位置是否出错Error("edge Error");while (edge_i >= g->maxVexNum || edge_j >= g->maxVexNum)//插入的点太多了,要扩大空间extend(g);EdgeNodePtr temp = (EdgeNodePtr)malloc(sizeof(struct EdgeNode));if (temp == NULL)Error("OUT OF MEMORY");temp->adjvex = edge_j;temp->weight = weight;//记录权temp->nextedge = g->vexs[edge_i].firstedge;//插入到链表中g->vexs[edge_i].firstedge = temp;g->edgeNum++;g->vexs[edge_j].indegree++;}int getEdgeNum(Graph g) {if (g)return g->edgeNum;elseError("GRAPH EMPTY");}EdgeNodePtr getEdgeNodePtr(int vex, Graph g) {if (vex < 0 || vex >= g->maxVexNum)Error("POSITION ERROR");return g->vexs[vex].firstedge;}int getVex(EdgeNodePtr p) {return p->adjvex;}double getWeight(EdgeNodePtr p) {return p->weight;}EdgeNodePtr advance(EdgeNodePtr p) {return p->nextedge;}int indegree(Vertex v, Graph g) {if (v < 0 || v >= g->maxVexNum)Error("POSITION ERROR");return g->vexs[v].indegree;}int * indegreeArray(Graph g,int vexNum) {if (vexNum > g->maxVexNum)Error("error");int *arr = (int *)malloc(sizeof(int)*vexNum);if (arr == NULL)Error("OUT OF MEMORY");for (int i = 0; i < vexNum; i++)arr[i] = g->vexs[i].indegree;return arr;}




hash.h

typedef char* XType;//图的点的名字typedef int YType;//图的点的名字映射的下标#ifndef _Hash_H#define _Hash_Htypedef unsigned int Position_hash;//元素的位置struct HashTbl;typedef struct HashTbl* HashTable;HashTable initializeTable_hash(int tableSize);//初始化,tableSize为初始大小,返回哈希表void destroyTable_hash(HashTable h);//销毁哈希表Position_hash find_hash(const XType x, HashTable h);//查找元素,用来查找图的点的名字,O(1)完成,返回一个地址,然后再配合isLegitimate看看是否存在元素,是的话x存在HashTable insert_hash(XType x, YType y, HashTable h);//插入图的点的名字,O(1)完成,由于可能再散列,需要更新哈希表的指针HashTable rehash(HashTable h);//再散列,填充因子太高的时候扩充哈希表,避免性能下降,O(N),返回新的哈希表指针YType map_hash(XType x, HashTable h);//获取映射的Y,就是获取图的名字映射的数字O(1)int isLegitimate_hash(Position_hash pos, HashTable h);//查看p哈希表pos位置是否存在元素,配合find使用int isExist(XType x, HashTable h);//查询点是否存在#endif

hash.cpp

#include"hash.h"#include"fatal.h"#include<math.h>#include<string.h>#define MinTableSize 5enum KindOfEntry { Legitimate, Empty, Deleted };//哈希表的数组的单元的状态,已插入,空,懒惰删除typedef struct {XType x;//点的名字YType y;//点再图中的下标}ElementType;struct HashEntry {ElementType element;enum KindOfEntry info;};typedef struct HashEntry Cell;struct HashTbl {int tableSize;int hasinsert_hashedNum;//已插入的数量,用来计算装填因子Cell *theCells;//数组};static int hash(XType s, int tableSize) {//字符串的hash计算unsigned int hashVal = 0;while (*s != '\0')hashVal = (hashVal << 5) + *s++;//字符串的每个字符的ascii的值根据位置不同乘以2的不同次方return hashVal % (tableSize);//返回对数组的取余}static int isPrime(int num) {//判断num是否素数for (int i = 2; i <= sqrt(num); i++)if (num%i == 0)return 0;return 1;}static int nextPrime(int num) {//求num的下一个素数int i = num;while (!isPrime(i))i++;return i;}int isLegitimate_hash(Position_hash pos, HashTable h) {//看看单元是否占有return h->theCells[pos].info == Legitimate;}HashTable initializeTable_hash(int tableSize) {HashTable h;int i;if (tableSize < MinTableSize) {Error("Table size too small");return NULL;}h = (HashTable)malloc(sizeof(struct HashTbl));if (h == NULL)FatalError("Out of space!!!");h->tableSize = nextPrime(tableSize);//数组的大小是素数的话,对大小取余会减少余数相同的概率,从而减少碰撞的概率h->theCells = (Cell*)malloc(sizeof(Cell)*h->tableSize);h->hasinsert_hashedNum = 0;if (h->theCells == NULL)FatalError("Out of space!!!");for (i = 0; i < h->tableSize; i++) {h->theCells[i].info = Empty;//所有的单元声明为空}return h;}void destroyTable_hash(HashTable h) {for (int i = 0; i < h->tableSize; i++)if (h->theCells[i].info != Empty)free(h->theCells[i].element.x);//释放字符串申请的内存空间free(h->theCells);free(h);}Position_hash find_hash(const XType x, HashTable h) {Position_hash currentPos = hash(x, h->tableSize);while (h->theCells[currentPos].info != Empty && strcmp(h->theCells[currentPos].element.x, x) != 0) {//若冲突,判断是否相同,不相同的话,去看看下一个,直到相同或者为空currentPos += 1;//线性探测currentPos = currentPos % h->tableSize;}return currentPos;}HashTable insert_hash(XType x, YType y, HashTable h) {if ((double)h->hasinsert_hashedNum / h->tableSize > 0.5)//线性探测在装填因子大于0.5的时候性能急剧下降h = rehash(h);//装得太多了,再散列,申请更大的内存空间Position_hash pos = find_hash(x, h);if (h->theCells[pos].info != Legitimate) {//插入的字符串曾经不存在h->theCells[pos].element.x = (char *)malloc(sizeof(char)*(strlen(x) + 1));//申请字符数组的空间if (h->theCells[pos].element.x == NULL)Error("OUT OF MEMORY");strcpy(h->theCells[pos].element.x, x);//字符串复制h->theCells[pos].element.y = y;//记录名字在图中的下标h->theCells[pos].info = Legitimate;//记录占用h->hasinsert_hashedNum++;//计数器+1}return h;}HashTable rehash(HashTable h) {HashTable newH = initializeTable_hash(h->tableSize * 2);//申请2倍大的哈希表for (int i = 0; i < h->tableSize; i++)if (h->theCells[i].info == Legitimate)//对每一个不为空的单元插入到新的哈希表中insert_hash(h->theCells[i].element.x, h->theCells[i].element.y, newH);destroyTable_hash(h);//删除原来的哈希表return newH;//返回新的的哈希表的指针}YType map_hash(XType x, HashTable h) {Position_hash p = find_hash(x, h);return h->theCells[p].element.y;}int isExist(XType x, HashTable h) {Position_hash p = find_hash(x, h);if (isLegitimate_hash(p, h)) {return 1;}elsereturn 0;}


简单拓扑,O(V^2)
int findNewVertexOfIndegreeZero(int *indegree, int *selected, int n) {for (int i = 0; i < n; i++) {if (selected[i] == 0 && indegree[i] == 0) {selected[i] = 1;return i;}}return NOTAVERTEX;}void topsort(Graph g) {int counter;Vertex v, w;int *indegree = indegreeArray(g, vexNum);int *topNum = malloc(sizeof(int)*vexNum);if (topNum == NULL)Error("OUT OF MEMORY");int *selected = malloc(sizeof(int)*vexNum);if (selected == NULL)Error("OUT OF MEMORY");memset(selected, 0, sizeof(int)*vexNum);for (counter = 0; counter < vexNum; counter++) {v = findNewVertexOfIndegreeZero(indegree, selected, vexNum);if (v == NOTAVERTEX) {Error("Graph has a cycle");}topNum[v] = counter;EdgeNodePtr adjVexptr = getEdgeNodePtr(v, g);while (adjVexptr) {w = getVex(adjVexptr);indegree[w]--;adjVexptr = advance(adjVexptr);}}for (int i = 0; i < vexNum; i++) {for (int j = 0; j < vexNum; j++) {if (topNum[j] == i) {printf("%s:%d\n", nameRecord[j], topNum[j]);break;}}}}


施行拓扑,O(E+V)

#include"hash.h"#include"graph.h"#include<stdlib.h>#include<stdio.h>#include<string.h>#include"fatal.h"#include<queue>#define MAXN 10000char nameRecord[MAXN][100];int vexNum = 0;//点的个数计数器Graph readGraph() {HashTable hash_StrToNum = initializeTable_hash(5);//初始化一个哈希表Graph g = initialize_Graph(5);//初始化一个邻接图char i_name[100];char j_name[100];int i, j;//点i,点double weight;//权while (scanf("%s%s%lf", i_name, j_name, &weight) == 3) {//输入两个点的名字,和他们之间的权,成功读入的话进入循环if (!isExist(i_name, hash_StrToNum)) {//查看曾经是否输入了点i,在O(1)内迅速找出  //点i第一次出现i = vexNum;//给点i分配图的下标strcpy(nameRecord[vexNum], i_name);//复制到名字记录中hash_StrToNum = insert_hash(i_name, vexNum, hash_StrToNum);//插入到哈希表中,O(1)完成vexNum++;//计数器增加}else {//以前出现过点ii = map_hash(i_name, hash_StrToNum);//O(1)内迅速获取点i的下标}if (!isExist(j_name, hash_StrToNum)) {j = vexNum;strcpy(nameRecord[vexNum], j_name);hash_StrToNum = insert_hash(j_name, vexNum, hash_StrToNum);vexNum++;}else {j = map_hash(j_name, hash_StrToNum);}insertEdge(i, j, weight, g);//在图中插入边}destroyTable_hash(hash_StrToNum);return g;//返回图}void printGraph(Graph g) {if (g) {for (int i = 0; i < vexNum; i++) {//给所有点来一个遍历EdgeNodePtr p = getEdgeNodePtr(i, g);//获取邻接的点printf("indegree:%d ", indegree(i, g));while (p) {printf("(%s,%s)%g ", nameRecord[i], nameRecord[getVex(p)], getWeight(p));p = advance(p);//获取下一个节点printf("\n");}}}else {Error("EMPTY GRAPH");}}void topsort(Graph g) {int counter = 0;Vertex v, w;std::queue<int> q;int *indegree = indegreeArray(g, vexNum);int *topNum = (int *)malloc(sizeof(int)*vexNum);if (topNum == NULL)Error("OUT OF MEMORY");for (int i = 0; i < vexNum; i++) {if (indegree[i] == 0)q.push(i);}while (!q.empty()) {v = q.front();q.pop();topNum[v] = counter++;EdgeNodePtr adjVexptr = getEdgeNodePtr(v, g);while (adjVexptr) {w = getVex(adjVexptr);indegree[w]--;if (indegree[w] == 0) {q.push(w);}adjVexptr = advance(adjVexptr);}}if (counter != vexNum) {Error("has cycle");}for (int i = 0; i < vexNum; i++) {for (int j = 0; j < vexNum; j++) {if (topNum[j] == i) {printf("%s:%d\n", nameRecord[j], topNum[j]);break;}}}}int main() {freopen("filein.txt", "r", stdin);Graph g = readGraph();topsort(g);}


0 0