拓扑排序

《数据结构与算法分析——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);
}


  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值