有向图的十字链表储存及相关算法

# include<iostream>
# include<stdlib.h>
# include<queue>
# include<cstring>
using namespace std;
const int N = 10050;
typedef struct Arcbox{
int tailvex,headvex;//弧尾和弧头的 位置(方向是弧尾能走到弧头)
struct Arcbox *hlink,*tlink;//相同弧头,相同弧尾的弧的链

}Arcbox;//存储边的信息

typedef struct{
  int data;//数据域
  Arcbox *firstin,*firstout;//指向该顶点的第一条入弧和第一条出弧
}Vexnode;//存储点的信息

typedef struct{
  Vexnode xlist[N];//表头向量(数组)
  int vexnum,arcnum;//有向图当前顶点数目和弧数
}OLGraph;

int Locatevex(OLGraph G,int u){
/*返回顶点u在有向图G中的位置(序号),不存在就返回-1*/
  int i;
  for(i = 1;i <= G.vexnum;++i)

  {
    if(G.xlist[i].data == u)
     return i;
  }
  return -1;
}

bool CreatDG(OLGraph * G){
/*建图*/
  Arcbox * p;
  printf("请输入有向图的顶点数,弧数");
  cin>>G->vexnum>>G->arcnum;
  cout<<"请输入有向图"<<G->vexnum<<"个顶点的数据"<<endl;
  for(int i = 1;i <= G->vexnum;++i)

  {
    cin>>G->xlist[i].data;
    G->xlist[i].firstin = NULL;//初始化入弧
    G->xlist[i].firstout = NULL;//初始化出弧
  }

  cout<<"请输入"<<G->arcnum<<"条边的弧尾和弧头";
  for(int i = 1;i <= G->arcnum;++i)

  {
    int v1,v2;
    cin>>v1>>v2;
    int j = Locatevex(*G,v1);//找到与v1值相同的点的下标
    int k = Locatevex(*G,v2);//找到与v2值相同的点的下标
    p = new Arcbox;//p指向一个新的边
    p->tailvex = j;//新边的弧尾是j(v1)
    p->headvex = k;//新边的弧头是k(v2)
    p->hlink = G->xlist[k].firstin;
    p->tlink = G->xlist[j].firstout;
    G->xlist[k].firstin = G->xlist[j].firstout = p;
    /*头插法*/
  }
  return true;
}

void DestroyGraph(OLGraph *G)

{
/*初始条件:存在图G*/
/*操作结果:摧毁有向图G*/
  Arcbox *p,*q;
  for(int i = 1;i <=G->vexnum;++i)

  {
    p = G->xlist[i].firstout;//指向该顶点的第一条出边
    while(p)

    {
      q = p;
      p = p->tlink;//指向该顶点的下一条出边
      free(q);//释放出边
    }
  }
  G->arcnum = G->vexnum = 0;
  }

int * GetVex(OLGraph * G,int v)

{
    if(v>=G->vexnum||v < 0) exit(-1);//循环中不要用,因为在图中如果调用到没有值得点会被终止程序,造成逻辑错误
    return &G->xlist[v].data;//返回顶点v的值域地址
}

bool PutVex(OLGraph *G,int v,int val)

{
  /*修改顶点v的值为val*/
  int i = Locatevex(*G,v);
  if(i < 0)//如果不存在点值为v的点
    return false;//修改失败
  G->xlist[i].data = val;
    return true;
}

int FirstAdjvex(OLGraph G,int v)

{
/*返回值为v的顶点的第一个领接顶点的序号,若没有领接顶点则返回-1*/
  int i;
  Arcbox *p;
  i = Locatevex(G,v);
  p = G.xlist[i].firstout;
  if(p)
    return p->headvex;//指向该顶点的弧头
  else
    return -1;
}

int NextAdjvex(OLGraph G,int v,int w){
/*初始条件:有向图G存在,v是图G的顶点,w是v的领接点*/
/*操作结果:返回值为v的顶点领接点中,在w之后的下一个领接顶点的序号*/
/*若w是v的最后一个领接顶点,返回-1*/
  Arcbox * p;
  int i = Locatevex(G,v);
  int j = Locatevex(G,w);
  p = G.xlist[i].firstout;
  while(p && p->headvex != j)
    p = p->tlink;
  if(p) /*w不是v的最后一个领接点*/
    p = p->tlink;/*指向w的下一个领接点*/
  if(p)/*w后是否存在下一个领接点*/
    return p->headvex;/*有则输出*/
  else return -1;
}

