有向圖的十字連結串列儲存及相關演算法
# 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;
}