单源无权最短路径

单源无权最短路径是指对一个无权图G=(V,E),给定一个顶点s作为输入(单源),从该顶点出发分别到各个顶点的最短无权路径。最短无权路径可以看成是最短赋权路径的特殊情况,只是所有的边的权值都为1.

以下图为例:

选择v_{3}为输入顶点,从v_{3}开始计算每一个顶点的最短无权路径。

显然,从v_{3}v_{3}的距离为0,把这个信息作个标记:

接下来找到与v_{3}距离为1的顶点,这些顶点可以通过考察v_{3}的邻接顶点找到:

 

接下来找到所有与v_{3}距离为3的顶点,这些点可以通过考察与v_{3}距离为1的顶点的邻接顶点找到:

 接下来找到与v_{3}距离为3的顶点:

 

到此就得到了所有顶点的最短无权路径,这种搜索一个图的方法称为广度优先搜索,这种方法按照层处理顶点,首先处理输入顶点的邻接顶点,再处理邻接顶点的邻接顶点,很像树的层序遍历。

代码实现:

void Unweighted(g* p) {
	int currdis;
	for (currdis = 0; currdis < 7; currdis++) {//当前处理的顶点的距离
		for (int i = 0; i < 7; i++) {//遍历找到未知的对应距离元素
			if (p->v[i]->known == 0 && p->v[i]->d == currdis) {//如果元素未知且距离相符就处理
				p->v[i]->known = 1;//将该元素标记为已知
				l* tmp = p->v[i]->next;
				while (tmp != NULL) {//处理其邻接顶点
					if (p->v[tmp->val]->d == inf) {//如果邻接顶点的距离没有被更新才进行更新
						p->v[tmp->val]->d = currdis + 1;
						p->v[tmp->val]->path = i;//路径中的上一个顶点
					}
					tmp = tmp->next;
				}
			}
		}
	}
}

 这个算法包含了两个for循环,所以算法的时间复杂度为O(|V|^2),由于在循环过程中,不断地考察了一些已知的顶点和一些已经处理过的未知顶点,所以算法的效率较低。我们可以参考拓扑排序的改进来对这个算法进行改进,我们希望在处理当前定点后处理那些未知的并且距离已经赋值的顶点,而不对已知的或是未被处理过的顶点进行考察,所以可以使用一个队列来存放将要被处理的顶点。首先将输入顶点入队,当队列不为空时,队头元素出队,并对队头元素的邻接顶点进行处理,如果邻接顶点是未知的,则改变它的距离并入队,重复这些工作直到队列为空。以上图为例,下面用表格来说明这个过程:

v初始状态
Knowndispath
v_{1}0inf-1
v_{2}0inf-1
v_{3}00-1
v_{4}0inf-1
v_{5}0inf-1
v_{6}0inf-1
v_{7}0inf-1
Queuev_{3}
vv_{3}出队
Knowndispath
v_{1}01

  v_{3}

v_{2}0inf-1
v_{3}10-1
v_{4}0inf-1
v_{5}0inf-1
v_{6}01

 v_{3}

v_{7}0inf-1
Queuev_{1}v_{6}

 

vv_{1}出队
Knowndispath
v_{1}11v_{3}
v_{2}02v_{1}
v_{3}10-1
v_{4}02v_{1}
v_{5}0inf-1
v_{6}01v_{3}
v_{7}0inf-1
Queuev_{6}v_{2}v_{4}
vv_{6}出队
Knowndispath
v_{1}11v_{3}
v_{2}02v_{1}
v_{3}10-1
v_{4}02v_{1}
v_{5}0inf-1
v_{6}11v_{3}
v_{7}0inf-1
Queuev_{2}v_{4}
vv_{2}出队
Knowndispath
v_{1}11v_{3}
v_{2}12v_{1}
v_{3}10-1
v_{4}02v_{1}
v_{5}03v_{2}
v_{6}11v_{3}
v_{7}0inf-1
Queuev_{4}v_{5}
vv_{4}出队
Knowndispath
v_{1}11v_{3}
v_{2}12v_{1}
v_{3}10-1
v_{4}12v_{1}
v_{5}03v_{2}
v_{6}11v_{3}
v_{7}03v_{4}
Queuev_{5}v_{7}
vv_{5}出队
Knowndispath
v_{1}11v_{3}
v_{2}02v_{1}
v_{3}10-1
v_{4}02v_{1}
v_{5}03v_{2}
v_{6}01v_{3}
v_{7}03v_{4}
Queuev_{7}
vv_{7}出队
Knowndispath
v_{1}11v_{3}
v_{2}02v_{1}
v_{3}10-1
v_{4}02v_{1}
v_{5}03v_{2}
v_{6}01v_{3}
v_{7}03v_{4}
Queueempty

 代码实现:

