C++数据结构之图的储存结构——十字链表
一、介紹
1.十字鏈表是有向圖的另一種存儲結構
2.十字鏈表是將有向圖的鄰接表和逆鄰接表結合起來的一種數據結構
tips:為什么說十字鏈表是鄰接表和逆鄰接表結合起來的一種數據結構呢?
??????? 首先我們需要知道的是對于有向圖的鄰接表來說,要計算一個頂點的出度非常簡單,只需要遍歷以這個頂點為鏈表頭的鏈表,計算其中的結點數再減去頂點自身就可以得到出度,但是如果要計算一個頂點的入度該怎么辦呢?在有向圖的鄰接表中我們只能通過遍歷整個鄰接表,計算除了以自身為鏈表頭的其他所有鏈表中,自己這個節點存在的次數,這比計算出度來說顯然麻煩了許多,因此就引入了逆鄰接表,也就是入弧的方式是跟鄰接表反過來的,創建了一個逆鄰接表之后我們就可以和鄰接表中計算出度一樣只需要遍歷一次本節點作為鏈表頭的鏈表就可以得到入度了。而接下來要介紹的十字鏈表,他可以很方便的計算入度和出度,只需要對弧類進行一些數據項的添加即可。
二、類
十字鏈表與鄰接表一樣由兩個類構成,分別是弧類和頂點類
弧類:
class arc//弧類 {public:int tailvertex; //弧尾頂點的位置int headvertex; //弧頭頂點的位置arc* taillink; //指向弧尾相同的下一條弧arc* headlink; //指向弧頭相同的下一條弧int weight; //本弧上的權值 };頂點類:
class vertex//頂點類 {public:int data; //頂點內含的數據arc* firstheadarc; //指向以該頂點為弧頭的第一條弧arc* firsttailarc; //指向以該頂點為弧尾的第一條弧 };????????除了這兩個類之外我們還需要定義一個有向圖類,并在類中利用弧類和頂點類構建一個圖。
有向圖類:
class digraph//有向圖類 {public:vertex* listhead; //鏈表頭int vertexnum; //有向圖中的頂點數量int arcnum; //有向圖中的弧數量void createdigraph();int gethead(int value);void getdegree(); };??????? 在有向圖類中我們定義了三個成員函數,一個是用來創建有向圖的createdigraph(),一個是用來獲取用戶輸入數據對應的鏈表頭(也就是頂點)的gethead(int value),另一個是用來計算用戶輸入頂點的度的getdegree();
void digraph::createdigraph() {cout<<"請輸入頂點數目:"<<endl; //輸入頂點的數量cin>>vertexnum;listhead=new vertex[vertexnum]; //生成多個鏈表頭的類for (int i = 0; i < vertexnum; i++) //循環輸入鏈表頭內含的數據{cout<<"請輸入頂點"<<i<<"的數據:"<<endl;cin>>listhead[i].data; listhead[i].firstheadarc=NULL; //鏈表頭指向的弧初始化為空listhead[i].firsttailarc=NULL;}cout<<"請輸入弧的數量:"<<endl; //輸入弧的數量int v1,v2;cin>>arcnum;for (int i = 0; i < arcnum; i++){cout<<"請輸入要相連的兩個點的數據:分別為弧頭和弧尾"<<endl;cin>>v1>>v2; //輸入兩個相連的頂點int h1=gethead(v1);int h2=gethead(v2);arc* newarc=new arc; //為弧指針分配內存memset(newarc,0,sizeof(arc)); //將指針分配的內存的數據全部初始化為0newarc->headvertex=h1; //將新建的一個弧的弧頭頂點賦值為頂點h1newarc->tailvertex=h2; //將新建的一個弧的弧尾頂點賦值為頂點h2newarc->weight=0; //弧的權重暫時不需要newarc->headlink=listhead[h1].firstheadarc;//把該弧指向弧頭相同的下一條弧指向頂點h1的以頂點h1為弧頭的第一條弧listhead[h1].firstheadarc=newarc;//把以頂點h1為弧頭的第一條弧指向該弧newarc->taillink=listhead[h2].firsttailarc;//把該弧指向弧尾相同的下一條弧指向頂點h2的以頂點h2為弧頭的第一條弧listhead[h2].firsttailarc=newarc; }cout<<"構建完畢"<<endl; }獲取鏈表頭函數gethead:
int digraph::gethead(int value) {int j;for (j = 0; j < vertexnum; j++){if (listhead[j].data==value){break;}}return j; }創建有向圖函數createdigraph:
這一步是最為關鍵的一步,具體的實現步驟為:
1.獲取用戶輸入的頂點數量
2.利用獲取到的頂點數量循環為各個頂點輸入數據并且將頂點的兩個參數初始化為空,分別為firstheadarc(頂點指向的第一條以該頂點為弧頭的弧)和firsttailarc(頂點指向的第一條以該頂點為弧尾的弧),從而避免野指針問題
3.獲取用戶輸入的弧數量
4.利用獲取到的弧數量構建循環從而連接頂點
5.(重點)頂點連接循環:
5.1.讓用戶輸入要連接的兩個頂點的數據(分別為弧頭和弧尾)
5.2.通過gethead函數得到用戶輸入的兩個數據對應的鏈表頭
5.3.創建一條新弧
5.4.將這條新弧的弧頭弧尾分別指向對應的頂點(上面已經得到了對應的鏈表頭)
5.5.插入弧(這一步有點難懂,因為參數有點多),不過總體來說就是新建一條弧,并且將其插入已經存在的弧和對應頂點之間,不過由于一條弧是由弧頭和弧尾構成的,因此每插入一條弧就要更改一次兩個頂點的firstheadarc和firsttailarc。
下面是本人畫的兩幅圖用來幫助理解:
1.用戶輸入1,3???? 插入弧arc1
?2.用戶輸入1,2??? 插入弧arc2
要想更好的理解建議在紙上自己寫寫畫畫,并且最好多讀兩次各個參數代表的意思不然容易搞混
下面上代碼:
void digraph::createdigraph() {cout<<"請輸入頂點數目:"<<endl; //輸入頂點的數量cin>>vertexnum;listhead=new vertex[vertexnum]; //生成多個鏈表頭的類for (int i = 0; i < vertexnum; i++) //循環輸入鏈表頭內含的數據{cout<<"請輸入頂點"<<i<<"的數據:"<<endl;cin>>listhead[i].data; listhead[i].firstheadarc=NULL; //鏈表頭指向的弧初始化為空listhead[i].firsttailarc=NULL;}cout<<"請輸入弧的數量:"<<endl; //輸入弧的數量int v1,v2;cin>>arcnum;for (int i = 0; i < arcnum; i++){cout<<"請輸入要相連的兩個點的數據:分別為弧頭和弧尾"<<endl;cin>>v1>>v2; //輸入兩個相連的頂點int h1=gethead(v1);int h2=gethead(v2);arc* newarc=new arc; //為弧指針分配內存memset(newarc,0,sizeof(arc)); //將指針分配的內存的數據全部初始化為0newarc->headvertex=h1; //將新建的一個弧的弧頭頂點賦值為頂點h1newarc->tailvertex=h2; //將新建的一個弧的弧尾頂點賦值為頂點h2newarc->weight=0; //弧的權重暫時不需要newarc->headlink=listhead[h1].firstheadarc;//把該弧指向弧頭相同的下一條弧指向頂點h1的以頂點h1為弧頭的第一條弧listhead[h1].firstheadarc=newarc;//把以頂點h1為弧頭的第一條弧指向該弧newarc->taillink=listhead[h2].firsttailarc;//把該弧指向弧尾相同的下一條弧指向頂點h2的以頂點h2為弧頭的第一條弧listhead[h2].firsttailarc=newarc; }cout<<"構建完畢"<<endl; }求度函數getdegree:
求度只要定義兩個指針,一個是用來計算出度的outptr,另一個是用來計算入度的inptr,每當指針經過一個節點,相應的度就加一即可,下面上代碼。
void digraph::getdegree() {int outdegree=0,indegree=0;//初始化入度和出度為0cout<<"請輸入要求度的頂點:"<<endl;//用戶輸入頂點int temp;cin>>temp;int ver=gethead(temp);//利用gethead函數得到鏈表頭arc* outptr=listhead[ver].firsttailarc;//定義一個指針用來計算出度while (outptr)//當指針不為空時表示節點存在{outdegree++;//出度加一outptr=outptr->taillink;//指針指向下一個節點}cout<<"出度為:"<<outdegree<<endl;arc* inptr=listhead[ver].firstheadarc;//定義一個指針用來計算入度while (inptr)//當指針不為空時表示節點存在{indegree++;//入度加一inptr=inptr->headlink; //指針指向下一個節點}cout<<"入度為:"<<indegree<<endl;cout<<"度為:"<<outdegree+indegree<<endl;//度等于入度加出度}main函數:
int main() {digraph dg;dg.createdigraph();dg.getdegree();return 0; }三、總結
????????十字鏈表是一個非常不錯的東西,結合了鄰接表和逆鄰接表,主要歸功于弧類內部相對較多的參數得以實現,犧牲了一定的空間換來的較快的入度和出度的求解。
??????? 這是我的第二篇csdn博客,以后在學習的時候也會繼續寫一下自己的理解,如果有幫助到各位的可以給我點點贊,另外如果有沒寫好的歡迎指正,在這里謝謝各位啦!
總結
以上是生活随笔為你收集整理的C++数据结构之图的储存结构——十字链表的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: mysql自增id获取失败
- 下一篇: 1.0 C++远征:数据的封装