算法 最小生成樹(Kruskal和Prim算法)

最小生成樹Kruskal算法:首先對邊的權(quán)值進(jìn)行從小到大排列旦装,每次從剩余的邊中選擇權(quán)值較小且邊的兩個頂點(diǎn)不在
同一個集合內(nèi)的邊(就是不會產(chǎn)生回路的邊)锁保,加到生成樹中禁偎,直到加入的邊為n-1為止
時間復(fù)雜度:m 邊數(shù) n頂點(diǎn)數(shù) 對邊進(jìn)行快速排序是O(MlogM) 在m條邊中選擇n-1條邊的是O(MlogN); 故時間
復(fù)雜度為O(MlogM + MlogN);通常M比N大很多杀怠,故時間復(fù)雜度為O(MlogM);

#pragma mark - - 最小生成樹 Kruskal 算法
struct edge {
    int u; // 頂點(diǎn)
    int v; // 頂點(diǎn)
    int w; // 權(quán)值
};

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; //基準(zhǔn)數(shù)
    struct edge t;
    
    while (i!=j) {
        // 從右往左找 直到找到一個小于基準(zhǔn)數(shù) 停下
        while (e[j].w >= temp && i<j) {
            j--;
        }
        
        // 從左往右找 直到找到一個大于基準(zhǔn)數(shù) 停下
        while (e[i].w <= temp && i<j) {
            I++;
        }
        // 如果不相等
        if (i<j) {
            //交換
            t =e[I];
            e[i] =e[j];
            e[j] = t;
        }
    }
    
    // 相等 基準(zhǔn)數(shù)歸位 將left 和 i交換
    t = e[left];
    e[left] = e[I];
    e[i] = t;
    
    
    quicksort(left, i-1);  // 繼續(xù)處理左邊的
    quicksort(i+1, right); // 繼續(xù)處理右邊的
 
}

// 并查集尋找祖先 函數(shù)
int getf(int v, int f[]) {
    if (f[v] == v) {
        return v;
    }else {
        // 路徑壓縮
        f[v] = getf(f[v], f);
        return f[v];
    }
}

int merge(int v,int u,int f[]) {
    int t1;
    int t2;
    
    t1 = getf(v, f);
    t2 = getf(u, f);
    
    if (t1!=t2) {
        f[t2] =t1;
        return 1;
    }
    
    return 0;
}

-(void)test1 {
    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;
    // 初始化 邊數(shù)據(jù)
    for (int i=1; i<=m; i++) {
        struct edge t;
        t.u = s[i][0];
        t.v = s[i][1];
        t.w = s[i][2];
        e[i] = t;

    }
    // 快速排序 邊數(shù)據(jù)
    quicksort(1, m);
    
    // 初始化 并查集
    int f[10];
    for (int i=1; i<=n; i++) {
        f[i] = I;
    }
    
    //Kruskal算法 核心
    int sum=0;
    int count=0;
    // 從小到大 枚舉每一條邊
    for (int i=1; i<=m; i++) {
        if (merge(e[i].u, e[i].v, f)) {
            count ++;
            sum += e[i].w;
            printf("{%d,%d,%d} ",e[i].u,e[i].v,e[i].w);
        }
        
        if (count==n-1) {
            break ;
        }
    }
    
    printf("\n\n%d\n\n",sum);
    
}

Prim 算法:
1.從任意一個頂點(diǎn)開始構(gòu)造樹恕汇,假設(shè)從頂點(diǎn)1開始零酪。首先將頂點(diǎn)1加入生成樹中冒嫡,用一個一維數(shù)組Book來標(biāo)記哪些頂點(diǎn)
已經(jīng)加入生成樹
2.用數(shù)組dis記錄生成樹到各個頂點(diǎn)的距離。最初生成樹中只有1個頂點(diǎn)四苇。有直連邊時,數(shù)組dis中存儲的就是1號頂點(diǎn)
到該頂點(diǎn)的邊的權(quán)值方咆,沒有直連邊的時候就是無窮大月腋,即初始化dis數(shù)組
3.從數(shù)組dis中選出離生成樹最近的頂點(diǎn)(假設(shè)這個頂點(diǎn)為j)加入到生成樹中(Book[j]==1)。然后以j為中間點(diǎn)瓣赂,
更新生成樹到每一個非樹頂點(diǎn)的距離(松弛)榆骚,即如果dis[k] > e[j][k]則更新為dis[k]=e[j][k](j在生成樹
中,e[j][k]就是表示非樹頂點(diǎn)k到生成樹的以j為中間點(diǎn)的距離煌集,dis[k]表示非樹頂點(diǎn)k到生成樹的以其他頂點(diǎn)(
頂點(diǎn)j除外)為中間點(diǎn)的最短距離)
4.重復(fù)上述操作妓肢,直到生成樹中有n個頂點(diǎ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;
    // 初始化 邊數(shù)據(jù)
    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數(shù)組
    int dis[10];
  
    for (int i=0; i<10; i++) {
        dis[i] = 9999;
    }
    // 初始化book數(shù)組 標(biāo)記一個頂點(diǎn)是否已經(jīng)加入到生成樹
    int book[10] = {0};
    // 假設(shè)從頂點(diǎn)1開始
    dis[1] =0;
    
    // 記錄總距離
    int sum=0;
    
    // Prim算法 核心
    for (int j=1; j<=n; j++) {
        // 較小值
        int min =inf;
        // 較小點(diǎn)
        int u=0;
        // 找到最小值
        for (int i=1; i<=n; i++) {
            if (book[i]==0 && dis[i]<min) {
                min = dis[I];
                u=I;
            }
        }
        
        book[u] =1;
        sum+=dis[u];
        
        // 掃描當(dāng)前頂點(diǎn)u所有邊 再以u為中間點(diǎn),更新生成樹到每一個非樹頂點(diǎn)的距離
        for (int k=1; k<=n; k++) {
            if (dis[k] > z[u][k] && book[k]==0) {
                dis[k] = z[u][k];
            }
        }
       
    }
    
    printf("\n最短距離為%d\n\n",sum);
   
    
}

