【數據結構】76_Pirm 法生成最小生成樹

運營商的挑戰

  • 在下圖標出的城市間架設一條通訊線路
  • 要求:ios

    • 任意兩個城市間都可以通訊
    • 將假設成本降至最低

image.png

image.png


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

最小生成樹

  • 僅使用圖中的 n-1 條邊鏈接圖中的 n 個頂點
  • 不能使用產生迴路的邊
  • 各邊上的權值的總和達到最小

尋找最小生成樹

image.png

示例分析:手工尋找最小生成樹

prim 手工查找.png

最小生成樹算法步驟(Prim)

  1. 選擇某一頂點 v0 做爲起始頂點,使得 T = {v0}, F={v1,v2,...,vn}, E={}
  2. 每次選擇一條邊,這條邊是全部 (u, v) 中權值最小的邊,且 u ∈ T, v ∈ F
  3. 修改 T, F, E : T = T + {v}, F = F - {v}, E = E + {(u,v)}
  4. 當 F != NULL 時,且 (u, v) 存在,轉2; 不然結束

最小生成樹的原材料

類型 變量 用途
Array<bool> mark 標記頂點所屬的集合
Array[E] cost 記錄T集合到F集合中頂點的最小權值
Array<int> adjVex 記錄 cost 中權值的對應頂點
Queue<Edge> ret 記錄最小生成樹中的邊
注: 如何 T 集合到 F 集合中同一個頂點的鏈接有多條邊,那麼選擇權值最小的鏈接

871853-20181001080356339-473627711.png

image.png

步驟描述:

1.png

2.png

3.png

4.png

注意事項

  • 最小生成樹僅針對無向圖有意義
  • 必須判斷圖對象是否可以看做無向圖
問題:什麼樣的有向圖可以看做無向圖

有向圖的任意兩頂點若存在鏈接,則兩點需互相可達且權值相等算法

圖類型 (Graph) 中新增成員函數

  • virtual bool isAdjacent(int i, int j) = 0;編程

    • 判斷在當前圖中頂點 i 到頂點 j 是否鄰接
  • bool asUndirected();ide

    • 判斷當前的有向圖是否可以看做無向圖
// 鄰接矩陣圖結構
bool isAdjacent(int i, int j) const override
{
    return (0 <= i) && (i < vCount()) && (0 <= j) && (j < vCount()) && (m_edges[i][j] != nullptr);
}

// 鄰接鏈表圖結構
bool isAdjacent(int i, int j) const override
{
    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() && ret; ++i)
    {
        for (int j=0; j<vCount() && ret; ++j)
        {
            if (isAdjacent(i, j))
            {
                ret = isAdjacent(j, i) && (getEdge(i, j) == getEdge(j, i));
            }
        }
    }

    return ret;
}

編程實驗:最小生成樹算法

文件:Graph.h函數

#ifndef GRAPH_H
#define GRAPH_H

#include "Object.h"
#include "SharedPointer.h"
#include "DynamicArray.h"
#include "LinkQueue.h"
#include "LinkStack.h"

namespace DTLib
{

template <typename E>
struct Edge : public Object
{
    int b;
    int e;
    E data;

    Edge(int i=-1, int j=-1)  : b(i), e(j)
    {
    }

    Edge(int i, int j, const E &value) : b(i), e(j), data(value)
    {
    }

    bool operator == (const Edge &obj)
    {
        return (b == obj.b) && (e == obj.e);
    }

    bool operator != (const Edge &obj)
    {
        return !(*this == obj);
    }
};

template <typename V, typename E>
class Graph : public Object
{
public:
    virtual V getVertex(int i) const = 0;
    virtual bool getVertex(int i, V &value) const = 0;
    virtual bool setVertex(int i, const V &value) = 0;
    virtual SharedPointer<Array<int>> getAdjacent(int i) const = 0;
    virtual bool isAdjacent(int i, int j) const = 0;
    virtual E getEdge(int i, int j) const = 0;
    virtual bool getEdge(int i, int j, E &value) const = 0;
    virtual bool setEdge(int i, int j, const E &value) = 0;
    virtual bool removeEdge(int i, int j) = 0;
    virtual int vCount() const = 0;
    virtual int eCount() = 0;
    virtual int OD(int i) = 0;
    virtual int ID(int i) = 0;
    virtual int TD(int i)
    {
        return OD(i) + ID(i);
    }

