No.8.1 图的最小生成树

No.8.1 图的最小生成树

 

 

 一、找出一条路,可以连接所有顶点,并且路径之和最小!《一个连通的无向图,且没有回路,那么这就是求一颗最小生成树》

思路:

  1.要想总长度之和最小,那么应该从最短的边开始选取,将边进行由小到大排序;

  2.从最小边开始选,并且要保证不能有回路,按照并查集算法,即判断两个点是否在同一个集合(有相同的父节点);

  3.递归,直到选出 N-1 条边为止(无回路的 n 个点连接,只需要 n-1 条边)

 

struct edge{  // u = 起点,v = 终点, w = 边长
  int u;
  int v;
  int w;
};

struct edge e[10];  //用于存储输入边
int n,m;
int f[7]={0},sum=0,count=0;

void quicksort(int left, int right){  // 第一章学习的快速排序算法,时间复杂度O(logN)
  int i,j;
  struct edge t;
  if(left>right)
    return;
  i=left;
  j=right;
  while(i!=j){

    // 以left为基准,查找的顺序很重要,先从右边开始搜索,然后才是左边
    while(e[j].w>=e[left].w && i<j)
      j--;
    while(e[i].w<=e[left].w && i<j)
      i++;
    if(i<j){  // 比left大的都放右边,比left小的都放左边
      t=e[i];
      e[i]=e[j];
      e[j]=t;
    }
  }

  // 基准点left,已经被调到 i 
  t=e[left];
  e[left]=e[i];
  e[i]=t;

  // 递归左右两边的队列,完成 由小到大排序

  quicksort(left,i-1);
  quicksort(i+1,right);
  return;
}

int getf(int v){
  if(f[v]==v)
    return v;
  else{
    f[v]=getf(f[v]);
    return f[v];
  }
}

int merge(int x, int y){
  int u,v;
  u=getf(x);
  v=getf(y);

  // 如果两个点不在同一集合中,合并成功;否则,合并失败
  if(u!=v){
    f[v]=u;
    return 1;
  }
  return 0;
}

int main(){
  int i;
  scanf("%d %d",&n,&m);

  for(i=1;i<=m;i++)
  scanf("%d %d %d",&e[i].u,&e[i].v,&e[i].w);

  quicksort(1,m);
  for(i=1;i<=n;i++)
    f[i]=i;

  for(i=1;i<=m;i++){
    if(merge(e[i].u,e[i].v)){  // 如果合并成功
      count++;
      sum=sum+e[i].w;
    }
    if(count==m-1)
      break;
  }

  printf("%d ",sum);
  getchar();getchar();return 0;
}

 

二、关于 quicksort 的一点DEBUG:

1.首先界定了 left > right 时,quicksort结束,那么什么时候 left 会大于 right 呢?

2.在下面这个地方:首先右边的数大于等于left时,指针可以左移 j--;然后左边的数小于"等于"left时,指针也会右移;就出现了刚好 e[i]=e[j]=e[left]的一个位置,这时候j--,i++,就出现了 i > j,left交换的位置 i,成立的不等式为 e[i] >= e[j].

  while(i!=j){

    while(e[j].w>=e[left].w && i<j)
      j--;
    while(e[i].w<=e[left].w && i<j)
      i++;

一定要注意好边界!

上一篇:快速排序精简版


下一篇:快速排序