Dijkstra 最短路径

Dijkstra 最短路径:针对有向图,不支持负权值

// 图的相邻矩阵表示方法,还要用到最小值堆
#include <iostream>
#include <queue>
#define UNVISITED 0
#define VISITED 1
#define INFINITE 9999    //设置最大值
#define N 5             // 定义图的顶点数
using namespace std;

//GraphMatrix.h

//Edge
class Edge
{
public:
    int weight;  // Edge weight
    int from;
    int to; // form To to
    Edge()
    {
        from = -1;
        to = -1;
        weight = 0;
    };								// 构造函数
    Edge(int f,int t,int w)     			// 构造函数
    {
        from=f;
        to=t;
        weight=w;
    }
    bool operator < (const Edge &arg)
    {
        return (this->weight < arg.weight);
    };
    bool operator == (const Edge &arg)
    {
        return (this->weight == arg.weight);
    };
    bool operator > (const Edge &arg)
    {
        return (this->weight > arg.weight);
    };
    bool operator <= (const Edge &arg)
    {
        return (this->weight <= arg.weight);
    };
    bool operator >= (const Edge &arg)
    {
        return (this->weight >= arg.weight);
    };
};

// 图基类
class Graph
{
public:
    int numVertex;             //图的顶点的个数
    int numEdge;				//图的边的数目
    int *Mark;					/*Mark指针指向保存有图的顶点的标志位的数组,标志位用来标记某顶点是否被访问过*/
    int *Indegree;				//Indegree指针指向保存有图的顶点的入度的数组
    Graph(int numVert)   		//构造函数
    {
        numVertex = numVert;      //确定图的顶点的个数
        numEdge = 0;				//确定图的边的数目
        Indegree = new int[numVertex]; /*为保存图的顶点的入度申请数组,Indegree为数组指针*/
        Mark = new int[numVertex];	 /*为图的顶点的标志位申请数组,Mark为数组指针*/

        for (int i = 0; i < numVertex; i ++)   	/*确定图的顶点的标志位和入度,即所有顶点的标志位初始化为未被访问过,入度初始化为0*/
        {
            Mark[i] = UNVISITED;
            Indegree[i] = 0;
        }
    };
    ~Graph()  				//析构函数
    {
        delete [] Mark;
        delete [] Indegree;
    };
    virtual Edge FirstEdge(int oneVertex)  	// 返回与顶点oneVertex相关联的第一条边
    {
        Edge myEdge;
        myEdge.from = oneVertex;
        myEdge.to = -1;
        return myEdge;
    };
    virtual Edge NextEdge(Edge preEdge)        // 返回与边PreEdge有相同关联顶点的下一条边
    {
        return preEdge;
    };
    int VerticesNum()  		//返回图的顶点个数
    {
        return numVertex;
    };
    int EdgesNum()  			//返回图的边数
    {
        return numEdge;
    };
    int FromVertex(Edge oneEdge)    // 返回oneEdge的始点
    {
        return oneEdge.from;
    };
    int ToVertex(Edge oneEdge)  	// 返回oneEdge的终点
    {
        return oneEdge.to;
    };
    int Weight(Edge oneEdge)  		// 返回oneEdge的权值
    {
        return oneEdge.weight;
    };
    bool IsEdge(Edge oneEdge)   	//如果oneEdge是边则返回TRUE,否则返回FALSE
    {
        if (oneEdge.weight > 0 && oneEdge.weight < INFINITE && oneEdge.to >= 0)
            return true;
        else
            return false;
    };
    virtual void setEdge(int from, int to, int weight) = 0;
    virtual void delEdge(int from, int to) = 0;
};


