图 插入、删除节点 插入、删除边 返回边值 广度优先遍历

typedef int EdgeType;//边上的权值类型
typedef char VertexType;//顶点数据类型
typedef bool Boolean;//Boolean为布尔类型,其值为TRUE或者FALSE ,用于广度优先遍历
Boolean visited[MAX];//访问标志的数组,用于广度优先遍历

define MAXSIZE 9

define MAX 100//最大顶点数,由用户定义

define INFINITY 65525

我们的邻接表是这样的:
《图 插入、删除节点 插入、删除边 返回边值 广度优先遍历》

如图可见,邻接表由边表结点和顶点表结点组成的,所以我们接下来可以设置两个类,一个是顶点表,一个是边表。
//边表结点类

class EdgeNode//**边表结点类**
{
public:
    int adjvex;//这个为边表结点所记录的结点下标
    VertexType data;//边表结点所记录的下标代表的数值(图中没有显示)
    int weight;//权值
    EdgeNode *next;//指向下一个边表结点

}; 

//顶点表结点类

class VertexNode//**顶点表结点类**
{
public:
    VertexType data;//记录顶点的数值,比如A
    EdgeNode *firstedge;//与边表结点所对应的指针域

};

接下来设置最后一个关于邻接表的类
//邻接表存储类

class Link
{
public:
    VertexNode adjLink[MAX];//这里记录的就是顶点表的数组,每一个数组对应着一个顶点表结点
    int numVertexes,numEdges;//每一个邻接表的边数和顶点个数
};

创造图的邻接表:

void CreateLink(Link *G)//顶点表的指针对象,在主函数已经开辟了空间
{
int i,j,k,w;
cout<<"请输入顶点数和边数:";
cin>>G->numVertexes>>G->numEdges;
cout<<"请输入顶点信息:";
for(i=0;i<G->numVertexes;i++)//读入顶点信息,建立顶点表 
{
    cin>>G->adjLink[i].data;
    G->adjLink[i].firstedge = NULL;//将顶点表对边表结点的链接域全部置空 
}

for(k=0;k<G->numEdges;k++)//这里分成了两个部分,因为是无向图,所以需要有反向链接,如果是有向图,只需将第二段代码删除
{
    cout<<"输入边(vi,vj)的下标i、下标j和权值w:";

    cin>>i>>j>>w;
    EdgeNode *e = new EdgeNode;//申请空间,生成边表结点 
    e->adjvex = j;//邻接序号为j 
    e->weight = w;//权值为w 
    e->data = G->adjLink[j].data;//将顶点的值赋值到边表结点中 
    e->next = G->adjLink[i].firstedge;//将e指针指向当前顶点指向的结点
    G->adjLink[i].firstedge = e;//将当前顶点的指针指向e 




    EdgeNode *e1 = new EdgeNode;//向内存申请空间,生成边表结点 
    e1->adjvex = i;//邻接序号为i 
    e1->weight = w;//权值为w
    e1->data = G->adjLink[i].data;//将顶点的值赋值到边表结点中 
    e1->next = G->adjLink[j].firstedge;//将e指针指向当前顶点指向的结点 
    G->adjLink[j].firstedge = e1; //将当前的指针指向e
} 

}

接下来就是返回顶点和顶点之间边值的代码:

void Return_Data(Link *G,VertexType data1,VertexType data2)//返回图中指定边的边值 ,data1和data2为边的顶点下标 
{
    int k,j;
    bool flag1 = false;
    for(k=0;k<G->numVertexes;k++)
        if(G->adjLink[k].data == data1)
            break;
    for(j=0;j<G->numVertexes;j++)
        if(G->adjLink[j].data == data2)
            break;
    EdgeNode *e1 = G->adjLink[k].firstedge;
    while(e1)
    {
        if(e1->adjvex == j)
        {
            cout<<"你想查找的边的边值为:"<<e1->weight;//这里输出的应该是权值
            flag1 = true;
            break;
        }
        e1 = e1->next;
        }
    if(!flag1)
    {
        cout<<"这两个结点没有边"; 
    }
}