    bool asUndirected()
    {
        bool ret = true;

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

        return ret;
    }

    SharedPointer<Array<int>> BFS(int i)
    {
        DynamicArray<int> *ret = nullptr;

        if ((0 <= i) && (i < vCount()))
        {
            LinkQueue<int> q;
            LinkQueue<int> r;
            DynamicArray<bool> visited(vCount());

            for (int j=0; j<visited.length(); ++j)
            {
                visited[j] = false;
            }

            q.add(i);

            while (q.length() > 0)
            {
                int v = q.front();

                q.remove();

                if (!visited[v])
                {
                    SharedPointer<Array<int>> aj = getAdjacent(v);

                    for (int j=0; j<aj->length(); ++j)
                    {
                        q.add((*aj)[j]);
                    }

                    r.add(v);

                    visited[v] = true;
                }
            }

            ret = toArray(r);
        }
        else
        {
            THROW_EXCEPTION(InvalidParameterExcetion, "Parameter i is invalid ...");
        }

        return ret;
    }

#ifdef DFS_R
    SharedPointer<Array<int>> DFS(int i)  // 遞歸版深度優先遍歷
    {
        DynamicArray<int> *ret = nullptr;

        if ((0 <= i) && (i < vCount()))
        {
            LinkQueue<int> r;
            DynamicArray<bool> visited(vCount());

            for (int j=0; j<vCount(); ++j)
            {
                visited[j] = false;
            }

            DFP(i, visited, r);

            ret = toArray(r);
        }
        else
        {
            THROW_EXCEPTION(InvalidParameterExcetion, "Parameter i is invalid ...");
        }

        return ret;
    }
#else
    SharedPointer<Array<int>> DFS(int i)
    {
        DynamicArray<int> *ret = nullptr;

        if ((0 <= i) && (i < vCount()))
        {
            LinkStack<int> s;
            LinkQueue<int> r;
            DynamicArray<bool> visited(vCount());

            for (int j=0; j<visited.length(); ++j)
            {
                visited[j] = false;
            }

            s.push(i);

            while (s.size() > 0)
            {
                int v = s.top();

                s.pop();

                if (!visited[v])
                {
                    SharedPointer<Array<int>> aj = getAdjacent(v);

                    for (int j=aj->length()-1; j>=0; --j)
                    {
                        s.push((*aj)[j]);
                    }

                    r.add(v);

                    visited[v] = true;
                }
            }

            ret = toArray(r);
        }
        else
        {
            THROW_EXCEPTION(InvalidParameterExcetion, "Parameter i is invalid ...");
        }

        return ret;
    } 
#endif

    SharedPointer<Array<Edge<E>>> prim(const E &LIMIT, 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 = nullptr;
            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 i=0; i<aj->length(); ++i)
            {
                cost[(*aj)[i]] = getEdge(v, (*aj)[i]);
                adjVex[(*aj)[i]] = 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 ? (m > cost[j]) : (m < cost[j])))
                    {
                        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
        {
            THROW_EXCEPTION(InvalidOpertionExcetion, "Prim operator is for undirected grap only ...");
        }

        if (ret.length() != (vCount() - 1))
        {
            THROW_EXCEPTION(InvalidOpertionExcetion, "No enough edge for prim operation ...");
        }

        return toArray(ret);
    }

protected:
    template <typename T>
    DynamicArray<T>* toArray(LinkQueue<T> &queue)
    {
        DynamicArray<T> *ret = new DynamicArray<T>(queue.length());

        if (ret != nullptr)
        {
            for (int i=0; i<ret->length(); ++i, queue.remove())
            {
                ret->set(i, queue.front());
            }
        }
        else
        {
            THROW_EXCEPTION(NoEnoughMemoryException, "No memory to create ret obj ...");
        }

        return ret;
    }

#ifdef DFS_R
    void DFP(int i, DynamicArray<bool> &visited, LinkQueue<int>& queue)
    {
        if (!visited[i])
        {
            queue.add(i);

            visited[i] = true;

            SharedPointer<Array<int>> aj = getAdjacent(i);

            for (int j=0; j<aj->length(); ++j)
            {
                DFP((*aj)[j], visited, queue);
            }
        }
    }
#endif
};

}

#endif // GRAPH_H

文件:main.cppthis

#include <iostream>
#include "MatrixGraph.h"
#include "ListGraph.h"

using namespace std;
using namespace DTLib;

