數(shù)據(jù)結(jié)構(gòu)和算法-最小生成樹

一、概念

最小生成樹:一個有 n 個結(jié)點的連通圖的生成樹是原圖的極小連通子圖,且包含原圖中的所有 n 個結(jié)點,并且有保持圖連通的最少的邊。

生成樹的特點:
1、圖是連通圖;
2、圖中包含了了N個頂點;
3、圖中邊的數(shù)量等于N-1條邊.

求最小生成樹的算法有prim(普里姆)算法和kruskal(克魯斯卡爾)算法

二、prim(普里姆)算法

1、思路

  • 定義2個數(shù)組; adjvex ?來保存相關(guān)頂點下標(biāo); lowcost 保存頂點之間的權(quán)值
  • 初始化2個數(shù)組, 從v0開始尋找最??成樹, 默認(rèn)v0是最??成樹上第?個頂點
  • 循環(huán)lowcost 數(shù)組,根據(jù)權(quán)值,找到頂點 k;
  • 更新lowcost 數(shù)組
  • 循環(huán)所有頂點,找到與頂點k 有關(guān)系的頂點. 并更新lowcost 數(shù)組與adjvex 數(shù)組;
    2、代碼實現(xiàn)
void MiniSpanTree_Prim(MGraph G){
    int min;
    int sum = 0;

    int adjvex[MAXVEX];//保存相關(guān)頂點下標(biāo),如頂點0和頂點相連,則adjvex[1] = 0;
    int lowcost[MAXVEX];//保存相關(guān)頂點的權(quán)值
    
    //當(dāng)v0加入生成樹,adjvex和lowcost數(shù)組的值初始化
    for (int i = 0; i<G.numNodes; i++) {
        lowcost[i] = G.arc[0][i];
        adjvex[i] = 0;
    }
    
    //循環(huán)除0以外的全部頂點,找到lowcost數(shù)組中最小的頂點k
    for (int i = 1; i<G.numNodes; i++) {
        min = INFINITYC;
        int j = 1;
        int k = 0;
        while (j<G.numNodes) {
            if (lowcost[j] != 0 && lowcost[j]<min) {
                min = lowcost[j];
                k = j;
            }
            j++;
        }
        printf("v%d - v%d = %d\n",adjvex[k],k,G.arc[adjvex[k]][k]);
        sum += G.arc[adjvex[k]][k];
        lowcost[k] = 0; //頂點k已加入生成樹
        
        //更新lowcost數(shù)組和adjvex數(shù)組
        for (j = 1; j<G.numNodes; j++) {
            if (lowcost[j] != 0 && G.arc[k][j]<lowcost[j]) {
                lowcost[j] = G.arc[k][j];
                adjvex[j] = k;
            }
        }
    }
    printf("sum = %d\n",sum);
}

三、kruskal(克魯斯卡爾)算法

1、思路

  • 將鄰接矩陣 轉(zhuǎn)化成 邊表數(shù)組;
  • 對邊表數(shù)組根據(jù)權(quán)值按照從?到?的順序排序;
  • 遍歷所有的邊, 通過parent 數(shù)組找到邊的連接信息; 避免閉環(huán)問題;
  • 如果不存在閉環(huán)問題,則加?到最??成樹中. 并且修改parent 數(shù)組

2、代碼實現(xiàn)

邊表數(shù)組Edge結(jié)構(gòu)的定義

typedef struct
{
  int begin;
  int end;
  int weight;
}Edge;

根據(jù)權(quán)值進(jìn)行排序

void sort(Edge edges[],MGraph G)
{
  //對權(quán)值進(jìn)行排序(從小到大)
  int i, j;
  Edge temp;
  for ( i = 0; i < G.numEdges; i++)
  {
      for ( j = i + 1; j < G.numEdges; j++)
      {
          if (edges[i].weight > edges[j].weight)
          {
              temp = edges[i];
              edges[i] = edges[j];
              edges[j] = temp;
          }
      }
  }
  
  printf("邊集數(shù)組根據(jù)權(quán)值排序之后的為:\n");
  for (i = 0; i < G.numEdges; i++)
  {
      printf("v%d - v%d = %d\n", edges[i].begin, edges[i].end, edges[i].weight);
  }
}

根據(jù)頂點f以及parent數(shù)組,可以找到當(dāng)前頂點的尾部下標(biāo); 幫助我們判斷兩點之間是否存在閉環(huán)問題

int Find(int *parent, int f)
{
  while ( parent[f] > 0)
  {
      f = parent[f];
  }
  return f;
}

kruskal算法

void MiniSpanTree_Kruskal(MGraph G){
  int sum = 0;
  /* 定義一數(shù)組用來判斷邊與邊是否形成環(huán)路,用來記錄頂點間的連接關(guān)系. 通過它來防止最小生成樹產(chǎn)生閉環(huán);*/
  int parent[MAXVEX];
  //邊表數(shù)組
  Edge edges[MAXVEX];
  int k = 0;
  //根據(jù)鄰接矩陣生成邊表數(shù)組
  for (int i = 0; i<G.numNodes; i++) {
      for (int j = i+1; j<G.numNodes; j++) {
          if (G.arc[i][j]<INFINITYC) {
              edges[k].begin = i;
              edges[k].end = j;
              edges[k].weight = G.arc[i][j];
              k++;
          }
      }
  }
  
  //將邊表數(shù)組排序
  sort(edges, G);
  
  //初始化parent 值為0
  for (int i = 0; i<MAXVEX; i++) {
      parent[i] = 0;
  }
  int n,m;
  for (int i = 0; i<G.numEdges; i++) {
      n = Find(parent, edges[i].begin);
      m = Find(parent, edges[i].end);
      if (n != m) {
          parent[n] = m; //更新parent數(shù)組
          //非閉環(huán)情況下,且根據(jù)權(quán)值從小到大的排序邊表數(shù)組,此時的邊i是沒有加入到生成樹的最小邊
          sum  += edges[i].weight; //邊i加入的生成樹
      }
  }
  printf("sum = %d\n",sum);
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容