1. 程式人生 > 其它 >有向圖的十字連結串列儲存及相關演算法

有向圖的十字連結串列儲存及相關演算法

# 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;
}