void Insertvex(OLGraph *G,int v)

{
/*在 图中增加新的顶点,不增加边*/
  G->xlist[++G->vexnum].data = v;
  G->xlist[G->vexnum].firstin = G->xlist[G->vexnum].firstout = NULL;

}

bool Deletevex(OLGraph *G,int v)

{
/*删除顶点v及其相关边*/
  int j,k;
  Arcbox *p,*q;
  k = Locatevex(*G,v);//获取值为v的顶点下标
  if(k < 0) return false;//若不存在值为v的顶点就返回删除失败
  /*删除顶点v的出弧*/
  for(j = 1;j <= G->vexnum;++j)
  {
    if(j == k) continue;//先不动顶点v
    p = G->xlist[j].firstin;/*顶点v的出弧就是其他点的入弧*/
    while(p)
      if(p->tailvex == k && (p == G->xlist[j].firstin))/*待删节点为首节点,也就是说我们要删除的节点没有领接点*/
      {
        G->xlist[j].firstin = p->hlink;/*此时p->hlink为NULL*/
        break;
      }
      else if (p->tailvex != k)//如果边p的弧尾不是v
      {
        q = p;//q储存上一条边的位置
        p = p->hlink;//p指向下一条边
      }
      else
      {
        q->hlink = p->hlink;//上一条边的链指向下一条边的下一条边
        break;
      }
  }
  /*删除与顶点v有关的出弧*/
  p = G->xlist[k].firstout;
  while(p)
  {
    q = p->tlink;//q指向下一条出边
    free(p);//释放当前边
    G->arcnum--;//总边数减少
    p = q;//p也指向下一条出边
  }
  /*删除顶点v的入弧*/
  for(j = 1;j <= G->vexnum;++j)
  {
    if(j == k) continue;
    p = G->xlist[j].firstout;/*顶点v的入弧就是其他顶点的出弧*/
    while(p)
      if(p->headvex == k &&p == G->xlist[j].firstout)//若p指向该顶点的第一条(它的弧头为v)出边且该顶点只有这一条出边
      {
        G->xlist[j].firstout = p->tlink;//让顶点j的出边指向NULL
        break;
      }
      else if(p->headvex != k)//如果该出边弧头不是v
      {
        q = p;//q指向上一条出边
        p = p->tlink;//p指向下一条出边
      }
      else
      {
        q->tlink = p->tlink;//上一条出边的链指向下一条出边的下一条出边
        break;
      }
  }
  /*删除与顶点v有关的入弧*/
  p = G->xlist[k].firstin;
  while(p)
  {
    q = p->hlink;
    free(p);
    G->arcnum--;
    p = q;
  }
  /*改变顶点位于图中的位置(下标)*/
  for(j = k+1;j <= G->vexnum;++j) G->xlist[j-1] = G->xlist[j];//所有顶点向前移一位
    G->vexnum--;//减少总顶点数
  for(j = 1;j <= G->vexnum;++j)
  {
    p = G->xlist[j].firstout;
    while(p)
    {
      if(p->tailvex>k)
      p->tailvex--;//顶点大小-1
      if(p->headvex>k)
      p->headvex--;//顶点大小-1
      p = p->tlink;
    }
  }
  return true;
}

bool InsertArc(OLGraph *G,int v,int w)

{
  /*对图G中点v和点w加一条边<v,w>*/
  int i = Locatevex(*G,v);
  int j = Locatevex(*G,w);
  Arcbox * p;
  if(i<0 || j<0) return false;
  p = new Arcbox;
  p->tailvex = i;
  p->headvex = j;
  p->hlink = G->xlist[j].firstin;
  p->tlink = G->xlist[i].firstout;/*头插法*/
  G->xlist[j].firstin = G->xlist[i].firstout = p;
  G->arcnum++;
  return true;
}

bool DeleteArc(OLGraph * G,int v,int w)

