数据结构图之二(最小生成树--普里姆算法)

【1】什么是最小生成树?

对于连通的带权图(连通网)G,其生成树也是带权的。

生成树T各边的权值总和称为该树的权。

权最小的生成树称为G的最小生成树(Minimum SpannirngTree)。简记为MST。

注意:最小是指权值最小

一个连通图的生成树是一个极小的连通子图,它包含全部的顶点,但只有足以构成一棵树的n-1条边。

求最小生成树有两种算法:普里姆算法和克鲁斯卡尔算法

不好理解?看不懂?能通俗点不?看个实例哈:

假设你是电信实施工程师,需要为一个镇的九个村庄架设通信网络做设计。

村庄位置大致如下图,之间连线的数字表示村与村间的可通达直线距离。

你们领导要求你必须用最小的成本完成这次任务。你说怎么办?

好,这就是很现实的一个最小生成树案例。且听下面详解。

【2】普里姆算法

利用 普里姆算法 要解决如上问题,首先我们构造图的邻接矩阵。如下图所示:

注意:实际中我们用65535来代表无穷大。

关于普里姆算法以及讲解如下图

针对上面我们遇到的实际案例,普里姆算法执行循环过程如下图

每次所选最小边分别如 图1-图8 所示

最后用所有边把各个顶点连通也就是所谓的最小生成树。

【3】普里姆算法的实现

实现代码如下:

#include <iostream>
#include "SeqList.h"
#include <iomanip>
using namespace std;

#define  INFINITY  65535

template<class NameType, class DistType>
class Graph
{
private:
    SeqList<NameType> Vertices;
    DistType **Edges;
    int nVer, nEdges;

public:
    Graph() 
        : Edges(NULL)
        , nEdges(0)
        , nVer(0)
    {}
    ~Graph()
    {}

public:

    istream & operator>>(istream &in)
    {
        int v, u, value;
        int i, j;
        NameType item;
        cout << "请输入顶点的个数: " << endl;
        in >> nVer;
        cout << "请输入顶点的数据信息: " << endl;
        for (i = 0; i < nVer; ++i)
        {
            in >> item;
            Vertices.push_back(item);    // 保存全部顶点
        }
        /////二维数组的创建并初始化
        Edges = new DistType*[nVer]; // DistType *ar[10];
        for (i = 0; i < nVer; ++i)
        {
            Edges[i] = new DistType[nVer];
            for (j = 0; j < nVer; ++j)
            {
                Edges[i][j] = 0;
            }
        }
        cout << "请输入边的个数: " << endl;
        in >> nEdges;
        cout << "请输入边的信息:" << endl;
        for (i = 0; i < nEdges; ++i)
        {
            in >> v >> u >> value;
            Edges[v][u] = value;
            Edges[u][v] = value;
        }
        return in;
    }
    ostream & operator<<(ostream &out) const
    {
        int i, j;
        out << "顶点信息 " << endl;
        for (i = 1; i <= nVer; ++i)
        {
            out << Vertices[i] << setw(5);
        }
        out << endl;
        out << "矩阵信息:" << endl;
        out << setw(10);
        for (i = 1; i <= nVer; ++i)
        {
            out << Vertices[i] << setw(5);
        }
        out << endl;
        for (i = 0; i < nVer; ++i)
        {
            out << Vertices[i+1] << setw(5);
            for (j = 0; j < nVer; ++j)
            {
                if (0 == Edges[i][j] && i != j)
                    Edges[i][j] = INFINITY;
                cout << Edges[i][j] << setw(5);
            }
            out << endl;
        }
        out << endl;

        return out;
    }
    //  图采用邻接矩阵存储  最小生成树 普里姆算法
    void MiniSpanTree()  
    {  
        int min = 0, i = 0, j = 0, k = 0;
        int* adjvex = new  int[nVer];    // 保存相关顶点下标  
        int* lowcost = new int[nVer];    // 保存相关顶点间边的权值  
        lowcost[0] = 0;         // 初始化第一个权值为0,即V0已加入生成树
        //lowcost的值为0,在这里就是此下标的顶点已经加入生成树    
        adjvex[0] = 0;          // 初始化第一个顶点下标为0  

        for (i = 1; i < nVer; ++i)  //循环除过下标为0外的全部顶点
        {  
            lowcost[i] = Edges[0][i];  // 将v0顶点与之有边的权值存入数组 
            adjvex[i] = 0;             // 并初始化都为v0的下标
        }  

        for (i = 1; i < nVer; ++i) 
        {  
            min = INFINITY;  // 初始化最小权值为常数,通常设置为不可能到达的数值
            k = 0;    // 复位

            for (j = 1; j < nVer; ++j)  
            {  
                // 如果两个顶点之间存在边有权值,不为0并且小于min  
                if (lowcost[j] != 0 && lowcost[j] < min)  
                {  
                    min = lowcost[j];  // 缓存最小值
                    k = j;    // 将当前最小值的下标缓存入k
                }
            }  
            cout << "("<< adjvex[k] << "," << k << ")" << endl;   //打印当前顶点边中权值最小的边  
            lowcost[k] = 0;                     // 将当前顶点的权值设为0,表示此顶点已经完成任务  

            for (j = 1; j < nVer; ++j)  // 循环所有节点
            {  
                if (lowcost[j] != 0 && Edges[k][j] < lowcost[j])  
                {  // 若下标为k顶点各边权值小于此前这些顶点未被加入生成树权值
                    lowcost[j] = Edges[k][j]; // 用较小者替换  
                    adjvex[j] = k;  // 将下标为k的顶点存入adjvex
                }  
            }  
        }

        delete []adjvex;
        delete []lowcost;
        adjvex = NULL;
        lowcost = NULL;
    }
};

