当前位置:   article > 正文

【数据结构】图的基本操作_1、分别以临接矩阵和临接表作为存储结构,实现以下图的基本操作: (1)增加新节点 (

1、分别以临接矩阵和临接表作为存储结构,实现以下图的基本操作: (1)增加新节点 (

一、问题描述

分别以邻接矩阵和邻接表作为存储结构,实现以下图的基本操作:

  • 增加一个新结点v,Insert(G,v);
  • 删除顶点v及其相关的边,Delete(G,v);
  • 增加一条边<v,w>,Insert(G,v,w);
  • 删除一条边<v,w>,Delete(G,v,w);

二、设计思路

1、邻接矩阵实现:

        邻接矩阵实现图的基本操作,主要通过二维数组寻址方式进行数据处理。函数包括:邻接矩阵创建并存储数据、顶点寻址、增加顶点、删除顶点、增加边、删除边、邻接矩阵打印。

        邻接矩阵创建并存储,先根据输入的总顶点数、边数对二维数组初始化,边权为MaxInt,表示两点无关联。将顶点信息存储至顶点数组集中。存储数据时,依次循环根据输入边相关的两点以及权重,先通过顶点寻址函数寻找两点位置,后利用二维数组将矩阵相应位置边权赋予新值,完成数据存储。

        顶点寻址函数设计,只需根据顶点信息,在顶点数组集中遍历,返回其对应数组下标即可。

        增加顶点时,在顶点数组集中增加一位,并将邻接矩阵中多加的一行一列赋值为MaxInt。

        删除顶点时,需要通过数组移动,列方面从待删点开始右侧数据,全部左移;行方面从待删顶点开始下方数据,全部上移。最后删除顶点在顶点数组中的位置。上述操作通过数组移动实现开销较大,也可以在删除时,将最后一个顶点相关信息替代删除顶点,因为本身顶点在数组中的位置无现实意义,可减少算法时间复杂度。

        增加边时,通过顶点寻址函数获取两个顶点位置,通过二维数组将邻接矩阵中对应的边权赋予新值即可。

        删除边时,通过顶点寻址函数获取两个顶点位置,通过二维数组将邻接矩阵中对应的边权赋予MaxInt即可。

        邻接矩阵打印,只需通过二维数组,将其各行各列依次输出,为了增加矩阵可观性,可输出行名、列名以及控制权重输出格式。

        最后通过主函数测试上述所有的函数。

2、邻接表实现:

        邻接表实现图的基本操作,主要通过数组+链表方式进行数据处理。函数包括:邻接表创建并存储数据、顶点寻址、增加顶点、删除顶点、增加边、删除边、邻接表打印。

        邻接表创建并存储,先根据输入的总顶点数、边数对邻接表初始化,数组单元接收顶点信息,由于开始无边,将各单元firstarc指针赋NULL,避免野指针。存储边信息时,创建两个边结点并创建相应指针,在边结点中存储边权以及关联点通过顶点寻址函数查找的下标(如a、b两个顶点,则边结点e1中adjvex存放为b的下标,边结点e2中adjvex存放为a的下标)。并将边结点插入各自数组单元的关联顶点链表首结点(即被各单元firstarc指针指向)。

        顶点寻址函数设计,只需根据顶点信息,在顶点数组集中遍历,返回其对应数组下标即可。

        增加顶点,只需在邻接表数组单元vertices中存储新的顶点信息,并将其firstarc指针赋值为NULL。

        删除顶点,下述操作:先删除待删顶点中所有关联顶点链表中其他顶点信息 ;用最后一个顶点替代删除结点位置(目的在于避免顶点表中出现废弃点占位情况)。实现操作需要遍历邻接表数组单元vertices中关联顶点链表中所有的adjvex,与待删顶点的位置下标比对,相同时删除该结点。结点删除时,需要单独优先考虑表头首结点元素中adjvex是否为待删结点下标,循环删除至表头结点adjvex不为待删结点下标为止,遍历后续所有结点(优先考虑的原因是表头结点有一个firstarc指向问题,不考虑则会出现断链情况)。删除完待删结点在其他单元的信息时,循环待删结点所在单元格关联顶点链表所有结点,将firstarc赋值为NULL。最后将数组vertices中最后一个顶点替代待删结点位置(数组前移覆盖会增加额外的开销)。

        增加边即为上述邻接表存储步骤单次操作,通过创建两个边结点,分别赋值相应数据后,插入两个对应vertices单元格的关联顶点链表的表头(即成为首结点)。

        删除边,即为删除顶点中单次操作,先定位待删边的两个顶点位置,然后遍历两个顶点对应数组vertices单元中的关联顶点链表,查找adjvex的值,删除含有对应顶点位置的边结点,同样注意单独考虑首结点是否含有删除对应顶点位置信息。

        用于删除函数多次使用,因此在优化时,将输出函数提取。

        邻接表打印时,依次访问vertices所有单元中的关联顶点链表,输出所有其中边结点的adjvex对应的顶点信息以及边权。

        最后通过主函数测试上述所有函数。

三、代码展示

1、邻接矩阵实现:

  1. #include<iostream>
  2. using namespace std;
  3. typedef int Status;
  4. #define OK 1
  5. #define ERROR -1
  6. #define MaxInt 32767 //初始化边权无穷大
  7. #define MVNum 100
  8. #define VerTexType char
  9. #define ArcType int
  10. typedef struct AMGraph{ //定义邻接矩阵
  11.  VerTexType vexs[MVNum];    //创建顶点集
  12.  ArcType arcs[MVNum][MVNum];  //创建邻接矩阵
  13.  int vexnum,arcnum;    //存放当前图的总顶点数和边数
  14. };
  15. int VexLocate(AMGraph &G,VerTexType u){ //返回顶点在顶点集的位置
  16.  for(int i=0;i<G.vexnum;i++){ //循环遍历顶点集
  17.   if(u==G.vexs[i]) return i; //找到顶点返回其数组下标
  18.  }
  19.  return -1;
  20. }
  21. Status UDNCreate(AMGraph &G){ //无向邻接矩阵创建并存储数据
  22.  cout<<"请输入无向图总顶点数和总边数:";
  23.  cin>>G.vexnum>>G.arcnum;   //输入图的顶点数和边数
  24.  cout<<"请输入顶点信息:";
  25.  for(int i=0;i<G.vexnum;i++){
  26.   cin>>G.vexs[i];
  27.  }
  28.  for(int i=0;i<G.vexnum;i++){  //初始化邻接矩阵边权为无穷 
  29.   for(int j=0;j<G.vexnum;j++){
  30.    G.arcs[i][j]=MaxInt;
  31.   }
  32.  }
  33.  VerTexType v1,v2; //结点信息
  34.  ArcType w; //边权重
  35.  cout<<"请输入边的两顶点及所构边的权重:"<<endl; 
  36.  for(int i=0;i<G.arcnum;i++){ //利用二维数组寻址相对两点赋权
  37.   cin>>v1>>v2>>w;     //依次输入两个顶点以及所构边的权重
  38.   G.arcs[VexLocate(G,v1)][VexLocate(G,v2)]=w;
  39.   G.arcs[VexLocate(G,v2)][VexLocate(G,v1)]=w;
  40.  }
  41.  return OK;
  42. }
  43. Status VexInsert(AMGraph &G,VerTexType v){ //插入顶点
  44.  G.vexs[G.vexnum]=v;     //由于数组下标比数量少1,可用旧顶点数做下标
  45. //增加新顶点 
  46.  G.vexnum++;         //顶点数加1 
  47.  for(int i=0;i<G.vexnum;i++){  //构建新顶点与其他顶点关联,初始化边权
  48. //为无穷 
  49.   G.arcs[G.vexnum-1][i]=MaxInt;
  50.   G.arcs[i][G.vexnum-1]=MaxInt;
  51.  }
  52. }
  53. Status VexDelete(AMGraph &G,VerTexType v){ //顶点删除
  54.  int p=VexLocate(G,v); //存放待删顶点下标位置
  55.  for(int i=0;i<G.vexnum;i++){ //删除待删顶点所在列数据
  56.   for(int j=p+1;j<G.vexnum;j++){
  57.    G.arcs[i][j-1]=G.arcs[i][j];
  58.   }
  59.  }
  60.  for(int i=0;i<G.vexnum;i++){ //删除待删顶点所在行数据
  61.   for(int j=p+1;j<G.vexnum;j++){
  62.    G.arcs[j-1][i]=G.arcs[j][i];
  63.   }
  64.  }
  65.  for(int i=p+1;i<G.vexnum;i++){ //删除在顶点集中的待删顶点
  66.   G.vexs[i-1]=G.vexs[i];
  67.  }
  68.  G.vexnum--; //顶点数自减
  69.  return OK;
  70. }
  71. Status ArcInsert(AMGraph &G,VerTexType v1,VerTexType v2,ArcType w)
  72. { //插入边
  73.  int p1=VexLocate(G,v1),p2=VexLocate(G,v2); //存放两个关联点位置
  74.  G.arcs[p1][p2]=w; //将关联点在矩阵两处边权赋值
  75.  G.arcs[p2][p1]=w;
  76.  return OK;
  77. }
  78. Status ArcDelete(AMGraph &G,VerTexType v1,VerTexType v2){//删除边
  79.  int p1=VexLocate(G,v1),p2=VexLocate(G,v2); //存放两个关联点位置
  80.  G.arcs[p1][p2]=MaxInt; //将关联点在矩阵两处边权赋为无穷
  81.  G.arcs[p2][p1]=MaxInt;
  82.  return OK; 
  83. }
  84. Status AMGraphPrint(AMGraph &G){ //打印无向邻接矩阵
  85.  cout<<"邻接矩阵展示:"<<endl;
  86.  for(int i=0;i<G.vexnum;i++){
  87.   cout<<G.vexs[i]<<":"; //输出矩阵行名
  88.   for(int j=0;j<G.vexnum;j++){ //循环输出各点与其他点的边权,0为无边
  89.    if(G.arcs[i][j]!=MaxInt) cout<<G.arcs[i][j]<<"\t";
  90.    else cout<<"0"<<"\t";
  91.   }
  92.   cout<<endl;
  93.  }
  94.  return OK;
  95. }
  96. int main(){
  97.  AMGraph G; //创建无向邻接矩阵
  98.  UDNCreate(G); //无向邻接矩阵初始化并存储数据
  99.  AMGraphPrint(G); //打印无向邻接矩阵
  100.  VerTexType v1,v2;
  101.  ArcType w;
  102.  cout<<"请输入要插入的顶点:";
  103.  cin>>v1;
  104.  VexInsert(G,v1); //插入顶点
  105.  AMGraphPrint(G); //打印无向邻接矩阵
  106.  cout<<"请输入要删除的顶点:";
  107.  cin>>v2;
  108.  VexDelete(G,v2); //删除顶点
  109.  AMGraphPrint(G); //打印无向邻接矩阵
  110.  cout<<"请输入增加边的两个顶点及边权重:";
  111.  cin>>v1>>v2>>w;
  112.  ArcInsert(G,v1,v2,w); //插入边
  113.  AMGraphPrint(G); //打印无向邻接矩阵
  114.  cout<<"请输入删除边的两个顶点:";
  115.  cin>>v1>>v2;
  116.  ArcDelete(G,v1,v2); //删除边
  117.  AMGraphPrint(G); //打印无向邻接矩阵
  118.  return 0;
  119. }

2、邻接表实现:

  1. #include<iostream>
  2. using namespace std;
  3. typedef int Status;
  4. #define OK 1
  5. #define ERROR -1
  6. #define VerTexType char
  7. #define ArcType int
  8. #define MVNum 50 
  9. typedef struct ArcNode{    //边结点 
  10.  int adjvex;      //该边所指向的顶点位置
  11.  struct ArcNode* nextarc;  //指向下一条边指针
  12.  int weight;      //边的权重 
  13. };
  14. typedef struct VNode{    //顶点信息 
  15.  VerTexType data;    //顶点数据 
  16.  ArcNode *firstarc;    //指向第一条与该点相连的边的指针 
  17. }VNode,AdjList[MVNum];    //邻接表 
  18. typedef struct{      //定义图 
  19.  AdjList vertices;    //顶点表 
  20.  int vexnum,arcnum;    //图当前顶点数与边数 
  21. }ALGraph;
  22. int VexLocate(ALGraph &G,VerTexType v){  //顶点定位,找出其在邻接表
  23. //中数组下标的位置 
  24.  for(int i=0;i<G.vexnum;i++){
  25.   if(v==G.vertices[i].data) return i;  //根据顶点数据查找,返回表中数
  26. //组下标值 
  27.  }
  28.  return -1;
  29. }
  30. Status ALGraphprint(ALGraph &G){    //邻接表打印函数 
  31.  cout<<"操作后新的连接表如下:"<<endl;
  32.  for(int i=0;i<G.vexnum;i++){    //依次输出每个顶点与相连顶点的情况
  33. //以及边权 
  34.   cout<<G.vertices[i].data<<":";    //输出当前顶点数据 
  35.   ArcNode* p=G.vertices[i].firstarc;  //指向与当前顶点相连的第一个顶
  36. //点的边的指针 
  37.   while(p!=NULL){        //依次遍历所有与该顶点相连的点的边 
  38.    cout<<G.vertices[p->adjvex].data<<"("<<p->weight<<")"<<"\t"
  39. //输出与该顶点相连的所有点以及边的权重
  40.    p=p->nextarc;
  41.   }
  42.   cout<<endl;
  43.  }
  44.  return OK;
  45. }
  46. Status Delete(ALGraph &G,int pos1,int pos2)//删除邻接表中,指定两
  47. //顶点的边 
  48.  ArcNode* p=G.vertices[pos1].firstarc;  //指向pos1所表示顶点的与其
  49. //第一个相连顶点的边的指针
  50.  if(p&&p->adjvex==pos2){      //由于firstarc指针固定,不能单纯删除p
  51. //的结点,因此表头结点需要单独考虑 
  52.   G.vertices[pos1].firstarc=p->nextarc; //如果表头结点存放连接信息为
  53. //要删结点位置信息pos2,直接删除
  54.   delete p;
  55.  }
  56.  else{
  57.   while(p!=NULL){        //否则从p开始循环遍历表中所有连接点
  58.    if(p->nextarc!=NULL&&p->nextarc->adjvex==pos2){  //如果找到要删
  59. //结点pos2,进行链表删除操作
  60.     ArcNode* temp=p->nextarc;
  61.     p->nextarc=p->nextarc->nextarc;
  62.     delete temp;
  63.     break;
  64.    }
  65.    p=p->nextarc;       //没有找到,p指针后移 
  66.   }
  67.  }
  68.  return OK;
  69. }
  70. Status UDGCreate(ALGraph &G){ //邻接表创建并存储数据
  71.  cout<<"请输入图的总顶点数和总边数:";
  72.  cin>>G.vexnum>>G.arcnum;
  73.  cout<<"请输入顶点信息:";
  74.  for(int i=0;i<G.vexnum;i++){    //初始化表头结点指针域为NULL 
  75.   cin>>G.vertices[i].data;     //输入顶点数据 
  76.   G.vertices[i].firstarc=NULL;
  77.  }
  78.  VerTexType v1,v2;
  79.  ArcType w;
  80.  int pos1,pos2;
  81.  cout<<"请输入边的两顶点及所构边的权重:"<<endl;
  82.  for(int i=0;i<G.arcnum;i++){    //输出边的信息 
  83.   cin>>v1>>v2>>w;
  84.   pos1=VexLocate(G,v1);      //获取顶点v1位置 
  85.   pos2=VexLocate(G,v2);      //获取顶点v2位置 
  86.   ArcNode* p1=new ArcNode;     //创建边结点 
  87.   p1->adjvex=pos2;      //v1开始的边所连另一端为v2位置
  88.   p1->weight=w;
  89.   p1->nextarc=G.vertices[pos1].firstarc;  
  90.   G.vertices[pos1].firstarc=p1;    //将边加入v1的边表头部
  91.   ArcNode* p2=new ArcNode;      //创建边结点
  92.   p2->adjvex=pos1;        //v2开始的边所连另一端为v1
  93.   p2->weight=w;
  94.   p2->nextarc=G.vertices[pos2].firstarc;
  95.   G.vertices[pos2].firstarc=p2;    //将边加入v2的边表头部 
  96.  }
  97.  return OK; 
  98. }
  99. Status VexInsert(ALGraph &G,VerTexType v){  //实现顶点的插入 
  100.  G.vertices[G.vexnum].data=v;    //由于数组下标从0开始,则新增顶点存
  101. //放位置为G.vexnum 
  102.  G.vertices[G.vexnum].firstarc=NULL; //避免野指针情况,指针域为NULL 
  103.  G.vexnum++;          //顶点数自增 
  104. }
  105. Status VexDelete(ALGraph &G,VerTexType v){  //顶点删除 
  106.  int pos=VexLocate(G,v);       //查找待删除顶点 
  107.  for(int i=0;i<G.vexnum;i++){ //删除其他顶点连接表中的待删除顶点信息
  108.   Delete(G,i,pos);
  109.   
  110.   //下半部份代码优化为Delete()函数 
  111. //  ArcNode* p=G.vertices[i].firstarc;
  112. //  if(p&&p->adjvex==pos){
  113. //   G.vertices[i].firstarc=p->nextarc;
  114. //   delete p;
  115. //  }
  116. //  else{
  117. //   while(p!=NULL){
  118. //    if(p->nextarc!=NULL&&p->nextarc->adjvex==pos){
  119. //     ArcNode* temp=p->nextarc;
  120. //     p->nextarc=p->nextarc->nextarc;
  121. //     delete temp;
  122. //     break;
  123. //    }
  124. //    p=p->nextarc;
  125. //   }
  126. //  }
  127.  }
  128.  
  129. //下述操作:1、先删除待删顶点中所有连接表中其他顶点信息 2、用最后一个顶
  130. //点替代删除结点位置(目的在于避免顶点表中出现废弃点占位情况)
  131.  ArcNode* p=G.vertices[pos].firstarc;  //p指向待删顶点的第一个连接
  132. //点的边的指针 
  133.  G.vertices[pos].firstarc=NULL;     //野指针问题,删除记得赋值为NULL,
  134. //否则运行无结果 
  135.  while(p!=NULL){        //循环删除该点连接表下所有顶点信息 
  136.   ArcNode* temp=p;
  137.   p=p->nextarc;
  138.   delete temp;
  139.  }
  140.  G.vertices[pos]=G.vertices[G.vexnum-1];  //将最后一个顶点替代删除
  141. //顶点的位置 
  142.  G.vexnum--;          //顶点数自减 
  143.  return OK; 
  144. }
  145. Status ArcInsert(ALGraph &G,VerTexType v1,VerTexType v2,int w){  
  146. //插入边 
  147.  int pos1,pos2;
  148.  pos1=VexLocate(G,v1);
  149.  pos2=VexLocate(G,v2);
  150.  ArcNode* p1=new ArcNode;     //创建边结点 
  151.  p1->adjvex=pos2;        //v1开始的边所连另一端为v2位置 
  152.  p1->weight=w;
  153.  p1->nextarc=G.vertices[pos1].firstarc;  
  154.  G.vertices[pos1].firstarc=p1;//将边加入v1的边表头部
  155.  ArcNode* p2=new ArcNode;     //创建边结点
  156.  p2->adjvex=pos1;        //v2开始的边所连另一端为v1
  157.  p2->weight=w;
  158.  p2->nextarc=G.vertices[pos2].firstarc;
  159.  G.vertices[pos2].firstarc=p2;    //将边加入v2的边表头部
  160.  G.arcnum++;          //边数自增 
  161.  return OK;
  162. }
  163. Status ArcDelete(ALGraph &G,VerTexType v1,VerTexType v2)//删除边
  164.  int pos1,pos2;
  165.  pos1=VexLocate(G,v1);      //存放删除边的第一个顶点位置 
  166.  pos2=VexLocate(G,v2);      //存放删除边的第二个顶点位置 
  167.  ArcNode* p1=G.vertices[pos1].firstarc;  //指向第一个顶点的连接表中
  168. //与该顶点相连的边的指针 
  169.  ArcNode* p2=G.vertices[pos2].firstarc;  //指向第二个顶点的连接表中
  170. //与该顶点相连的边的指针
  171.  Delete(G,pos1,pos2);      //在第一个顶点连接表中删除第二顶点的信息 
  172.  Delete(G,pos2,pos1);      //在第二个顶点连接表中删除第一顶点的信息
  173.  
  174.  //下半部份代码用Delete()函数进行优化替代 
  175. // if(p1&&p1->adjvex==pos2){
  176. //  G.vertices[pos1].firstarc=p1->nextarc;
  177. //  delete p1;
  178. // }
  179. // else{
  180. //  while(p1!=NULL){
  181. //   if(p1->nextarc!=NULL&&p1->nextarc->adjvex==pos2){
  182. //    ArcNode* temp=p1->nextarc;
  183. //    p1->nextarc=p1->nextarc->nextarc;
  184. //    delete temp;
  185. //    break;
  186. //   }
  187. //   p1=p1->nextarc;
  188. //  }
  189. // }
  190. // if(p2&&p2->adjvex==pos1){
  191. //  G.vertices[pos2].firstarc=p2->nextarc;
  192. //  delete p2;
  193. // }
  194. // else{
  195. //  while(p2!=NULL){
  196. //   if(p2->nextarc!=NULL&&p2->nextarc->adjvex==pos1){
  197. //    ArcNode* temp=p2->nextarc;
  198. //    p2->nextarc=p2->nextarc->nextarc;
  199. //    delete temp;
  200. //    break;
  201. //   }
  202. //   p2=p2->nextarc;
  203. //  }
  204. // }
  205.  G.arcnum--; //边数自减
  206.  return OK;
  207. }
  208. int main(){
  209.  ALGraph G;
  210.  UDGCreate(G);          //构建图 
  211.  ALGraphprint(G);         //打印图 
  212.  VerTexType v1,v2;
  213.  int w;
  214.  cout<<"请输入要插入的顶点:";
  215.  cin>>v1;
  216.  VexInsert(G,v1);        //插入顶点 
  217.  ALGraphprint(G);
  218.  cout<<"请输入要删除的顶点:";
  219.  cin>>v1;
  220.  VexDelete(G,v1);        //删除顶点 
  221.  ALGraphprint(G);
  222.  cout<<"请输入要插入边的两顶点及边权重:";
  223.  cin>>v1>>v2>>w;
  224.  ArcInsert(G,v1,v2,w);   //插入边 
  225.  ALGraphprint(G);
  226.  cout<<"请输入要删除边的两顶点:";
  227.  cin>>v1>>v2;
  228.  ArcDelete(G,v1,v2);     //删除边 
  229.  ALGraphprint(G);
  230.  return 0;
  231. }

四、结果展示

1、邻接矩阵结果:

 2、邻接表结果:

 五、算法时间复杂度

        1、邻接矩阵实现图的基本操作,函数包括:邻接矩阵创建并存储数据、顶点寻址、增加顶点、删除顶点、增加边、删除边、邻接矩阵打印。其中操作建立于二维数组,与数据规模有关,设总顶点数与总边数数据规模为n,则总体算法时间复杂度为O(n2)。

        2、邻接表实现图的基本操作,主要通过数组+链表方式进行数据处理。函数包括:邻接表创建并存储数据、顶点寻址、增加顶点、删除顶点、增加边、删除边、邻接表打印。其中操作主要建立于链表上,与数据规模有关,设总顶点数与总边数数据规模为n,则总体算法时间复杂为O(n)。

PS:第一次写博客,有问题欢迎交流。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/运维做开发/article/detail/984740
推荐阅读
相关标签
  

闽ICP备14008679号