返回值的思想很简单。首先我们输入的是结点的数值,开头利用了两次循环来寻找结点数值所对应的下标data1和data2.
下一步就是去data1位下标的顶点表结点中,利用了链接域,在边表结点不为空的情况下,利用循环判断条件,直到寻找到下标为data2的边表结点e1,从而利用e1->data输出了他们边的权值

广度优先遍历

void BFSTraverse(Link *G)
{
    int i;
    EdgeNode *p;
    Queue *Q = new Queue;
    for(i=0;i<G->numVertexes;i++)
    {
        visited[i] = false;//将数组标识成未访问过的形式
    }
    InitQueue(Q);//初始化队列
    for(i=0;i<G->numVertexes;i++)
    {
        if(!visited[i])//当结点没有被访问过
        {
            if(G->adjLink[i].firstedge == NULL)//当结点没有边时,跳过循环
                continue;
            visited[i] = true;//表示访问过该结点
            cout<<G->adjLink[i].data;//输出数据
            EnQueue(Q,i);//进队
            while(!QueueEmpty(Q))//当队列不为空
            {
                DeQueue(Q);//将上一个顶点出队
                p = G->adjLink[i].firstedge;//找边表结点
                while(p)//当边表结点不为空时,进行操作
                {
                    if(!visited[p->adjvex])
                    {
                        visited[p->adjvex] = true;//标识成访问过
                        cout<<G->adjLink[p->adjvex].data;//输出数据
                        EnQueue(Q,p->adjvex);//边表结点进队
                    }
                    p = p->next;//寻找下一个边表结点
                }
            }       
        }
    }
}

