1. 程式人生 > >資料結構 筆記:最小生成樹(prim)

資料結構 筆記:最小生成樹(prim)

運營商的挑戰

-在下圖標出的城市間架設一條通訊線路

要求:

·任意兩個城市間都能夠通訊

·將架設成本呢將至最低

 

如何在圖中選擇n-1條邊使得n個頂點間兩兩可達,並且這n-1條邊的權值之和最小

最小生成樹

-僅使用圖中的n-1條邊連線圖中的n個頂點

-不能使用產生迴路的邊

-個邊上的權值的總和達到最小

最小生成樹演算法步驟

-選擇某一頂點V0作為起始頂點,是的T={V0},F={V1,V2,...Vn},E={ }

-每次選擇一條邊,這條邊是所有(u,v)中權值最小的邊,且u屬於T,v屬於F

-修改T,F,E:

T = T + {v} ,F = F - {v}, E = E + {(u,v)}

-當F != NULL是,切(u,v)存在,轉2;否則,結束

 最小生成樹演算法的原材料

型別 變數 用途
Array<bool> mark 標記頂點所屬的集合(T or F)
Array<E> cost 記錄T集合到F集合中頂點的最小權值
Array<int> adjVex 記錄cost中權值的對應頂點
Queue<Edge> ret 記錄最小生成樹中的邊

注意事項

-最小生成樹僅針對無向圖有意義

-必須判斷圖物件是否能夠看做無向圖

圖型別(Graph)中的新增成員函式

-virtual bool isAdjacent (int i,int j) = 0;

-bool assUndirected();

·判斷當前的有向圖是否能夠看做無向圖

    bool isAdjacent(int i ,int j)
    {
        return (0 <= i) && ( i < vCount()) && ( 0 <= j) && (j < vCount()) && (m_list.get(i)->edge.find(Edge<E>(i,j)) >=0);
    }
    bool asUndirected()
    {
        bool ret = true;

        for(int i = 0;i<vCount() ; i++)
        {
            for(int j = 0;j<vCount();j++)
            {
                if(isAdjacent(i,j))
                {
                    ret = ret && isAdjacent(j,i) && (getEdge(i,j) == getEdge(j,i));
                }
            }
        }
        return ret;
    }

    SharedPointer <Array <Edge <E> >> prim( const E& LIMIT,const bool MINIMUM = true)
    {
        LinkQueue<Edge<E>> ret;

        if(asUndirected())
        {
            DynamicArray<int> adjVex(vCount());
            DynamicArray<bool> mark(vCount());
            DynamicArray<E> cost(vCount());
            SharedPointer<Array<int>> aj = NULL;
            bool end = false;
            int v = 0;

            for(int i = 0;i<vCount() ; i++)
            {
                adjVex[i] = -1;
                mark[i] = false;
                cost[i] = LIMIT;
            }

            mark[v] = true;

            aj = getAdjacent(v);

            for(int j = 0;j < aj->length();j++)
            {
                cost[(*aj)[j]] = getEdge(v,(*aj)[j]);
                adjVex[(*aj)[j]] = v;
            }

            for(int i = 0;(i<vCount()) && !end;i++)
            {
                E m = LIMIT;
                int k = -1;

                for(int j = 0;j<vCount();j++)
                {
                    if( !mark[j] && (MINIMUM ?(cost[j] < m) :(cost[j] > m)))
                    {
                        m = cost[j];
                        k = j;
                    }
                }

                end = (k == -1);

                if(!end)
                {
                    ret.add(Edge<E>(adjVex[k],k,getEdge(adjVex[k],k)));

                    mark[k] = true;

                    aj = getAdjacent(k);

                    for(int j = 0;j<aj->length();j++)
                    {
                        if(!mark[(*aj)[j]] && (MINIMUM ? (getEdge(k,(*aj)[j]) < cost[(*aj)[j]]):(getEdge(k,(*aj)[j]) > cost[(*aj)[j]])))
                        {
                            cost[(*aj)[j]] = getEdge(k,(*aj)[j]);
                            adjVex[(*aj)[j]] = k;
                        }
                    }
                }
            }
        }
        else
        {
            //丟擲異常
        }

        if(ret.length() != (vCount() -1))
        {
            //丟擲異常
        }
        return toArray(ret);
    }

總結:

-最小生成樹使得頂點間的連通代價最小

-Prim演算法通過頂點的動態標記尋找最小生成樹

-Prim演算法的關鍵是集合概念的援用(T 集合,F 集合)

-利用Prim演算法的思想也能尋找圖的“最大生成樹”