void UnweightedQ(g* p) {
	q* pq = CreatQueue();
	enqueue(pq, 2);//首先将输入顶点入队
	while (!isempty(pq)) {//队列不为空就出队
		int n = dequeue(pq);
		p->v[n]->known = 1;
		l* tmp = p->v[n]->next;
		while (tmp != NULL) {//对出队顶点的邻接顶点进行考察,如果没有被处理过,就进行处理并入队
			if (p->v[tmp->val]->d == inf) {
				p->v[tmp->val]->d = p->v[n]->d + 1;
				p->v[tmp->val]->path = n;
				enqueue(pq, tmp->val);
			}
			tmp = tmp->next;
		}
	}
}

 图的定义和测试代码:

#define inf 99999999//定义无穷大来表示未到达的顶点的距离

typedef struct list {//邻接表,用来存放该顶点的邻接顶点的编号
	int val;
	struct list* next;
}l;

typedef struct table {//用来存放各个顶点的信息
	int known;//顶点是否已知
	int d;//顶点到输入顶点的距离
	int path;//顶点在路径中的上一个顶点
	l* next;//指向邻接表的指针
}t;

typedef struct graph {//图,存放指向七个顶点信息的指针
	t* v[7];
}g;

g* CreatGraph() {//用暴力方法建立一个图
	g* pg = (g*)malloc(sizeof(g));
	for (int i = 0; i < 7; i++) {//在程序中,下标为0处代表编号为1的顶点
		t* p = (t*)malloc(sizeof(l));
		p->next = NULL;
		p->d = inf;
		p->known = 0;
		p->path = -1;
		pg->v[i] = p;
	}

	l* p = (l*)malloc(sizeof(l));
	p->val = 1;
	p->next = pg->v[0]->next;
	pg->v[0]->next = p;
	p = (l*)malloc(sizeof(l));
	p->val = 3;
	p->next = pg->v[0]->next;
	pg->v[0]->next = p;

	p = (l*)malloc(sizeof(l));
	p->val = 3;
	p->next = pg->v[1]->next;
	pg->v[1]->next = p;
	p = (l*)malloc(sizeof(l));
	p->val = 4;
	p->next = pg->v[1]->next;
	pg->v[1]->next = p;

	p = (l*)malloc(sizeof(l));
	p->val = 0;
	p->next = pg->v[2]->next;
	pg->v[2]->next = p;
	p = (l*)malloc(sizeof(l));
	p->val = 5;
	p->next = pg->v[2]->next;
	pg->v[2]->next = p;

	p = (l*)malloc(sizeof(l));
	p->val = 2;
	p->next = pg->v[3]->next;
	pg->v[3]->next = p;
	p = (l*)malloc(sizeof(l));
	p->val = 4;
	p->next = pg->v[3]->next;
	pg->v[3]->next = p;
	p = (l*)malloc(sizeof(l));
	p->val = 5;
	p->next = pg->v[3]->next;
	pg->v[3]->next = p;
	p = (l*)malloc(sizeof(l));
	p->val = 6;
	p->next = pg->v[3]->next;
	pg->v[3]->next = p;

	p = (l*)malloc(sizeof(l));
	p->val = 6;
	p->next = pg->v[4]->next;
	pg->v[4]->next = p;

	p = (l*)malloc(sizeof(l));
	p->val = 5;
	p->next = pg->v[6]->next;
	pg->v[6]->next = p;

	return pg;
}

//队列ADT
typedef struct queue {
	int arr[7];
	int rear;
	int front;
	int num;
}q;

q* CreatQueue() {
	q* p = (q*)malloc(sizeof(q));
	p->front = 0;
	p->rear = -1;
	p->num = 0;
	return p;
}

void enqueue(q* p, int i) {
	p->arr[++p->rear] = i;
	p->num++;
}

int dequeue(q* p) {
	p->num--;
	return p->arr[p->front++];
}

int isempty(q* p) {
	return p->num == 0;
}

//打印图中的信息
void print(g* p) {
	for (int i = 0; i < 7; i++) {
		printf("v%d     path = v%d     know = %d     distance = %d\n", i + 1, p->v[i]->path + 1, p->v[i]->known, p->v[i]->d);
	}
}

//测试代码
int main() {

	g* pg = CreatGraph();

	pg->v[2]->d = 0;//选择编号3的顶点为输入顶点

	//Unweighted(pg);

	UnweightedQ(pg);

	print(pg);

	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值