这一段代码是借鉴大话数据结构里面的代码写出来的。但是大话数据结构的代码有一个不足之处。在一个顶点没有边的情况下,它的顶点值依然可以被遍历到。本人修改了一下,在代码中间加上了 if(G->adjLink[i].firstedge == NULL
continue;
这样的语句,当一个顶点的边表结点不存在时,意味着这个顶点没有边,此时跳过下面的步骤,重新进行下一次的循环。操作我觉得还是可以有的。

插入顶点

void InsertNode(Link *G,VertexType data)
{
    G->numVertexes++;
    G->adjLink[G->numVertexes-1].data = data;
    G->adjLink[G->numVertexes-1].firstedge = NULL;
}

操作简单,在顶点数组里多加一个位置并且赋值就好啦~

插入边

void InsertEdge(Link *G,VertexType data1,VertexType data2,int datas)
{
    int i,j;//这个,我先写玩后面的再来看看
    bool flag2;
    for(i=0;i<G->numVertexes;i++)//找顶点下标
       if(G->adjLink[i].data == data1)
            break;
    for(j=0;j<G->numVertexes;j++)//找顶点下标
       if(G->adjLink[j].data == data2)
            break;
    EdgeNode *e1 = G->adjLink[i].firstedge;//设立一个指向顶点数组后的边表结点的指针
    while(e1)//当顶点数组存在后继指针,即顶点数组如果有其它边
    {
        if(e1->adjvex == j)
        {
            cout<<"你想插入的边已经存在,值为:"<<e1->weight;//这里输出的应该是权值
            flag2 = true;
            break;
        }
        e1 = e1->next;
    }
    if(!flag2)//当想创建的边不存在时
    {
        cout<<"这两个结点没有边,可以插入边."; 
        EdgeNode *e = new EdgeNode;//新建结点
        e1 = G->adjLink[i].firstedge;
        if(G->adjLink[i].firstedge)//虽然刚写完但是我看着代码也怕啊
        {
            e1 = G->adjLink[i].firstedge;
            while(e1->next)//一直循环到边表结点的最后一个,插入新的边表结点,这个结点记录了你想插入边的另外一个顶点的信息
            {
                e1 = e1->next;
            } 
            e1->next = e;
        }
        else
        {
            G->adjLink[i].firstedge = e;
        }
        e->next = NULL;
        e->adjvex = j;
        e->weight = datas;



        EdgeNode *e2 = new EdgeNode;//新建结点,因为无向图,所以两个结点分别进行正反操作
        e1 = G->adjLink[j].firstedge;

        if(G->adjLink[j].firstedge)
        {
            e1 = G->adjLink[j].firstedge;
            while(e1->next)
            {
                e1 = e1->next;
            } 
            e1->next = e2;
        }
        else
        {
            G->adjLink[j].firstedge = e2;
        }

        e2->adjvex = i;
        e2->next = NULL;
        e2->weight = datas;


    }

}

删除节点

void DeleteNode(Link *G,VertexType data)
{
    int i,num,w,j;
    bool flag3 = false;
    for(i=0;i<G->numVertexes;i++)
        if(G->adjLink[i].data == data)
            num = i;//记录顶点的数组对应下标 
    EdgeNode *e1;
    for(i=0;i<G->numVertexes;i++)
    {
        if(i == num)//当循环到删除节点的下标时,不执行任何操作 
            continue;
        EdgeNode *e = G->adjLink[i].firstedge;
        e1 = e; 
        while(e)
        {
            if(e->adjvex == num)
            {
                flag3 = true;
                break;
            }
            e1 = e;
            e = e->next;
        }
        if(G->adjLink[i].firstedge == e)
            G->adjLink[i].firstedge = e->next;
        if(flag3)
        {
            e1->next = e->next; 
        }
        flag3 = false;

    } 

        cout<<endl;

    for(w=0;w<G->numVertexes-1;w++)
    {
        G->adjLink[w].data = G->adjLink[w+1].data;
        G->adjLink[w].firstedge = G->adjLink[w+1].firstedge; 
        for(j=0;j<G->numVertexes;j++)
        {
            EdgeNode *e = G->adjLink[j].firstedge;
            while(e)
            {
                if(e->data == G->adjLink[w].data)
                      e->adjvex = w;
                e = e->next;
            }
        }
    }

    G->numVertexes--; 
    cout<<"结点删除已结束.";

} 

删除节点要记住两件事,在数组里去掉结点的值,并调节它们的下标。
在各个数组的边表结点中,调整它们因为数组更改所对应的下标值,并且要删除顶点表中,储存的被删除顶点信息有关的边表结点,切记啊!

删除边

void DeleteEdge(Link *G,VertexType data1,VertexType data2)
{

    int i,j;
    bool flag4 = false,flag5 = false,flag6 = false;
    for(i=0;i<G->numVertexes;i++)
        if(G->adjLink[i].data == data1)
            break;
    for(j=0;j<G->numVertexes;j++)
        if(G->adjLink[j].data == data2)
            break;
    EdgeNode *e1 = G->adjLink[i].firstedge;
    while(e1)
    {
        if(e1->adjvex == j)
        {
            cout<<"你想删除的边存在,值为:"<<e1->weight;//这里输出的应该是权值
            flag4 = true;
            break;
        }
        e1 = e1->next;
    }
    if(!flag4)
    {
        cout<<"你想删除的边不存在;";
    }
    //没有问题




    if(flag4)//存在边,开始删除 
    {
        EdgeNode *e;//新建结点
        e1 = G->adjLink[i].firstedge;
        e = e1;
        if(G->adjLink[i].firstedge)
        {
            while(e1)
            {
                if(e1->adjvex == j)
                    break;
                e = e1;
                e1 = e1->next;
                flag5 = true;
            } 
            if(!flag5)
                G->adjLink[i].firstedge = e1->next;
            else if(flag5)
                e->next = e1->next;
        }
        else if(!G->adjLink[i].firstedge)
        {
            G->adjLink[i].firstedge = NULL;
        }

        EdgeNode *e2;//新建结      
        EdgeNode *e3 = G->adjLink[j].firstedge;
        e2 = e3;
        if(G->adjLink[j].firstedge)
        {
            while(e3)
            {
                if(e3->adjvex == i)
                    break;
                e2 = e3;
                e3 = e3->next;
                flag6 = true;
            } 
            if(!flag6)
                G->adjLink[j].firstedge = e3->next;
            else if(flag6)
                e2->next = e3->next;
        }
        else if(!G->adjLink[j].firstedge)
        {
            G->adjLink[j].firstedge = NULL;
        }  
        cout<<"成功删除";   

    }

}

删除边的想法是:循环找到对应结点的下标,然后利用下标在数组里面寻找边表结点,然后可以向链表一样删除指针,因为无向图,正反各执行一次

作者要去洗澡没时间了啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊,以后有空再详细写!!!

    原文作者:数据结构之图
    原文地址: https://blog.csdn.net/qq_41217121/article/details/78760973
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