1.Prim算法,来自于Dijkstra算法的变化,选取一个起始点,然后每次寻找距离这个点权值最小的点,收入树中(使它距离源点的距离为0),然后整个树相当于都是源点,而在V集合中(V集合指的是不在S集合中的点,也就是说还没被收录到树中的点)找一个权值最小的,然后收录之,依次循环,直到所有的点都被收录。
这是一种从点出发的算法,在Dijkstra算法上进行改进。
注意到,我们的初始化过程中首先将权值全部设置为极大值,便于之后选取最小权重,然后将自己到自己的距离设为0,将源点的邻接点设为权值。为什么要这么做呢?试想一下两个集合,一个S一个V,当你在S中放入源点的时候,那V中就要相应的出现其邻接点。
然后我们去在V中(既然是在V中,我们就必须设立一个collected数组作为判断点是否访问过的flag存在),选择一个权重最小的点,加进S中。
最后注意,我们每次改变把V中元素放入S的时候都要更新一下S中的值,去判断一下目前S中的源点(注意,这里的源点可不是一开始写的源点,整个树都是源点,)到邻接点距离和源点取道上一个点再到目标点的距离谁更小,谁小就保存在dist中。
void Dijkstra(int s)
{
int MinDist = INIFITY;
int min_i;
int i;
for (i = num_vertex; i >= 0; i--)//先粗略初始化,把距离权值设为无穷,把经过路径点设置为-1
{
dist[i] = INIFITY;
path[i] = -1;
collected[i] = 0;//收集到为1,未收集到为0
}
dist[s] = 0;//自己到自己的距离为0
//将源点的邻接点距离设置为权值。
Edge E = ver[s].ptr_edge;
while (E != NULL)
{
dist[E->adjvex] = E->weight;//到邻节点的距离就是权值
E = E->next;
}
while (1)
{
MinDist = INIFITY;
for (i = 0; i < num_vertex; i++)//找到dist中最小的那个值
{
if(collected[i] == 0)//我要在未搜集的那些里面去寻找最小距离
if (MinDist > dist[i])
{
MinDist = dist[i];
min_i = i;//并且保存最小值的下标
}
}
if (MinDist == INIFITY)//如果MinDist未发生改变,说明所有点都被搜集到了
break;
collected[min_i] = 1;//最小距离点被访问到了,下面应该去找这个最小值的邻接点了
Edge E = ver[min_i].ptr_edge;
while (E != NULL)
{
if (!collected[E->adjvex])//如果有没被访问过的邻接点
if ((dist[min_i] + E->weight) < dist[E->adjvex])//如果我走两段的距离比直接走要小,那么就取代它
{
dist[E->adjvex] = dist[min_i] + E->weight;
path[E->adjvex] = min_i;//说明我是通过min_走到的邻接点
}
E = E->next;
}
}
2.Kruskal算法,是并查集的应用。每次都取最小的边,但是有要求,不能形成回路,一直搜集,直到搜集到顶点-1,说明搜集完毕。
问题来了,怎么判断不形成回路。要判断这个边的两个点,是否属于一个树(集合)内,如果是,那就是回路,如果不是,那就可以加入。
//邻接表
#include <stdio.h>
#include <stdlib.h>
#define MAXSIZE 100
#define INIFITY 655535
typedef struct LNode_Vertex* Vertex;
typedef struct LNode_Edge* Edge;
typedef struct LNode_List* List;
typedef struct LNode_Queue* Queue;
struct LNode_Vertex
{
int vex;
Edge ptr_edge;
};
struct LNode_Edge
{
int adjvex;
int weight;
Edge next;
};
struct TNode
{
int parent[MAXSIZE];
int data[MAXSIZE];
};
typedef struct TNode* Tree;
struct SetNode
{
int parent;
int data;
};
typedef struct SetNode* Set;
SetNode S[MAXSIZE];
LNode_Vertex ver[MAXSIZE];
int num_vertex, num_edge;
int visited[MAXSIZE];
int dist[MAXSIZE];
int path[MAXSIZE];
int collected[MAXSIZE];
Tree MST;
int delete_i;
void Create_Graph()
{
printf("你要输入的点和边数\n");
scanf("%d %d", &num_vertex, &num_edge);
printf("下面请输入顶点\n");
for (int i = 0; i < num_vertex; i++)
{
scanf("%d", &ver[i].vex);
}
printf("下面请输入顶点1,顶点2,权值\n");
for (int i = 0; i < num_edge; i++)
{
int vex1, vex2, w;
scanf("%d %d %d", &vex1, &vex2, &w);
Edge E = (Edge)malloc(sizeof(struct LNode_Edge));//建立一个边节点
if(ver[vex1].ptr_edge == NULL)
ver[vex1].ptr_edge = E;
else
{
Edge p = ver[vex1].ptr_edge;
while (p->next != NULL)//直到找到顶点节点所引导的一系列节点的最后
{
p = p->next;//每次往后移动一位
}
p->next = E;//将最后一个的next指向E
}
E->weight = w;
E->adjvex = vex2;
E->next = NULL;
}
}
void Print_Graph()
{
for (int i = 0; i < num_vertex; i++)
{
printf("%d——》", ver[i].vex);
if (ver[i].ptr_edge != NULL)
{
printf("[%d,%d]->", ver[i].ptr_edge->adjvex, ver[i].ptr_edge->weight);
while (ver[i].ptr_edge->next != NULL)
{
printf("[%d,%d]->", ver[i].ptr_edge->next->adjvex, ver[i].ptr_edge->next->weight);
ver[i].ptr_edge->next = ver[i].ptr_edge->next->next;
}
}
putchar('\n');
}
}
Edge Find_Min()
{
int Min = ver[0].ptr_edge->weight;
Edge E,Ans_E;
for (int i = 0; i < num_vertex; i++)
{
E = ver[i].ptr_edge;
while (E)
{
if (E->weight < Min)
{
Min = E->weight;
Ans_E = E;
delete_i = i;
}
E = E->next;
}
}
return Ans_E;
}
//int Find_Pre(Edge Ans_E)
//{
// Edge E = ver[delete_i].ptr_edge;
// Edge q = Ans_E;
//
// if (E == q)
// {
// return ver[delete_i].vex;
// }
// else
// {
// while (E->next != q)
// {
// E = E->next;//找到应该被删除的前一项
// }
// return E->adjvex;
// }
//
//}
void DeleteMin(Edge Ans_E)
{
Edge E = ver[delete_i].ptr_edge;
Edge q = Ans_E;
//如果所引导的链表第一个就要被删除
if (E == q)
{
if (E->next == NULL)
ver[delete_i].ptr_edge = NULL;
else
ver[delete_i].ptr_edge = E->next;//指向下一个
}
else
{
while (E->next != q)
{
E = E->next;//找到应该被删除的前一项
}
if (q->next == NULL)
E->next = NULL;
else
E->next = q->next;//指向下一个
}
free(q);//释放被删除链表的空间
}
//下面是并查集的相关操作
int Find_Set(int Item)//查找Item在哪个集合中
{
int i = 0;
while (S[i].data != Item) i++;
while (S[i].parent != -1) i = S[i].parent;//S[i]的爸爸指向的是所在集合头的首元素的下标
return i;
}
void Union_Set(SetNode S[], int x1, int x2)
{
int root1, root2;
root1 = Find_Set(x1);
root2 = Find_Set(x2);
S[root2].parent = root1;
}
void Kruskal()
{
int i, k,flag = 0;
Edge Find_E;
int m, n;
for (i = 0; i < num_vertex; i++)
{
MST->data[i] = INIFITY;
MST->parent[i] = -1;
}
while (flag <= num_vertex -1)
{
Find_E = Find_Min();
flag++;
//这边要用并查集的相关操作
m = delete_i;
n = Find_E->adjvex;
if (Find_Set(m) == Find_Set(n))//如果在同一个集合中,就不操作
;
else//不在一个集合中就合并
{
Union_Set(S,m,n);
}
DeleteMin(Find_E);
}
}
int main()
{
Create_Graph();
Print_Graph();
putchar('\n');
return 0;
}