{
  /*删除边<v,w>*/
  int i = Locatevex(*G,v);/*弧尾序号*/
  int j = Locatevex(*G,w);/*弧头序号*/
  Arcbox *p,*p2;
  if(i<0||j<0 || i==j) return false;
  p2 = G->xlist[i].firstout;//删除弧尾节点
  if(p2&&p2->headvex == j) G->xlist[i].firstout = p2->tlink;//如果第一个领接边为待删除边
  else

  {
    while(p2&&p2->headvex!=j)//向后找
    {
      p = p2;
      p2 = p2->tlink;
    }
    if(p2)/*没到表尾*/
    {
      p->tlink = p2->tlink;
    }
  }
  p2 = G->xlist[j].firstin;//处理入边
  if(p2&&p2->tailvex == i) G->xlist[j].firstin = p2->hlink;
  else

  {
    while(p2&&p2->tailvex!=i)

    {
      p = p2;
      p2 = p2->hlink;
    }
    if(p2) p ->hlink = p2->hlink;
  }
  free(p2);/*释放边*/
  G->arcnum--;//弧数减一
  return true;
}

bool vis[N];//标记是否遍历过该点
void DFS(OLGraph G,int i)

{

  Arcbox *p;
  vis[i] = true;//标记当前结点已经来过
  cout<<G.xlist[i].data<<" ";/*输出结点值域*/
  p = G.xlist[i].firstout;/*p指向i的第一条出边*/
  while(p && vis[p->headvex]) p = p->tlink;/*如果i存在出边且该出边的弧头已被遍历*/
  if(p && !vis[p->headvex])/*如果p存在且当前p指向的边的弧头尚未被遍历*/
  DFS(G,p->headvex); /*递归调用DFS*/
}
void dfsTraverse(OLGraph G)

{
  for(int i = 1;i <= G.vexnum;++i) vis[i] = false;/*初始化所有结点为尚未被遍历*/
  for(int i = 1;i <= G.vexnum;++i)
  if(!vis[i] ) DFS(G,i);/*如果当前结点尚未被遍历,则调用DFS*/
  cout<<endl;
}

void BFS(OLGraph G){
  for(int i = 1;i <=G.vexnum;++i) vis[i] = false;/*初始化所有结点为尚未被遍历*/
  queue<int> q;/*创建队列*/
  for(int i =1;i <= G.vexnum;++i)
  {
    if(!vis[i])/*如果当前结点没被遍历*/
    {
      vis[i] = true;/*设为遍历过*/
      cout<<G.xlist[i].data<<" ";/*输出结点信息*/
      q.push(i);/*点i入队*/
      while(!q.empty())/*队列非空*/
      {
        int top = q.front();/*top接收队头元素*/
        int k = *GetVex(&G,top);/*k获取顶点top的值域*/
        q.pop();/*队头出队*/
        for(int w = FirstAdjvex(G,k);w != -1; w = NextAdjvex(G,k,G.xlist[w].data))/*遍历顶点top的所有出边*/
          if(!vis[w])/*如果尚未被遍历*/
          {
            vis[w] = 1;/*设为被遍历过*/
            cout<<G.xlist[w].data<<" ";/*输出值域*/
            q.push(w); /*入队*/

          }
      }
    }
  }

  cout<<endl;
}
int main()

{
  int n;
  OLGraph G;
  int v1,v2;
  CreatDG(&G);
  dfsTraverse(G);
  printf("清修改顶点的值,输入原值,新值:");
  cin>>v1>>v2;
  PutVex(&G,v1,v2);
  printf("请插入新顶点,并输入顶点的值:");
  cin>>v1;
  Insertvex(&G,v1);
  printf("请输入新顶点相关的弧,请输入弧数:");
  cin>>n;
  for(int i = 1;i <= n;++i)

  {
    printf("请输入另一顶点的值,另一顶点的方向(0:弧头,1:弧尾)");
    int j;
    cin>>v2>>j;
    if(j)

      InsertArc(&G,v2,v1);
    else
      InsertArc(&G,v1,v2);
  }

  dfsTraverse(G);

  printf("删除一条弧,请输入待删除弧的弧尾 弧头:");
  cin>>v1>>v2;
  DeleteArc(&G,v1,v2);
  dfsTraverse(G);
  printf("删除顶点及其相关弧,请输入顶点的值:");
  cin>>v1;
  Deletevex(&G,v1);
  dfsTraverse(G);
  BFS(G);
  return 0;
}

posted @ 2022-02-17 14:41  empty_y  阅读(161)  评论(0)    收藏  举报