最小生成树Kruskal算法:首先对边的权值进行从小到大排列,每次从剩余的边中选择权值较小且边的两个顶点不在
同一个集合内的边(就是不会产生回路的边),加到生成树中,直到加入的边为n-1为止
时间复杂度:m 边数 n顶点数 对边进行快速排序是O(MlogM) 在m条边中选择n-1条边的是O(MlogN); 故时间
复杂度为O(MlogM + MlogN);通常M比N大很多,故时间复杂度为O(MlogM);
#pragma mark - - 最小生成树 Kruskal 算法
struct edge {
int u; // 顶点
int v; // 顶点
int w; // 权值
};
struct edge e[10];
void quicksort(int left,int right) {
if (left>right) {
return ;
}
int I=left;
int j=right;
int temp = e[left].w; //基准数
struct edge t;
while (i!=j) {
// 从右往左找 直到找到一个小于基准数 停下
while (e[j].w >= temp && i
Prim 算法:
1.从任意一个顶点开始构造树,假设从顶点1开始。首先将顶点1加入生成树中,用一个一维数组Book来标记哪些顶点
已经加入生成树
2.用数组dis记录生成树到各个顶点的距离。最初生成树中只有1个顶点。有直连边时,数组dis中存储的就是1号顶点
到该顶点的边的权值,没有直连边的时候就是无穷大,即初始化dis数组
3.从数组dis中选出离生成树最近的顶点(假设这个顶点为j)加入到生成树中(Book[j]==1)。然后以j为中间点,
更新生成树到每一个非树顶点的距离(松弛),即如果dis[k] > e[j][k]则更新为dis[k]=e[j][k](j在生成树
中,e[j][k]就是表示非树顶点k到生成树的以j为中间点的距离,dis[k]表示非树顶点k到生成树的以其他顶点(
顶点j除外)为中间点的最短距离)
4.重复上述操作,直到生成树中有n个顶点为止~
#pragma mark - - 最小生成树 Prim 算法 (DJP算法)
-(void)test2 {
int s[10][3] = {{0,0,0},{2,4,11},{3,5,13},{4,6,3},{5,6,4},{2,3,6},{4,5,7},{1,2,1},{3,4,9},{1,3,2}};
int m=9;
int n=6;
int z[10][10];
int inf =9999;
// 初始化 边数据
for (int i=0; i<10; i++) {
for (int j=0; j<10; j++) {
if (i==j) {
z[i][j] =0;
}else {
z[i][j] =inf;
}
}
}
for (int i=1; i<=m; i++) {
int s1 = s[i][0];
int s2 = s[i][1];
int s3 = s[i][2];
// 无向图
z[s1][s2] = s3;
z[s2][s1] = s3;
}
// 初始化化 dis数组
int dis[10];
for (int i=0; i<10; i++) {
dis[i] = 9999;
}
// 初始化book数组 标记一个顶点是否已经加入到生成树
int book[10] = {0};
// 假设从顶点1开始
dis[1] =0;
// 记录总距离
int sum=0;
// Prim算法 核心
for (int j=1; j<=n; j++) {
// 较小值
int min =inf;
// 较小点
int u=0;
// 找到最小值
for (int i=1; i<=n; i++) {
if (book[i]==0 && dis[i] z[u][k] && book[k]==0) {
dis[k] = z[u][k];
}
}
}
printf("\n最短距离为%d\n\n",sum);
}
优化方向:
1.dis[]最小值 可以用堆排序
2.扫描当前顶点u所有边 再以u为中间点,更新生成树到每一个非树顶点的距离,这块可以用邻接表
#pragma mark - - 优化 Prim 算法
int size=6;
int dis[7];
int h[7]; //保存堆数据 h[i]=t 表示顶点t
int pos[7]; // pos用来存储每个顶点在堆中的位置 pos[h[i]] = I;
int book[7]={0};
void siftdown(int i) {
int t=0;
int flag=0;
while (2*i<=size && flag == 0) {
if (dis[h[i]] > dis[h[2*i]]) {
t =2*I;
}else {
t = I;
}
if (1+2*i <= size) {
if (dis[h[t]] > dis[h[2*i+1]]) {
t =2*I+1;
}
}
if (t!=i) {
swap(t, i);
I=t;
}else {
flag=1;
}
}
}
void swap(int x, int y) {
int t = h[x];
h[x] = h[y];
h[y] = t;
// 同步更新 pos
t = pos[h[x]];
pos[h[x]] = pos[h[y]];
pos[h[y]] = t;
return;
}
// 从堆顶取出一个元素
int pop() {
int t;
t = h[1];
pos[t] =0;
h[1] = h[size];
pos[h[1]] = 1;
size --;
siftdown(1);
return t;
}
void siftup(int i) {
if (i==1) {
return ;
}
int flag=0;
while (i!=1 && flag == 0) {
if (dis[h[i]] < dis[h[i/2]]) {
swap(i, i/2);
}else {
flag =1;
}
i=I/2;
}
}
-(void)test3 {
int u[20],v[20],w[20];
int first[20],next[20];
int m=9;
int n=6;
int count=0;
int sum=0;
int inf=9999;
int s[10][3] = {{0,0,0},{2,4,11},{3,5,13},{4,6,3},{5,6,4},{2,3,6},{4,5,7},{1,2,1},{3,4,9},{1,3,2}};
for (int i=1; i<=m; i++) {
u[i] = s[i][0];
v[i] = s[i][1];
w[i] = s[i][2];
}
// 由于是无向图 所以需要把所有边反向再存储一遍
for (int i=m+1; i<=2*m; i++) {
u[i] = v[i-m];
v[i] = u[i-m];
w[i] = w[i-m];
}
//初始化 first
for (int i=0; i<20; i++) {
first[i] = -1;
}
// 邻接表
for (int i=1; i<=2*m; i++) {
next[i] = first[u[I]];
first[u[i]] = I;
}
// Prim 核心代码
// 将1号点加入生成树
book[1] =1;
count++;
// 初始化dis数组,这里是1号顶点到其余各个顶点的初始距离
for (int i=0; i<=n; i++) {
dis[i] = inf;
}
dis[1]=0;
int k=first[1];
while (k!=-1) {
dis[v[k]] = w[k];
k=next[k];
}
// 初始化堆
for (int i=1; i<=size; i++) {
h[i] =I;
pos[i] =I;
}
for (int i=size/2; i>=1; i--) {
siftdown(i);
}
//先弹出一个堆顶元素 因为此时堆顶是1号顶点
pop();
while (count w[k]) {
dis[v[k]] =w[k];
siftup(pos[v[k]]);
}
k=next[k];
}
}
printf("\n最短距离为%d\n\n",sum);
}