最近有個同學在用十字鍊錶做畢設,然後我手癢了就拿過來把他畢設拿過來**翻掉重寫。一方面以前也沒怎麼寫過十字鍊錶,另一方面好久不寫資料結構了隨便找個來練練手。至於他畢設的其他內容嘛應他本人要求結題前就不發了。
圖大概一共有4中儲存方式:鄰接矩陣,鄰接表,邊集陣列,十字鍊錶。
其實前三種資料結構都十分得好理解,尤其是鄰接矩陣。大概所有人都是從這個資料結構開始入門圖論的。他也確實非常得好理解,乙個二維矩陣的第i行第j列表示是否存在一條邊從i到j,當然也可以表示i到j這條邊的權重。
這裡我們就稍微簡單一點處理,0表示兩節點不相連,1表示相互連線。
比如下面左邊的鄰接矩陣可以對應成右邊這張圖。
鄰接矩陣無疑是非常簡單的,但是其缺點也暴露的非常明顯——當是乙個稀疏圖時(節點多,連邊少),尋找乙個點能連到的所有點代價非常大,需要把矩陣整個一行都遍歷一遍。此外,鄰接矩陣的儲存需要空間也恆定為點數量的平方,對於稀疏圖而言,矩陣中會存在大量0表示兩個點之間沒有連線,這樣會存在非常大的空間浪費。
為了解決這個問題,誕生了鄰接表和邊集陣列。
鄰接表和邊集陣列當然稀疏圖上相比鄰接矩陣是有明顯優勢的,從乙個特定節點出發去尋找其他節點時不需要把所有點都看個遍了,但是鄰接表和邊集陣列終究還是在一些特殊問題上遇到些麻煩——不能沿著邊反著找。就比如上圖,如果我需要尋找所有能連線到2的節點,就需要把0,1,3給找個遍了。並且由於鄰接表和邊集陣列的儲存方式,可能找這樣的點得把整個圖都遍歷一次。
基於種種奇怪的需求,一種更健壯的資料結構產生了——十字鍊錶。
我們這裡演示一下如何從鄰接矩陣的角度去看十字鍊錶
我們首先把原來的鄰接矩陣給拆了,只保留有連線的部分
當然這種形狀電腦是絕對沒法儲存的,於是我們乾脆把它拆得更散一點
然後我們橫平豎直地去連線
好了,到這裡十字鍊錶的結構已經展現出來了。
對於上圖中的每乙個方塊,都表示了一條邊;對於圖中任意乙個箭頭,都可以用乙個指標來表示,最後乙個拐角即為空指標。
每個節點都有兩個指標,分別指向第一條指以它為出發點的邊,另一條指向指向以它為目標點的指標
每條邊也有兩個指標,乙個指向與相同出發點的下一條邊(橫著的箭頭),另乙個指向相同目標點的下一條邊(豎著的箭頭)
當然,這顯然是乙個長得比較好看的十字鍊錶。
實際使用中的十字鍊錶可能長這樣:
乍一眼看下去很複雜,但是仔細看會發現每條邊依舊有兩個指標,乙個指向同一行的其他邊(相同源點),另乙個指向同一列的其他邊(相同目標點)。
看到這裡相信大家對十字鍊錶有個基本的認識了,接著我們看**實現
首先是節點類:
可以看到節點類裡面一共有4個私有成員,firstinedge與firstoutedge分別指向以該節點為目標與源的兩條邊,對應下圖中的這幾條線class vertex
;
接著我們來看邊類:
同樣是4個私有成員,src與dst分別指向源點與目標點,nextedge_src與nextedge_dst分別指向下一條相同源點與目標點的邊。即對應下圖中橫著與豎著的指標。其中nextedge_src對應橫著的連線,nextedge_dst對應豎著的連線。class edge
;
最後是一些功能函式的實現,其中大部分都是非常基本的鍊錶操作。唯一需要注意的是刪邊和刪點的時候會把迴圈變數i自己給刪掉,所以一定要提前留個nextedge記錄好i的下一條邊。這樣即使i被刪掉迴圈也能正常繼續。
edge::edge()
edge::edge(vertex * s, vertex * d) :nextedge_dst(nullptr), nextedge_src(nullptr)
void edge::setsrc_dst(vertex * s, vertex * d)
edge * edge::getnextedge_src() const
edge * edge::getnextedge_dst() const
void edge::deleteedge()
} }vertex *d = this->dst;
if (d->firstinedge->src == src) d->firstinedge = d->firstinedge->nextedge_dst;
else
}} delete this;
}vertex * edge::getsrc() const
vertex * edge::getdst() const
edge::~edge()
vertex::vertex(int x, void* p) :firstinedge(nullptr), firstoutedge(nullptr), id(x), data(p)
edge * vertex::getfirstoutedge() const
edge * vertex::getfirstinedge() const
vertex::~vertex()
void vertex::deletevertex()
for (edge *i = firstoutedge; i != nullptr; i = nextedge)
delete this;
}void vertex::setid(int x)
int vertex::getid() const
void * vertex::getdata()
void vertex::linkto(vertex * dst)
bool vertex::linkedwith(vertex * d)
vertex::vertex(int x, void* p) :firstinedge(nullptr), firstoutedge(nullptr), id(x), data(p)
edge * vertex::getfirstoutedge() const
edge * vertex::getfirstinedge() const
vertex::~vertex()
十字鍊錶的實現
include include includeusing namespace std typedef struct olist olist,olpointer typedef struct olhead col head olpointer malloc col create size sizeof...
十字鍊錶(Java)
對於有向圖來說,鄰接表是有缺陷的。關心了出度問題,想要了解入度情況就必須要遍歷整個圖才能知道。反之也一樣。那麼,這一節就介紹有向圖的一種儲存方法,它能將鄰接表和逆鄰接表結合起來 十字鍊錶。定義頂點表結點結構 vertex firstin firstout 其中,firstin表示入邊表頭指標,指向該...
十字鍊錶 Working routine
工作使艾奇快樂。勤奮的工作為國家直接貢獻了gdp,艾奇認為只要對國家有利,即使犧牲自己生命也心甘情願,絕不會因為自己可能受到禍害而躲開。當艾奇無聊的時候,她就會去工作,然而並不是每次工作都是輕鬆而愉悅的。當天艾奇又一次來到了學校,等待著她的是乙個有n 行m 列的巨大的矩陣和q個任務。對於每個任務,艾...