// 图的相邻矩阵表示法
class Graphm:public Graph
{
private:
    int **matrix;				//指向相邻矩阵的指针
public:
    void IniGraphm(Graphm *Graphm, int A[N][N]); // 初始化
    void DFS(Graph &G, int V);			// 深度优先搜索
    void BFS(Graph &G, int V);			// 广度优先搜索
    void Visit(Graph &G, int V);		// 访问

public:
    Graphm(int numVert):Graph(numVert)   //构造函数
    {
        int i, j;			//i, j作为for循环中的计数器
        matrix = (int **)new int*[numVertex]; /*申请matrix数组,该数组有numVertex个元素,每个元素是整型指针类型*/

        for (i = 0; i < numVertex; i ++)		/*matrix数组的每个元素,都指向一个具有numVertex个元素的数组*/
            matrix[i] = new int[numVertex];
        for (i = 0; i < numVertex; i++)       /*相邻矩阵的所有元素都初始化为0,如果矩阵元素matrix[i][j]不为0,则表明顶点i与顶点j之间有一条边,边的权即为matrix[i][j]的值*/
            for (j = 0; j < numVertex; j ++)
                matrix[i][j] = 0;
    };
    ~Graphm()  							//析构函数
    {
        for (int i = 0; i < numVertex; i ++)
            delete [] matrix[i];			//释放每个matrix[i]申请的空间
        delete [] matrix;				//释放matrix指针指向的空间
    };
    Edge FirstEdge(int oneVertex)   	//返回顶点oneVertex的第一条边
    {
        Edge myEdge;						//边myEdge将作为函数的返回值
        myEdge.from = oneVertex;			//将顶点oneVertex作为边myEdge的始点
        for (int i = 0; i < numVertex; i ++)
        {
            /* 下面寻找第一个使得matrix[oneVertex][i]不为0的i值,
            										那么边(oneVertex,i)或者弧<oneVertex,i>即为顶点oneVertex
            										的第一条边,将顶点i作为边myEdge的终点边myEdge的权为
            	矩阵元素matrix[oneVertex][i]的值*/
            if (matrix[oneVertex][i] != 0)
            {
                myEdge.to = i;
                myEdge.weight = matrix[oneVertex][i];
                break;
            }
        }
        return myEdge;
        /*如果找到了顶点oneVertex的第一条边,则返回的myEdge确实是一条边;如果没有找到顶点oneVertex的第一条边,则myEdge的成员变量to为-1,根据IsEdge函数判断可知myEdge不是一条边*/
    };
    Edge NextEdge(Edge preEdge)    //返回与边PreEdge有相同关联顶点的下一条边
    {
        Edge myEdge;
        myEdge.from=preEdge.from; /*将边myEdge的始点置为与上一条边preEdge的始点相同*/
        if(preEdge.to<numVertex)
        {
            //如果preEdge.to+1>=numVertex,那么就不存在下一条边了
            for(int i=preEdge.to+1; i<numVertex; i++)
            {
                /*寻找下一个使得//matrix[preEdge.from][i]不为0的i值,那么(preEdge.from,i)或者<preEdge.from,i>即为顶点preEdge.from的下一条边*/
                if(matrix[preEdge.from][i]!=0)
                {
                    myEdge.to=i;
                    myEdge.weight=matrix[preEdge.from][i];
                    break;
                }
            }
        }
        return myEdge; /*如果找到了顶点preEdge.from的下一条边,则返回的myEdge确实是一条边;如果没有找到顶点preEdge.from的下一条边,则myEdge的成员变量to为-1,
		根据IsEdge函数判断可知myEdge不是一条边*/
    }

    void setEdge(int from, int to, int weight)   	//为图设定一条边
    {
        if (matrix[from][to] <= 0)      //如果matrix[from][to]<=0,则边(from,to)或者<from,to>将是新增的一条边,否则该边已经存在(现在只是对该边进行修改)
        {
            numEdge ++;
            Indegree[to] ++;
        }
        matrix[from][to] = weight;
    };

    void delEdge(int from,int to)        //删除图的一条边
    {
        if(matrix[from][to]>0)
        {
            /*如果matrix[from][to]>0,则边(from,to)或者<from,to>确实存在,否则该边实际上并不存在(从而不必对图的边数和顶点to的入度进行修改)*/
            numEdge--;
            Indegree[to]--;
        }
        matrix[from][to]=0;
    }
};

// 函数功能:显示顶点
void Graphm::Visit(Graph &G, int v)
{
    cout << 'V' << v <<" ";
}


// 函数功能:初始化图
void Graphm::IniGraphm(Graphm *Graphm, int A[N][N])
{
    for (int i = 0; i < N; i ++)
    {
        for (int j = 0; j < N; j ++)
        {
            if (A[i][j] > 0)
                Graphm->setEdge(i, j, A[i][j]);
        }
    }
}

