图的基本操作

目录

一:图的基类函数

         二:有向图的数据成员定义 

三:构造函数和析构函数 

四:增加+删除+判断边的存在性 

         五:两种遍历 

六:拓扑排序 

         七:主函数实现


一:图的基类函数

template<class typeOfVer,class typeOfEdge>
class graph{//基类:提供点数和边数信息
protected:
     int numVers;
     int numEdges;
public:
     int getNumVers() const{return numVers;}
     int getNumEdges() const{ return numEdges;}
};

注意:点数和边数用protected修饰,较新的编译器中,其继承函数要用:this->去访问

二:有向图的数据成员定义 

 //边结点
    struct edgeNode{
         int end;
         typeOfEdge weight;
         edgeNode* next;
         //边的构造函数
         edgeNode(int e,typeOfEdge w,edgeNode* p=NULL):
         end(e),weight(w),next(p){}
    };
    //点结点
    struct verNode{
        typeOfVer ver;
        edgeNode* head;
        //点的构造函数
        verNode(edgeNode* h=NULL):head(h){}
    };
    //点集动态数组
    verNode* verList;

说明:

  • 点集动态数组存放由verNode类型组成的数组,每个verNode类型记录顶点是什么+其引出的没有头顶点的邻接表的边的首地址
  • 边集存放了边的终点(起点不用记录,就是指向的),以及边的权重和连在一起的next指针
    /*工具函数*/
    //返回顶点数组下标
    int find(typeOfVer v) const{
    for(int i=0;i<this->numVers;i++)
         if(verList[i].ver==v) return i;
     return -1;
    }

这是一个私有函数,目的是---将顶点输出成数组存储的下标

三:构造函数和析构函数 

   /*构造函数*/
   adjListGraph(int vSize,const typeOfVer d[]){
     //初始化保护成员
     this->numVers=vSize;
     this->numEdges=0;
     //初始化顶点数组
     verList=new verNode[vSize];
     for(int i=0;i<this->numVers;i++){
          verList[i].ver=d[i];
     }
   }

注意:传入的const typeOfVer d[]前没有任何修饰符

别忘了对基类成员的点数和边数赋值


   /*析构函数*/
   ~adjListGraph(){
     //释放邻接表
     for(int i=0;i<this->numVers;i++){
          edgeNode* p=verList[i].head;
          while(p){
               verList[i].head=verList[i].head->next;
               delete p;
               p=p->next;
          }
     }
     //释放点集
     delete[] verList;
     }

暂时会报错---不过注意先释放点的邻接表,再释放点集

四:增加+删除+判断边的存在性 

先看有向图

1:增边----insert,要传入两点+边权值

   /*insert--添加*/
   void insert(typeOfVer x,typeOfVer y,typeOfEdge w){
     int u=find(x);
     int v=find(y);
     verList[u].head=new edgeNode(v,w,verList[u].head);
     ++this->numEdges;
   }

这里:由于是没有头结点,而且头插法,所以先new一个结点,插入到当前头结点前,然后新的头结点指向它

edgeNode* tmp-----其next是verList[u].head,然后:verList[u].head=tmp

2:判断----exist,传入两个顶点

   /*esist--判断*/
   bool exist(typeOfVer x,typeOfVer y)const{
     int u=find(x);
     int v=find(y);
     edgeNode* p=verList[u].head;
     while(p && p->end!=v) p=p->next;
     if(p) return true;
     else return false;
   }

这里用到编程一个常用思想---找结点

这里找的是:终点end是v的指针p,所以还有对while结束的两个条件判断是哪个不成立

3:删除----remove,传入两个顶点

  /*删除---remove*/
   void remove(typeOfVer x,typeOfVer y){
     int u=find(x);
     int v=find(y);
     edgeNode* p=verList[u].head;
     /*看看头结点*/
     if(p==NULL) return;//1:不存在
     if(p->end==v){//2:头结点就是待删结点
         verList[u].head=verList[u].head->next;
         delete p;
         --this->numEdges;
         return;//别忘了退出
     }
     /*待删结点是中间的结点*/
     //先找到待删结点的前驱
     edgeNode* pre=p;
     while(pre->next&&pre->next->end!=v) pre=pre->next;
     if(pre->next){//待删结点存在
        p=pre->next;
        pre->next=p->next;
        delete p;
        --this->numEdges;
        //由于程序到底了,无需return
     }
   }

