《数据结构与算法分析——C语言描述》 第九章
第九章不像以前那样直接给出代码了。是伪代码。很多具体实现需要自己去想。图的声明和实现想了挺久的。
graph.h
#ifndef _Graph_H #define _Graph_H struct EdgeNode; typedef struct EdgeNode* EdgeNodePtr; struct GraphRecord; typedef struct GraphRecord* Graph; typedef int Vertex; #define NOTAVERTEX -1 Graph 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; else Error("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_H typedef 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
#include"hash.h" #include"fatal.h" #include<math.h> #include<string.h> #define MinTableSize 5 enum 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; } else return 0; }
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 10000 char 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 { //以前出现过点i i = 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); }