/******************** 代码5.17  堆的类定义和筛选法 ******************/
#define FALSE 0
#define TRUE 1

template <class T>
class MinHeap   							//最小堆类定义
{
private:
    T* heapArray;							//存放堆数据的数组
    int CurrentSize;						//当前堆中元素数目
    int MaxSize;							//堆所能容纳的最大元素数目
    void swap(int pos_x, int pos_y);		//交换位置x和y的元素
    void BuildHeap();						//建堆
public:
    MinHeap(const int n);					//构造函数,n表示初始化堆的最大元素数目
    virtual ~MinHeap()
    {
        delete []heapArray;
    }; 	//析构函数
    bool isLeaf(int pos) const;		 	    //如果是叶结点,返回TRUE
    int leftchild(int pos) const;		 	  	//返回左孩子位置
    int rightchild(int pos) const;				//返回右孩子位置
    int parent(int pos) const;					//返回父结点位置
    bool Remove(int pos, T& node);			//删除给定下标的元素
    bool Insert(const T& newNode);			//向堆中插入新元素newNode
    T& RemoveMin();					//从堆顶删除最小值
    void SiftUp(int position);			 	//从position向上开始调整,使序列成为堆
    void SiftDown(int left);		  //筛选法函数,参数left表示开始处理的数组下标
    bool isEmpty()
    {
        return ((CurrentSize)?false:true);
    };

};

template<class T>
MinHeap<T>::MinHeap(const int n)
{
    if(n<=0)
        return;
    CurrentSize=0;
    MaxSize=n;							//初始化堆容量为n
    heapArray=new T[MaxSize];			//创建堆空间
    //此处进行堆元素的赋值工作
    BuildHeap();
}
template<class T>
bool MinHeap<T>::isLeaf(int pos) const
{
    return (pos>=CurrentSize/2)&&(pos<CurrentSize);
}

template<class T>
void MinHeap<T>::BuildHeap()
{
    for (int i=CurrentSize/2-1; i>=0; i--) 		//反复调用筛选函数
        SiftDown(i);
}

template<class T>
int MinHeap<T>::leftchild(int pos) const
{
    return 2*pos+1;						//返回左孩子位置
}

template<class T>
int MinHeap<T>::rightchild(int pos) const
{
    return 2*pos+2;						//返回右孩子位置
}

template<class T>
int MinHeap<T>::parent(int pos) const
{
    return (pos-1)/2;						//返回父结点位置
}

template <class T>
bool MinHeap<T>::Insert(const T& newNode)   //向堆中插入新元素newNode
{
    if(CurrentSize==MaxSize)				//堆空间已经满
        return FALSE;
    heapArray[CurrentSize]=newNode;
    SiftUp(CurrentSize);					//向上调整
    CurrentSize++;
    return TRUE;
}

template<class T>
void MinHeap<T>::SiftUp(int position)     //从position向上开始调整,使序列成为堆
{
    int temppos=position;
    T temp=heapArray[temppos];
    while((temppos>0)&&(heapArray[parent(temppos)]>temp))
    {
        heapArray[temppos]=heapArray[parent(temppos)];
        temppos=parent(temppos);
    }
    heapArray[temppos]=temp;
}

template<class T>
void MinHeap<T>::swap(int pos_x, int pos_y)     //交换位置x和y的元素
{
    T temp = heapArray[pos_x];
    heapArray[pos_x] = heapArray[pos_y];
    heapArray[pos_y] = temp;
}

template<class T>
T& MinHeap<T>::RemoveMin()	 					//从堆顶删除最小值
{
    if(CurrentSize==0)
    {
        cout<<"Can't Delete";
    }
    else
    {
        swap(0,--CurrentSize);						//交换堆顶和最后一个元素
        if(CurrentSize>1)
            SiftDown(0);							//从堆顶开始筛选
        return heapArray[CurrentSize];
    }
}

