在这篇文章中,我们要讨论一下关于图的知识点:

1.图的存储方式——邻接矩阵存储和邻接表存储

*邻接矩阵存储code如下所示

#include <iostream>
 
using namespace std;
 
template <class T>
class Graph
{
protected:
    int n,e;                                     //e是边数,n是节点数
public:
    virtual bool GInsert(int u,int v,T w)=0;   //图中在节点u和v之间插入一条权值为w的边
    virtual bool GRemove(int u,int v)=0;        //删除图中节点u和v之间的边
    virtual bool GExist(int u,int v) const=0;    //判断图中节点u和v之间是否存在路径
};
 
template <class T>
class MGraph:public Graph<T>
{
protected:                          //这里用protected权限是为了未来可能存在的继承该MGraph的类可以访问
    T **a;                          //二维数组指针指向图的邻接矩阵
public:
    MGraph(int mSize);
    ~MGraph();
    bool GInsert(int u,int v,T w);
    bool GRemove(int u,int v);
    bool GExist(int u, int v)const;
};
template <class T>
MGraph<T>::MGraph(int mSize)               //邻接矩阵是n*n的大小,即有n个节点
{
    n=mSize;
    a=new T* [n];                   //申请一个大小为n的T*指针类型数组空间,每个空间存储一个T指针
    for(int i=0;i<n;i++)            //初始化邻接矩阵
    {
        a[i]=new T [n];             //i个数组空间中的指针a[i]分别指向一段n连续的T类型数组空间
            for(int j=0;j<n;j++)
            {
                a[i][j]=-1;          //赋值-1,表明i和j节点之间没有边
            }
            a[i][i]=0;              //节点无自回路
    }
}
template <class T>
MGraph<T>::~MGraph()
{
    for(int i=0;i<n;i++)
        delete []a[i];
    delete []a;
}
template <class T>
bool MGraph<T>::GInsert(int u, int v, T w)
{
    if(u<0||v<0||u>n-1||v>n-1)
    {
        cout<<"节点值越界"<<endl;
        return false;
    }
    if(a[u][v]!=-1)
    {
        cout<<"边已经存在"<<endl;
        return false;
    }
    a[u][v]=w;
    e++;
    return true;
}
template <class T>
bool MGraph<T>::GRemove(int u,int v)
{
    if(u<0||v<0||u>n-1||v>n-1)
    {
        cout<<"节点值越界"<<endl;
        return false;
    }
    if(a[u][v]==-1)
    {
        cout<<"要删除的边不存在"<<endl;
        return false;
    }
    a[u][v]==-1;
    e--;
    return true;
}
 
template <class T>
bool MGraph<T>::GExist(int u, int v) const
{
    if(u<0||v<0||u>n-1||v>n-1)
    {
        cout<<"GExit:节点越界"<<endl;
        return false;
    }
    if(a[u][v]==-1)
    {
        cout<<"GExit:边不存在"<<endl;
        return false;
    }
    cout<<"GExit:边存在"<<endl;
    return true;
}
 
 
int main()
{
    MGraph<int> a(4);
    a.GInsert(0,1,1);
    a.GExist(0,2);
    return 0;
}

*邻接表类似于一个哈希表,邻接表存储方式的图code如下所示

#include <iostream>
using namespace std;
 
template <class T> class LGraph;
template <class T>
class LNode                 //邻接表节点类
{
private:
    int adjVex;             //节点号
    T w;                    //边权值
    LNode<T> *nextArc;      //节点指针
public:
    LNode(int vertex,T weight,LNode<T> *next)
    {
        adjVex=vertex;
        w=weight;
        nextArc=next;
    }
 
    friend class LGraph<T>; //友元类LGraph可以访问类LNode中的private成员
};
 
template <class T>
class LGraph                //邻接表类
{
private:
    LNode<T> **a;           //邻接表二维指针
    int n;                  //图的节点数
    int e;                  //图的边数
public:
    LGraph(int mSize);
    ~LGraph();
    bool LExist(int u,int v);
    bool LInsert(int u,int v,T w);
    bool LRemove(int u,int v);
};
 
template<class T>
LGraph<T>::LGraph(int mSize)    //初始化邻接表
{
    n=mSize;
    a=new LNode<T>* [n];
    for(int i=0;i<n;i++)
    {
        a[i]=NULL;
    }
}
 
template<class T>
LGraph<T>::~LGraph()
{
    for(int i=0;i<n;i++)
    {
        delete [] a[i];
    }
    delete []a;
}
 
