《数据结构与算法分析——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
hash.cpp
#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;
}
简单拓扑,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 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);
}