template<class NameType, class DistType>
istream & operator>>(istream &in, Graph<NameType,DistType> &g)
{
    g >> in;
    return in;
}

template<class NameType, class DistType>
ostream & operator<<(ostream &out, const Graph<NameType,DistType> &g)
{
    g << out;
    return out;
}

void main()
{
    Graph<char, int> myg;
    cin >> myg;
    cout << "打印所有输入信息:" << endl;
    cout << myg << endl;
    cout << "最小生成树边信息如下:" << endl;
    myg.MiniSpanTree();
    cout << endl;
}

代码中所引用是头文件SeqList.h

#include <iostream>
#include <assert.h>
#include <typeinfo.h>
#include <string.h>
using namespace std;

#define     UM_QUIT     0
#define  UM_INPUTR   1
#define  UM_PRINT    2
#define  UM_INPUTH   3
#define  FINDVALUE   4
#define  UM_INSERT   5
#define  UM_REMOVE   6
#define  UM_SORT     7
#define  UM_REMOVEALL  8
#define  UM_REVERSE    9
#define  UM_ERASE      10
#define  UM_PVPOS      11

#define  MAXSIZE      10
#define  INCSIZE      5

template <class Type>
class SeqList
{
private:
    Type* m_pData;        //数据区域
    size_t m_nMaxSize;    //最大容量
    size_t m_nLen;        //元素个数

public:
    SeqList(size_t size = MAXSIZE);
    SeqList(const SeqList<Type>& sqList);
    SeqList<Type>& operator=(const SeqList<Type>& sqList);
    ~SeqList();

public:
    size_t GetMaxSize() const;
    void SetMaxSize(size_t nMaxSize) const;
    size_t GetLength() const;
    void SetLength(size_t nLen) const;

public:
    bool IsFull() const;
    bool IsEmpty() const;
    bool IsElement(const Type& Value);
    void Push_Back(const Type& Value);
    void Push_Front(const Type& Value);
    void At(const size_t pos);
    size_t FindPos(const Type& Value);
    void Insert(const size_t pos, const Type& Value);
    void Remove(const Type& Value);
    void Erase(const size_t pos);
    void RemoveAll(const Type& Value);
    void Sort();
    void Reverse();
    void Print() const;

private:
    bool IncSize();
    void Swap(const size_t leftpos, const size_t rightpos);
};
//基本属性函数
template <class Type>
size_t SeqList<Type>::GetMaxSize() const
{
    return m_nMaxSize;
}
template <class Type>
void SeqList<Type>::SetMaxSize(size_t nMaxSize) const
{
    m_nMaxSize = nMaxSize;
}
template <class Type>
size_t SeqList<Type>::GetLength() const
{
    return m_nLen;
}
template <class Type>
void SeqList<Type>::SetLength(size_t nLen) const
{
    m_nLen = nLen;
}
// 作用:扩充顺序表容量
// 当数据数量大于最大容量时,为顺序表增加容量
// 增加度量为INCSIZE
template <class Type>
bool SeqList<Type>::IncSize()
{
    Type* pNewDate = new Type[m_nMaxSize + INCSIZE + 1];
    if (NULL == pNewDate)
        return false;
    memcpy(pNewDate, m_pData, sizeof(Type) * (m_nLen + 1));
    m_pData = pNewDate;
    m_nMaxSize += INCSIZE;
    return true;
}
//交换数据
template <class Type>
void SeqList<Type>::Swap(const size_t leftpos, const size_t rightpos)
{
    Type temp = m_pData[leftpos];
    m_pData[leftpos] = m_pData[rightpos];
    m_pData[rightpos] = temp;
}
//默认复合构造函数
template <class Type>
SeqList<Type>::SeqList(size_t size = MAXSIZE)
{
    m_nMaxSize = size > MAXSIZE ? size : MAXSIZE;
    m_pData = new Type[m_nMaxSize + 1];
    assert(m_pData != NULL);
    m_nLen = 0;
}
//拷贝构造函数
template <class Type>
SeqList<Type>::SeqList(const SeqList<Type>& sqList)
{
    if (this != &sqList)
    {
        m_pData = new Type[sqList.m_nMaxSize + 1];
        if (m_pData != NULL)
        {
            memcpy(m_pData, sqList.m_pData, sizeof(Type) * (sqList.m_nLen + 1));
        }
        m_nMaxSize = sqList.m_nMaxSize;
        m_nLen = sqList.m_nLen;
    }
}
//赋值函数(思考什么时候调用赋值函数)
template <class Type>
SeqList<Type>& SeqList<Type>::operator=(const SeqList<Type>& sqList)
{
    m_nLen = sqList.m_nLen;
    m_nMaxSize = sqList.m_nMaxSize;
    memcpy(m_pData, sqList.m_pData, sizeof(Type) * (sqList.m_nLen + 1));
    return *this; 
}
//析构函数
template <class Type>
SeqList<Type>::~SeqList()
{
    if (m_pData != NULL)
    {
        delete []m_pData;
        m_pData = NULL;
    }
    m_nMaxSize = 0;
    m_nLen = 0;
}
//判满
template <class Type>
bool SeqList<Type>::IsFull() const
{
    return m_nLen == m_nMaxSize;
}
//判空
template <class Type>
bool SeqList<Type>::IsEmpty() const
{
    return 0 == m_nLen;
}
//是否是顺序表中元素
template <class Type>
bool SeqList<Type>::IsElement(const Type& Value)
{
    size_t pos = FindPos(Value);
    return 0 != pos;
}
//从顺序表末尾添加元素
template <class Type>
void SeqList<Type>::Push_Back(const Type& Value)
{
    if (IsFull() && !IncSize())    //注意写法用意
        return;
    m_pData[++m_nLen] = Value;
}
//从顺序表首部添加元素
template <class Type>
void SeqList<Type>::Push_Front(const Type& Value)
{
    if (IsFull() && !IncSize())
        return;
    for (size_t i = m_nLen; i > 0; --i)
    {
        m_pData[i + 1] = m_pData[i];
    }
    ++m_nLen;
    m_pData[1] = Value;
}
//输出对应索引处的数据
template <class Type>
void SeqList<Type>::At(const size_t pos)
{
    if (pos <= 0 || pos > m_nLen)
        cout << "输入下标有误!" << endl;
    else
        cout << pos << "位置的数据为:" << m_pData[pos] << endl; 
}
//查找某数据的索引
template <class Type>
size_t SeqList<Type>::FindPos(const Type& Value)
{
    m_pData[0] = Value;
    size_t i = m_nLen;
    while (m_pData[i] != Value)
    {
        --i;
    }
    return i;
}
//在索引处插入值
template <class Type>
void SeqList<Type>::Insert(const size_t pos, const Type& Value)
{
    if (pos <= 0 || pos > m_nLen + 1)
        return;
    if (IsFull() && !IncSize())
        return;

    ++m_nLen;
    for (size_t i = m_nLen; i > pos; --i)
    {
        m_pData[i] = m_pData[i-1];
    }
    m_pData[pos] = Value;
}
//移除某个值
template <class Type>
void SeqList<Type>::Remove(const Type& Value)
{
    if (IsElement(Value))
    {
        size_t pos  = FindPos(Value);
        Erase(pos);
    }
    else
    {
        cout << "输入数据不存在!" << endl;
    }
}
//按索引删除数据
template <class Type>
void SeqList<Type>::Erase(const size_t pos)
{
    if (pos <= 0 || pos > m_nLen)
    {
        cout << "输入错误" << endl;
    }
    else
    {
        for (size_t i = pos; i < m_nLen; ++i)
        {
            m_pData[i] = m_pData[i+1];
        }
        --m_nLen;
        cout << "删除成功!删除后结果如下:" << endl;
        Print();
    }
}
//删除全部(删除所有出现在顺序表中的某值)
template <class Type>
void SeqList<Type>::RemoveAll(const Type& Value)
{
    if (IsElement(Value))
    {
        for (size_t i = m_nLen; i > 0; --i)
        {
            if (m_pData[i] == Value)
                Remove(Value);
        }

        cout << "删除全部成功!删除后结果如下:" << endl;
        Print();
    }
    else
    {
        cout << "输入的数据不存在!" << endl;
    }
}
//顺序表数据排序
template <class Type>
void SeqList<Type>::Sort()
{
    if (m_nLen > 2)
    {
        size_t i = 0, j = 0;
        for (i = 2; i <= m_nLen; ++i)
        {
            if (m_pData[i] < m_pData[i - 1])
            {
                m_pData[0] = m_pData[i];
                j = i - 1;
                do 
                {
                    m_pData[j + 1] = m_pData[j];
                    --j;
                } while(m_pData[j] > m_pData[0]);
                m_pData[j + 1] = m_pData[0];
            }
        }
    }
}
//逆置顺序表
template <class Type>
void SeqList<Type>::Reverse()
{
    if (m_nLen > 2)
    {
        size_t i = 0, j = 0;
        for (i = 1, j = m_nLen; i < j; ++i, --j)
        {
            Swap(i, j);
        }
        cout << "逆置结果如下:" << endl;
        Print();
    }
}
//打印顺序表数据信息
template <class Type>
void SeqList<Type>::Print() const
{
    cout << "容量:" << m_nMaxSize << endl;
    cout << "元素的个数:" << m_nLen << endl;
    cout << "数据如下:" << endl;
    for (size_t i = 1; i <= m_nLen; ++i)
    {
        cout << m_pData[i] << " ";
    }
    cout << endl;
}


你可能感兴趣的:(数据结构之图)