数据结构:图

1.图的2种常用存储方式

  • 邻接矩阵,即用二维数组表示,i->j的点1(true)代表右边,0(false)无边。
  • 邻接表 常用的一个数组,数组中的每一个都变为单链表的形式。

2.图的遍历

  • 深度优先遍历
    深度优先遍历,为了防止节点被重复访问,增加对应的一个标志的二维数组,每次访问之前,判断是否被访问过,没访问过即能访问,访问过就不能再次访问。
    能解决连通量的个数,变种是小岛的数量。
    深度遍历思路:
    1.先从v顶点出发,访问v.
    2.再访问v的邻接未被访问的节点,再从新的节点开始,继续访问新的邻接未被访问的节点,依次类推。直到访问过的节点再也没有未被访问的节点为止。
    3.如果当前节点再也没有未被访问过的邻接点,那么回退到上一次的节点,看上一次的节点周围还有未被访问过的节点,有则就访问,没有,就继续回退到上一次的节点。重复2,3.
    4.直到图中所有节点都被访问,搜索结束。
    深度优先搜索连通图
bool visited[num];
void DFS(Graph G,int v){
	//从第v个顶点出发递归地深度搜索图G
	cout<< v;
	visited[v] = true;
	for(w = FirstAdjVex(G,v);w>=0;w=NextAdjVex(G,v,w))
	//依次检查v的有所邻接点w,FirstAdjVex(G,v)表示v第一个邻接点
	//NextAdjVex(G,v,w)表示v相对于w的下一个邻接点,w>=0表示存在邻接点
	if(!visited[w])
	DFS(G,w);   //对v尚未访问过的邻接顶点w递归调用dfs
	}

深度优先搜索遍历非连通图