template< typename V, typename E >
Graph<V, E>& GraphEasy()
{
    static MatrixGraph<4, V, E> g;

    g.setEdge(0, 1, 1);
    g.setEdge(1, 0, 1);

    g.setEdge(0, 2, 3);
    g.setEdge(2, 0, 3);

    g.setEdge(1, 2, 1);
    g.setEdge(2, 1, 1);

    g.setEdge(1, 3, 4);
    g.setEdge(3, 1, 4);

    g.setEdge(2, 3, 1);
    g.setEdge(3, 2, 1);

    return g;
}

template< typename V, typename E >
Graph<V, E>& GraphComplex()
{
    static ListGraph<V, E> g(9);

    g.setEdge(0, 1, 10);
    g.setEdge(1, 0, 10);

    g.setEdge(0, 5, 11);
    g.setEdge(5, 0, 11);

    g.setEdge(1, 2, 18);
    g.setEdge(2, 1, 18);

    g.setEdge(1, 8, 12);
    g.setEdge(8, 1, 12);

    g.setEdge(1, 6, 16);
    g.setEdge(6, 1, 16);

    g.setEdge(2, 3, 22);
    g.setEdge(3, 2, 22);

    g.setEdge(2, 8, 8);
    g.setEdge(8, 2, 8);

    g.setEdge(3, 8, 21);
    g.setEdge(8, 3, 21);

    g.setEdge(3, 6, 24);
    g.setEdge(6, 3, 24);

    g.setEdge(3, 7, 16);
    g.setEdge(7, 3, 16);

    g.setEdge(3, 4, 20);
    g.setEdge(4, 3, 20);

    g.setEdge(4, 5, 26);
    g.setEdge(5, 4, 26);

    g.setEdge(4, 7, 7);
    g.setEdge(7, 4, 7);

    g.setEdge(5, 6, 17);
    g.setEdge(6, 5, 17);

    g.setEdge(6, 7, 19);
    g.setEdge(7, 6, 19);

    return g;
}

void func1()
{
    cout << "func1: ---------------------" << endl;

    Graph<int, int>& g = GraphEasy<int, int>();

    SharedPointer< Array< Edge<int> > > sa = g.prim(65535);

    int w = 0;

    for(int i=0; i<sa->length(); i++)
    {
        w += (*sa)[i].data;

        cout << (*sa)[i].b << " " << (*sa)[i].e << " " << (*sa)[i].data << endl;
    }

    cout << "Weight: " << w << endl;
}

void func2()
{
    cout << "func2: ---------------------" << endl;

    Graph<int, int>& g = GraphComplex<int, int>();

    SharedPointer< Array< Edge<int> > > sa = g.prim(65535);

    int w = 0;

    for(int i=0; i<sa->length(); i++)
    {
        w += (*sa)[i].data;

        cout << (*sa)[i].b << " " << (*sa)[i].e << " " << (*sa)[i].data << endl;
    }

    cout << "Weight: " << w << endl;
}

void func3()
{
    cout << "func3: ---------------------" << endl;

    Graph<int, int>& g = GraphComplex<int, int>();

    SharedPointer< Array< Edge<int> > > sa = g.prim(0, false);

    int w = 0;

    for(int i=0; i<sa->length(); i++)
    {
        w += (*sa)[i].data;

        cout << (*sa)[i].b << " " << (*sa)[i].e << " " << (*sa)[i].data << endl;
    }

    cout << "Weight: " << w << endl;
}

int main()
{
    func1();

    func2();

    func3();

    return 0;
}

輸出:spa

func1: ---------------------
0 1 1
1 2 1
2 3 1
Weight: 3
func2: ---------------------
0 1 10
0 5 11
1 8 12
8 2 8
1 6 16
6 7 19
7 4 7
7 3 16
Weight: 99
func3: ---------------------
0 5 11
5 4 26
4 3 20
3 6 24
3 2 22
3 8 21
6 7 19
2 1 18
Weight: 161

小結

  • 最小生成樹使得頂點間的連通代價最小
  • Prim 算法經過頂點的動態標記尋找最小生成樹
  • Prim 算法的關鍵是集合概念的運用 (T集合, F集合)
  • 利用 Prim 算法的思想也能尋找圖的 "最大生成樹"

以上內容整理於狄泰軟件學院系列課程,請你們保護原創!3d

相關文章
相關標籤/搜索