template<class T>
bool LGraph<T>::LExist(int u,int v)
{
    if(u<0||v<0||u>n-1||v>n-1)
    {
        cout<<"LExist:节点号越界"<<'\n';
        return false;
    }
    LNode<T> *p=a[u];
    while(p)
    {
        if(p->adjVex==v)
        {
            cout<<"LExist:存在"<<u<<"-"<<v<<"边"<<'\n';
            return true;
        }
        p=p->nextArc;
    }
    if(!p)
        return false;
}
 
template<class T>
bool LGraph<T>::LInsert(int u,int v,T w)
{
    if(u<0||v<0||u>n-1||v>n-1)
    {
        cout<<"LInsert:节点号越界"<<'\n';
        return false;
    }
    if(LExist(u,v))
    {
        cout<<"LInsert:已经存在"<<u<<"-"<<v<<"边"<<'\n';
        return false;
    }
    LNode<T> *p=new LNode<T>(v,w,a[u]);
    a[u]=p;
    e++;
    return true;
}
 
template<class T>
bool LGraph<T>::LRemove(int u,int v)
{
    if(u<0||v<0||u>n-1||v>n-1)
    {
        cout<<"LRemove:节点号越界"<<'\n';
        return false;
    }
    if(!LExist(u,v))
    {
        cout<<"LRemove:不存在"<<u<<"-"<<v<<"边"<<'\n';
        return false;
    }
    LNode<T> *p=a[u],*q=NULL;
    while(p&&p->adjVex!=v)
    {
        q=p;
        p=p->nextArc;
    }
    if(!p)
        return false;
    if(q)
        q->nextArc=p->nextArc;
    else
        a[u]=p->nextArc;
    delete p;
    e--;
    return true;
}

2.图的深度优先遍历(DFS),这里我们讨论以邻接表形式存储的图如何进行深度优先遍历。这里采用了递归法,大致思路如下

(1)访问任意节点v,并对v打上已经访问过的标记

(2)依次从v的未访问邻接点出发,深度优先搜索图

#include "lgraph.cpp"
 
//图的搜索
template <class T>
class ExtLGraph:public LGraph<T>
{
private:
    void DFS(int v,bool *visited);          //从v节点出发深度优先搜索,visited是标志位数组指针
    //void BFS();
public:
    ExtLGraph(int mSize):LGraph<T>(mSize){} //调用父类的构造函数来初始化子类构造函数
    void DFS();                             //深度优先遍历
    //void BFS();                               //宽度优先遍历
};
template <class T>
void ExtLGraph<T>::DFS()
{
    bool *visited=new bool [n];             //创建标志位数组
    for(int i=0;i<n;i++)
        visited[i]=false;                   //所有标志位至false,表明未访问过
    for(int i=0;i<n;i++)
        if(!visited[i])                     //未访问过的节点进行深度优先搜索递归算法
            DFS(i,visited);
    delete [] visited;
}
template <class T>
void ExtLGraph<T>::DFS(int v,bool *visited)
{
    visited[v]=true;                        //v节点标志位至为true
    cout<<" "<<v;                           //打印输出访问到的节点v
    LNode<T> *p=a[v];
    while(p)
    {
        if(!visited[p->adjVex])
            DFS(p->adjVex,visited);
        p=p->nextArc;
    }
    
}

注意:由于ExtLGraph类是LGraph的子类,由于友元类无法继承的原因,在ExtLGraph类中无法访问LNode类中的private成员,因此LNode类中应该将ExtLGraph类设置为友元类!!!

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,463评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,868评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,213评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,666评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,759评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,725评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,716评论 3 415
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,484评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,928评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,233评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,393评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,073评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,718评论 3 324
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,308评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,538评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,338评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,260评论 2 352

推荐阅读更多精彩内容

  • https://zh.visualgo.net/graphds 浅谈图形结构https://zh.visualgo...
    狼之独步阅读 4,146评论 0 0
  • 1. 图的定义和基本术语 线性结构中,元素仅有线性关系,每个元素只有一个直接前驱和直接后继;树形结构中,数据元素(...
    yinxmm阅读 5,448评论 0 3
  • 图是一种比线性表和树更复杂的数据结构,在图中,结点之间的关系是任意的,任意两个数据元素之间都可能相关。图是一种多对...
    Alent阅读 2,303评论 1 22
  • 1 概述 图是数据结构中最复杂的形式,也是最烧脑的结构。无数的牛人乐此不疲地钻研,然而,时至今日,依然有很多问题等...
    CodingTech阅读 2,310评论 0 8
  • 我爱你 永恒不熄 我走过你身边,悄然对视。从前我只会知道,那是你告诉我,你不想看见我的讯号。但从今天来看,我知...
    弗兰德老鬼阅读 206评论 0 6