较为麻烦的地方在于-----需要分析待删结点是不是头结点

因为头结点不用找,中间节点需要找

 如果是无向图,由于边在邻接表中存两次

1:insert()中加入: verLst[v].head=new edgeNode(u,w,verList[v].head)

2:remove()末尾加一句:remove(y,x)---递归一次

exist无需改变,因为查一个即可

五:两种遍历 

深度优先遍历

   //深度优先遍历
   void dfs()const{
      //初始化标记数组
      bool* visited=new bool[this->numVers];
      for(int i=0;i<this->numVers;i++){
          visited[i]=false;
      }
      //开始遍历
      cout<<"深度优先遍历:";
      for(int i=0;i<this->numVers;i++){
          if(!visited[i]) dfs(i,visited);
      }
      cout<<endl;
   }

由于图不一定连通,必须用for遍历一遍顶点,深度遍历递归实现

私有函数实现:

    //为公有dfs传递verlist
    void dfs(int i,bool visited[]) const{
         cout<<verList[i].ver<<" ";
         visited[i]=true;//访问过了
         edgeNode* p=verList[i].head;
         while(p){
            if(!visited[p->end]) dfs(p->end,visited);
            p=p->next;
         }
    }

广度优先遍历

需要用到队列---调用标准库#include<queue>

 //广度优先遍历
   void bfs() const{
     //初始化标记数组
      bool* visited=new bool[this->numVers];
      for(int i=0;i<this->numVers;i++){
          visited[i]=false;
      }
     //遍历辅助变量
     queue<int> q;
     int tmp;
     edgeNode* p;
     //开始遍历
     cout<<"广度优先遍历:";
     for(int i=0;i<this->numVers;i++){
          if(!visited[i]) q.push(i);//考虑不连通图
          while(!q.empty()){
               tmp=q.front();
               q.pop();
               if(visited[tmp]) break;
               visited[tmp]=true;
               cout<<verList[tmp].ver<<" ";
               p=verList[tmp].head;
               while(p){
                    if(!visited[p->end]) q.push(p->end);
                    p=p->next;
               }
          }
     }
     cout<<endl;
   }

一定在while输出前加一句:if(visited[tmp]) break;

防止重复访问-----因为在输出该节点前,可能因为以其是终点的边过多而多次入队

六:拓扑排序 

 

 //拓扑排序
   void topSort() const{
     //初始化入度数组--indegree[]
     int* indegree=new int[this->numVers];
     for(int i=0;i<this->numVers;i++) indegree[i]=0;
     //计算起始图的入读数组
     edgeNode* p;
     for(int i=0;i<this->numVers;i++){
          for(p=verList[i].head;p;p=p->next){
               ++indegree[p->end];//入度求法
          }
     }
     cout<<"拓扑排序序列:";
     //将入度为0的结点入队
     queue<int> que;
     int tmp;
     for(int i=0;i<this->numVers;i++){
          if(indegree[i]==0) que.push(i);
     }
     //拓扑过程
     while(!que.empty()){
          tmp=que.front();
          que.pop();
          cout<<verList[tmp].ver<<" ";
          for(p=verList[tmp].head;p;p=p->next){
               if(--indegree[p->end]==0) que.push(p->end);
          }
     }
     cout<<endl;
   }

关注一下:有关邻接表入度计算的方法

     for(int i=0;i<this->numVers;i++){
          for(edgeNode* p=verList[i].head;p;p=p->next){
               ++indegree[p->end];//入度求法
          }
     }

注意是:++indegree[p->end]

七:主函数实现

int main(){
    char dd[]={'a','b','c','d','e','f','g','h','i','j','k','l'};
    adjListGraph<char,int> alg(12,dd);
    alg.insert('a','d',1);
    alg.insert('a','c',2);
    alg.insert('a','b',3);
    alg.insert('a','b',1);
    alg.insert('d','g',2);
    alg.insert('b','f',3);
    alg.insert('b','e',1);
    alg.insert('h','g',2);
    alg.insert('h','i',3);
    alg.insert('i','k',1);
    alg.insert('j','l',2);
    alg.remove('a','d');
    alg.remove('b','e');
    alg.dfs();
    alg.bfs();
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值