template<class T>
bool MinHeap<T>::Remove(int pos, T& node)      	// 删除给定下标的元素
{
    if ((pos < 0) || (pos >= CurrentSize))
        return false;
    node = heapArray[pos];
    heapArray[pos] = heapArray[--CurrentSize];		// 用最后的元素值替代删除位置的元素
    if (heapArray[parent(pos)] > heapArray[pos])
        SiftUp(pos);						// 当前元素小于父结点,需要上升调整
    else SiftDown(pos);						// 当前元素大于父结点,向下筛
    return true;
}

template <class T>
void MinHeap<T>::SiftDown(int left)
{
    //准备
    int i=left;							//标识父结点
    int j= leftchild (i);					//标识关键值较小的子结点
    T	temp=heapArray[i];			//保存父结点
    //过筛
    while(j<CurrentSize)
    {
        if((j<CurrentSize-1)&&(heapArray[j]>heapArray[j+1]))
            j++;						//j指向右子结点
        if(temp>heapArray[j])
        {
            heapArray[i]=heapArray[j];
            i=j;
            j=leftchild(j);				//向下继续
        }
        else break;
    }
    heapArray[i]=temp;
}

//[代码7.8] Dijkstra算法
class Dist         //定义Dist类,下面的Dijkstra算法和Floyd算法要用到
{
public:
    int index;      //顶点的索引值,仅Dijkstra算法会用到
    int length;     //顶点之间的距离
    int pre;       //路径最后经过的顶点
    Dist() {};
    ~Dist() {};

    bool operator < (const Dist & arg)
    {
        return (length < arg.length);
    }
    bool operator == (const Dist &arg)
    {
        return (length==arg.length);
    }
    bool operator > (const Dist &arg)
    {
        return (length>arg.length);
    }
    bool operator <=(const Dist &arg)
    {
        return (length<=arg.length);
    }
    bool operator >= (const Dist &arg)
    {
        return (length>=arg.length);
    }
};

//Dijkstra算法,其中参数G是图,参数s是源顶点,D是保存最短距离及其路径的数组
void Dijkstra(Graphm& G, int s, Dist* &D)
{
    D = new Dist[G. VerticesNum()];          	// D数组
    for (int i = 0; i < G.VerticesNum(); i++)   // 初始化Mark数组、D数组
    {
        G.Mark[i] = UNVISITED;
        D[i].index = i;
        D[i].length = INFINITE;
        D[i].pre = s;
    }
    D[s].length = 0;
    MinHeap<Dist> H(G.EdgesNum());       	// 最小值堆(minheap)
    H.Insert(D[s]);
    for (int i = 0; i < G.VerticesNum(); i++)
    {
        bool FOUND = false;
        Dist d;
        while (!H.isEmpty())
        {
            d = H.RemoveMin();
            if(G.Mark[d.index]==UNVISITED)                  //打印出路径信息
            {
                cout<< "vertex index: " <<d.index<<"   ";
                cout<< "vertex pre  : " <<d.pre  <<"   ";
                cout<< "V0 --> V" << d.index <<"  length    : " << d.length <<endl;
            }

            if (G.Mark[d.index] == UNVISITED)   //找到距离s最近的顶点
            {
                FOUND = true;
                break;
            }
        }
        if (!FOUND)
            break;
        int v = d.index;
        G.Mark[v] = VISITED;           		// 把该点加入已访问组
        // 因为v的加入,需要刷新v邻接点的D值
        for (Edge e = G.FirstEdge(v); G.IsEdge(e); e = G.NextEdge(e))
            if (D[G.ToVertex(e)].length > (D[v].length+G.Weight(e)))
            {
                D[G.ToVertex(e)].length = D[v].length+G.Weight(e);
                D[G.ToVertex(e)].pre = v;
                H.Insert(D[G.ToVertex(e)]);
            }
    }
}

int A[N][N] =             //图7.20  单源最短路径的示例
{
//  v0  v1  v2  v3  v4
    0, 10,  0, 30, 100,
    0,  0, 50,  0,  0,
    0,  0,  0,  0, 10,
    0, 10, 20,  0, 60,
    0,  0,  0,  0,  0,
};

int main()
{
    Graphm aGraphm(N); // 建立图
    aGraphm.IniGraphm(&aGraphm, A); // 初始化图
    Dist *D;
    Dijkstra(aGraphm, 0, D);

    return 0;
}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值