優(yōu)化方向:
1.dis[]最小值 可以用堆排序
2.掃描當(dāng)前頂點(diǎn)u所有邊 再以u為中間點(diǎn)苫纤,更新生成樹到每一個非樹頂點(diǎn)的距離碉钠,這塊可以用鄰接表

#pragma mark - - 優(yōu)化 Prim 算法
int size=6;
int dis[7];
int h[7]; //保存堆數(shù)據(jù) h[i]=t 表示頂點(diǎn)t
int pos[7]; // pos用來存儲每個頂點(diǎn)在堆中的位置 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號點(diǎn)加入生成樹
    book[1] =1;
    count++;
    // 初始化dis數(shù)組,這里是1號頂點(diǎn)到其余各個頂點(diǎn)的初始距離
    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);
    }
    
    //先彈出一個堆頂元素 因?yàn)榇藭r堆頂是1號頂點(diǎn)
    pop();
    
    while (count<n) {
        int j=pop();
        book[j]=1;
        count++;
        sum+=dis[j];
        // 掃描當(dāng)前頂點(diǎn)j的所有的邊 再以j為中間頂點(diǎn) 進(jìn)行松弛
        k =first[j];
        while (k!=-1) {
            if (book[v[k]]==0 && dis[v[k]] > w[k]) {
                dis[v[k]] =w[k];
                siftup(pos[v[k]]);
            }
            k=next[k];
        }
        
    }
    
    printf("\n最短距離為%d\n\n",sum);
    
    
}

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末卷拘,一起剝皮案震驚了整個濱河市喊废,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌栗弟,老刑警劉巖污筷,帶你破解...
    沈念sama閱讀 218,386評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異乍赫,居然都是意外死亡瓣蛀,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,142評論 3 394
  • 文/潘曉璐 我一進(jìn)店門雷厂,熙熙樓的掌柜王于貴愁眉苦臉地迎上來惋增,“玉大人,你說我怎么就攤上這事罗侯∑饕福” “怎么了?”我有些...
    開封第一講書人閱讀 164,704評論 0 353
  • 文/不壞的土叔 我叫張陵钩杰,是天一觀的道長纫塌。 經(jīng)常有香客問我,道長讲弄,這世上最難降的妖魔是什么措左? 我笑而不...
    開封第一講書人閱讀 58,702評論 1 294
  • 正文 為了忘掉前任,我火速辦了婚禮避除,結(jié)果婚禮上怎披,老公的妹妹穿的比我還像新娘胸嘁。我一直安慰自己,他們只是感情好凉逛,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,716評論 6 392
  • 文/花漫 我一把揭開白布性宏。 她就那樣靜靜地躺著,像睡著了一般状飞。 火紅的嫁衣襯著肌膚如雪毫胜。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,573評論 1 305
  • 那天诬辈,我揣著相機(jī)與錄音酵使,去河邊找鬼。 笑死焙糟,一個胖子當(dāng)著我的面吹牛口渔,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播穿撮,決...
    沈念sama閱讀 40,314評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼缺脉,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了混巧?” 一聲冷哼從身側(cè)響起枪向,我...
    開封第一講書人閱讀 39,230評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎咧党,沒想到半個月后秘蛔,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,680評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡傍衡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,873評論 3 336
  • 正文 我和宋清朗相戀三年深员,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片蛙埂。...
    茶點(diǎn)故事閱讀 39,991評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡倦畅,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出绣的,到底是詐尸還是另有隱情叠赐,我是刑警寧澤,帶...
    沈念sama閱讀 35,706評論 5 346
  • 正文 年R本政府宣布屡江,位于F島的核電站芭概,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏惩嘉。R本人自食惡果不足惜罢洲,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,329評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望文黎。 院中可真熱鬧惹苗,春花似錦殿较、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,910評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽私恬。三九已至鳞陨,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間通铲,已是汗流浹背儡首。 一陣腳步聲響...
    開封第一講書人閱讀 33,038評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留偏友,地道東北人蔬胯。 一個月前我還...
    沈念sama閱讀 48,158評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像位他,于是被迫代替她去往敵國和親氛濒。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,941評論 2 355