void DFSTraverse(Graph G)
{
	//对非连通图G做深度优先遍历
	for(v=0;v<G.vexnum;++v)
	visited[v] = false;   //访问标志数组初始化
	for(v = 0;v<G.verxnum;++v)    //循环调用访问连通图的方法
	if(!visited[v])
	DFS(G,v);

邻接矩阵表示图的深度优先遍历搜索

void DFS_AM(AMGraph G,int v){
	//从v顶点开始深度搜索遍历G
	cout<<v;
	visited[v] = true;
	for(w = 0;w<G.vexnum;w++){  //依次检查邻接矩阵v的行
	if((G.arcs[v][w]!=0)&&(!visited[w]))
		DFS_AM(G,w);
		//G.arcs[v][w]!=0表示w是v的邻接点,如果w未访问,则递归调用dfs_am

邻接表表示图的深度遍历

void DFS_AL(ALGraph G,int v)
{  //从v开始
	cout<<v;
	visited[v] = true;
	p = G.vertices[v].firstarc;   //p指向v的边链表的第一个边节点
	while(p!=NULL)	//边节点非空
	{
		w = p->adjvex;  //表示w是v的邻接点
		if(!visited[w])
		DFS_AL(G,w);
		p = p->nextarc;   /p指向下一个
  • 广度优先遍历
    广度优先遍历,则是用来寻找最短路径,因为根据广度优先搜索,先找自己节点的附近的,再逐渐往深入。
    广度搜索类似树的层序遍历,需要用队列来保存。
    广度优先遍历连通图
void BFS(Graph G,int v){
	//广度优先非递归遍历G
	cout<<v;
	visited[v] = true;
	INitQueue(q); //初始化队列
	EnQueue(Q,v);  //v进队
	while(!QueueEmpty(Q))  //队列非空
	{
		DeQueue(Q,u);   //队头元素出队置u
		for(w = FirstAdjVex(G,u);w>=0;w = NextAdjVex(G,u,w))
		//依次检查u的所有邻接点w,FirstAdjVex(G,u)表示u第一个邻接点
		//NextAdjVex(G,u,w)表示u相对于w的下一邻接点,w>=0表示存在邻接点
			if(!visited[w])       //w为u的尚未访问的邻接点
 			{
				cout<<w;
				visited[w] = true;    //
				EnQueue(Q,w);
			}
	}
}	

邻接表

//
// Created by Administrator on 2019/5/24.
//

#ifndef INC_06_FINDING_A_PATH_SPARSEGRAPH_H
#define INC_06_FINDING_A_PATH_SPARSEGRAPH_H

#include <iostream>
#include <vector>
#include <cassert>

using namespace std;

// 稀疏图 - 邻接表
class SparseGraph{

private:
    int n, m;       // 节点数和边数
    bool directed;  // 是否为有向图
    vector<vector<int>> g;  // 图的具体数据

public:
    // 构造函数
    SparseGraph( int n , bool directed ){
        assert( n >= 0 );
        this->n = n;
        this->m = 0;    // 初始化没有任何边
        this->directed = directed;
        // g初始化为n个空的vector, 表示每一个g[i]都为空, 即没有任和边
        g = vector<vector<int>>(n, vector<int>());
    }

    ~SparseGraph(){ }

    int V(){ return n;} // 返回节点个数
    int E(){ return m;} // 返回边的个数

    // 向图中添加一个边
    void addEdge( int v, int w ){

        assert( v >= 0 && v < n );
        assert( w >= 0 && w < n );

        g[v].push_back(w);
        if( v != w && !directed )
            g[w].push_back(v);

        m ++;
    }

    // 验证图中是否有从v到w的边
    bool hasEdge( int v , int w ){

        assert( v >= 0 && v < n );
        assert( w >= 0 && w < n );

        for( int i = 0 ; i < g[v].size() ; i ++ )
            if( g[v][i] == w )
                return true;
        return false;
    }

    // 显示图的信息
    void show(){

        for( int i = 0 ; i < n ; i ++ ){
            cout<<"vertex "<<i<<":\t";
            for( int j = 0 ; j < g[i].size() ; j ++ )
                cout<<g[i][j]<<"\t";
            cout<<endl;
        }
    }

    // 邻边迭代器, 传入一个图和一个顶点,
    // 迭代在这个图中和这个顶点向连的所有顶点
    class adjIterator{
    private:
        SparseGraph &G; // 图G的引用
        int v;
        int index;

    public:
        // 构造函数
        adjIterator(SparseGraph &graph, int v): G(graph){
            this->v = v;
            this->index = 0;
        }

        ~adjIterator(){}

        // 返回图G中与顶点v相连接的第一个顶点
        int begin(){
            index = 0;
            if( G.g[v].size() )
                return G.g[v][index];
            // 若没有顶点和v相连接, 则返回-1
            return -1;
        }

        // 返回图G中与顶点v相连接的下一个顶点
        int next(){
            index ++;
            if( index < G.g[v].size() )
                return G.g[v][index];
            // 若没有顶点和v相连接, 则返回-1
            return -1;
        }

        // 查看是否已经迭代完了图G中与顶点v相连接的所有顶点
        bool end(){
            return index >= G.g[v].size();
        }
    };
};

#endif //INC_06_FINDING_A_PATH_SPARSEGRAPH_H

邻接矩阵

#ifndef INC_06_FINDING_A_PATH_DENSEGRAPH_H
#define INC_06_FINDING_A_PATH_DENSEGRAPH_H

#include <iostream>
#include <vector>
#include <cassert>

using namespace std;

// 稠密图 - 邻接矩阵
class DenseGraph{

private:
    int n, m;       // 节点数和边数
    bool directed;  // 是否为有向图
    vector<vector<bool>> g; // 图的具体数据



public:
    // 构造函数
    DenseGraph( int n , bool directed ){
        assert( n >= 0 );
        this->n = n;
        this->m = 0;    // 初始化没有任何边
        this->directed = directed;
        // g初始化为n*n的布尔矩阵, 每一个g[i][j]均为false, 表示没有任和边
        g = vector<vector<bool>>(n, vector<bool>(n, false));
    }

    ~DenseGraph(){ }

    int V(){ return n;} // 返回节点个数
    int E(){ return m;} // 返回边的个数

    // 向图中添加一个边
    void addEdge( int v , int w ){

        assert( v >= 0 && v < n );
        assert( w >= 0 && w < n );

        if( hasEdge( v , w ) )
            return;

        g[v][w] = true;
        if( !directed )
            g[w][v] = true;

        m ++;
    }

    // 验证图中是否有从v到w的边
    bool hasEdge( int v , int w ){
        assert( v >= 0 && v < n );
        assert( w >= 0 && w < n );
        return g[v][w];
    }

    // 显示图的信息
    void show(){

        for( int i = 0 ; i < n ; i ++ ){
            for( int j = 0 ; j < n ; j ++ )
                cout<<g[i][j]<<"\t";
            cout<<endl;
        }
    }

    // 邻边迭代器, 传入一个图和一个顶点,
    // 迭代在这个图中和这个顶点向连的所有顶点
    class adjIterator{
    private:
        DenseGraph &G;  // 图G的引用
        int v;
        int index;

    public:
        // 构造函数
        adjIterator(DenseGraph &graph, int v): G(graph){
            assert( v >= 0 && v < G.n );
            this->v = v;
            this->index = -1;   // 索引从-1开始, 因为每次遍历都需要调用一次next()
        }

        ~adjIterator(){}

        // 返回图G中与顶点v相连接的第一个顶点
        int begin(){

            // 索引从-1开始, 因为每次遍历都需要调用一次next()
            index = -1;
            return next();
        }

        // 返回图G中与顶点v相连接的下一个顶点
        int next(){

            // 从当前index开始向后搜索, 直到找到一个g[v][index]为true
            for( index += 1 ; index < G.V() ; index ++ )
                if( G.g[v][index] )
                    return index;
            // 若没有顶点和v相连接, 则返回-1
            return -1;
        }

        // 查看是否已经迭代完了图G中与顶点v相连接的所有顶点
        bool end(){
            return index >= G.V();
        }
    };
};

#endif //INC_06_FINDING_A_PATH_DENSEGRAPH_H
#ifndef INC_06_FINDING_A_PATH_READGRAPH_H
#define INC_06_FINDING_A_PATH_READGRAPH_H

#include <iostream>
#include <string>
#include <fstream>
#include <sstream>
#include <cassert>

using namespace std;

// 读取图算法
template <typename Graph>
class ReadGraph{

public:
    // 从文件filename中读取图的信息, 存储进图graph中
    ReadGraph(Graph &graph, const string &filename){

        ifstream file(filename);
        string line;
        int V, E;

        assert( file.is_open() );

        // 第一行读取图中的节点个数和边的个数
        assert( getline(file, line) );
        stringstream ss(line);
        ss>>V>>E;

        assert( V == graph.V() );

        // 读取每一条边的信息
        for( int i = 0 ; i < E ; i ++ ){

            assert( getline(file, line) );
            stringstream ss(line);

            int a,b;
            ss>>a>>b;
            assert( a >= 0 && a < V );
            assert( b >= 0 && b < V );
            graph.addEdge( a , b );
        }
    }
};

#endif //INC_06_FINDING_A_PATH_READGRAPH_H

计算路径,点与点之间的长度,能否到达

#ifndef INC_06_FINDING_A_PATH_PATH_H
#define INC_06_FINDING_A_PATH_PATH_H

#include <vector>
#include <stack>
#include <iostream>
#include <cassert>

using namespace std;

// 路径查询
template <typename Graph>
class Path{

private:
    Graph &G;   // 图的引用
    int s;      // 起始点
    bool* visited;  // 记录dfs的过程中节点是否被访问
    int * from;     // 记录路径, from[i]表示查找的路径上i的上一个节点

    // 图的深度优先遍历
    void dfs( int v ){

        visited[v] = true;

        typename Graph::adjIterator adj(G, v);
        for( int i = adj.begin() ; !adj.end() ; i = adj.next() ){
            if( !visited[i] ){
                from[i] = v;
                dfs(i);
            }
        }
    }

public:
    // 构造函数, 寻路算法, 寻找图graph从s点到其他点的路径
    Path(Graph &graph, int s):G(graph){

        // 算法初始化
        assert( s >= 0 && s < G.V() );

        visited = new bool[G.V()];
        from = new int[G.V()];
        for( int i = 0 ; i < G.V() ; i ++ ){
            visited[i] = false;
            from[i] = -1;
        }
        this->s = s;

        // 寻路算法
        dfs(s);
    }

    // 析构函数
    ~Path(){

        delete [] visited;
        delete [] from;
    }

    // 查询从s点到w点是否有路径
    bool hasPath(int w){
        assert( w >= 0 && w < G.V() );
        return visited[w];
    }

    // 查询从s点到w点的路径, 存放在vec中
    void path(int w, vector<int> &vec){

        assert( hasPath(w) );

        stack<int> s;
        // 通过from数组逆向查找到从s到w的路径, 存放到栈中
        int p = w;
        while( p != -1 ){
            s.push(p);
            p = from[p];
        }

        // 从栈中依次取出元素, 获得顺序的从s到w的路径
        vec.clear();
        while( !s.empty() ){
            vec.push_back( s.top() );
            s.pop();
        }
    }

    // 打印出从s点到w点的路径
    void showPath(int w){

        assert( hasPath(w) );

        vector<int> vec;
        path( w , vec );
        for( int i = 0 ; i < vec.size() ; i ++ ){
            cout<<vec[i];
            if( i == vec.size() - 1 )
                cout<<endl;
            else
                cout<<" -> ";
        }
    }
};

#endif //INC_06_FINDING_A_PATH_PATH_H

广度搜索最短路径

//
// Created by Administrator on 2019/5/24.
//

#ifndef DEMOGRAPH_SHORTESTPATH_H
#define DEMOGRAPH_SHORTESTPATH_H
#include <iostream>
#include <cassert>
#include "queue"
#include "stack"

using namespace std;
template <typename Graph>
class ShortestPath{

private:
    Graph &G;
    int s;
    bool *visited;
    int *from;
    int *ord;   //从s 到每一个i的最短距离
public:
    ShortestPath(Graph &graph,int s):G(graph){

        //算法初始化
        assert(s>=0&&s<graph.V());

        visited = new bool[graph.V()];
        from = new int[graph.V()];
        ord = new int[graph.V()];
        for(int i=0;i<graph.V();i++){

            visited[i] = false;
            from[i] = -1;
            ord[i] = 1;
        }

        this->s = s;

        queue<int> q;

        //无向图最短路径算法
        q.push(s);
        visited[s] = true;
        //自己到自己最短
        ord[s] = 0;
        while(!q.empty()){

            int v = q.front();
            q.pop();

            typename Graph::adjIterator adj(G,v);
            for(int i=adj.begin();!adj.end();i = adj.next()){
                if(!visited[i]){
                    q.push(i);
                    visited[i] = true;
                    //为啥是这样
                    from[i] = v;
                    //为啥是这样
                    ord[i] = ord[v]+1;
                }
            }
        }


    }

    ~ShortestPath(){

        delete [] visited;
        delete [] from;
        delete [] ord;
    }

    //查询从s到w是否有路径
    bool hasPath(int w){

        assert(w>=0&&w<G.V());

        return visited[w];

    }

    //查询从s到w点的路径,存放在Vec中
    void path(int w, vector<int> &vec){

        assert( w >= 0 && w < G.V() );

        stack<int> s;
        // 通过from数组逆向查找到从s到w的路径, 存放到栈中
        int p = w;
        while( p != -1 ){
            s.push(p);
            p = from[p];
        }

        // 从栈中依次取出元素, 获得顺序的从s到w的路径
        vec.clear();
        while( !s.empty() ){
            vec.push_back( s.top() );
            s.pop();
        }
    }

    //打印出从s到w点的路径
    void showPath(int w){

        assert( w >= 0 && w < G.V() );

        vector<int> vec;
        path(w, vec);
        for( int i = 0 ; i < vec.size() ; i ++ ){
            cout<<vec[i];
            if( i == vec.size()-1 )
                cout<<endl;
            else
                cout<<" -> ";
        }

    }

    //查看从s到w点的最短路径长度
    int length(int w){

        assert( w >= 0 && w < G.V() );
        return ord[w];
    }

};

#endif //DEMOGRAPH_SHORTESTPATH_H

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
本演示程序中,要求以邻接表作为的存储结构。中顶数据类型为字符型,在提示信息下由用户输入。边的信息由用户输入弧头和弧尾元素。<br><br><br>为实现上述程序功能,以线性链表表示集合。为此,需要两个抽象数据类型:线性表和集合。<br>1. 线性表的抽象数据类型定义为:<br> ADT ALGraph{<br> 数据对象V:V是具有相同特性的数据元素的集合,称为顶集。<br> 数据关系R1:R={VR}<br>VR={<v,w>|v,w V且P(v,w),<v,w>表示从v到w的弧,为此P(v,w)定义了弧<v,w> 的意义或信息}<br> 基本操作P: <br>void CreateAdjList(ALGraph& G)<br> 操作结果:根据相应的提示信息构造一个。<br> int LocateVex(&G,char u)<br> 初始条件:G存在,u和G中顶有相同特征。<br> 操作结果:若G中存在顶u,则返回该顶中位置;否则返回-1。<br>int FirstAdjVex(ALGraph G,int v)<br> 初始条件:G存在,v是G中某个顶。<br> 操作结果:返回v第一个邻接。若v在G中没有邻接,返回-1。<br>int NextAdjVex(ALGraph G,int v,int w)<br> 初始条件:G存在,v是G中某个顶,w是v的邻接。<br> 操作结果:返回v的(相对于w的)第一个邻接。若不存在,返回-1。<br>void DFS(ALGraph &G,int v)<br> 初始条件:G存在。<br> 操作结果:从顶v出发,对进行深度优先遍历。<br>void DFSTraverse(ALGraph *G)<br> 初始条件:G存在。<br> 操作结果:对进行深度优先遍历。<br>void BFSTraverse(ALGraph *G)<br> 初始条件:G存在。<br> 操作结果:对进行广度优先遍历。<br> }ADT ALGraph<br>2. 队列的抽象数据类型定义为:<br> ADT Queue{<br> 数据对象D:D={ | QNodeSet,i=1,2,…,n,n 0 }<br> 数据关系R2:R2={< , >| , D1, i=2,…,n}<br> 约定其中 端为队列头, 为队列尾<br> 基本操作P:<br> InitQueue (*Q)<br> 操作结果:构造一个空队列Q<br> EnQueue (*Q,e)<br> 初始条件:队列Q已存在<br> 操作结果:插入元素e为Q的新的队尾元素<br> DeQueue (*Q)<br> 初始条件:Q为非空队列<br> 操作结果:删除Q的队头元素,并返回其值<br> QueueEmpty (*Q)<br> 操作结果:队为空,则返回0;否则,返回1<br> }ADT Queue<br><br><br>的基本操作设置如下:<br>void CreateAdjList(ALGraph& G) //构建<br>int LocateVex(ALGraph &G,char u) //返回u在G中的位置<br>int FirstAdjVex(ALGraph G,int v) //返回v的第一个邻接<br>int NextAdjVex(ALGraph G,int v,int w) //返回v的相对w的下一个邻接<br>void DFS(ALGraph &G,int v) //从顶v开始深度优先遍历<br>void DFSTraverse(ALGraph *G) //深度优先遍历<br>void BFSTraverse(ALGraph *G) //广度优先遍历<br><br>队列的基本操作设置如下:(具体操作上一次上机题中已经涉及,故此处不再详述)<br>void InitQueue(LinkQueue &Q) <br>void EnQueue(LinkQueue& Q,int e)<br>int DeQueue( LinkQueue& Q)<br>int QueueEmpty(Queue *Q)<br><br>

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值