實現表的另一種方法是用指標將儲存表元素的那些單元依次串聯在一起。這種方法避免了在陣列中用連續的單元儲存元素的缺點,因而在執行插入或刪除運算時,不再需要移動元素來騰出空間或填補空缺。然而我們為此付出的代價是,需要在每個單元中設定指標來表示表中元素之間的邏輯關係,因而增加了額外的儲存空間的開銷。
為了將儲存表元素的所有單元用指標串聯起來,我們讓每個單元包含乙個元素域和乙個指標域,其中的指標指向表中下乙個元素所在的單元。例如,如果表是a1,a2,…,an ,那麼含有元素ai的那個單元中的指標應指向含有元素ai+1的單元(i=1,2,…,n-1)。含有an的那個單元中的指標是空指標nil。此外,通常我們還為每乙個表設定乙個表頭單元header,其中的指標指向開始元素中所在的單元,但表頭單元header中不含任何元素。設定表頭單元的目的是為了使表運算中的一些邊界條件更容易處理。這一點我們在後面可以看到。如果我們願意單獨地處理諸如在表的第乙個位置上進行插人與刪除操作等邊界情況,也可以簡單地用乙個指向表的第乙個單元的指標來代替表頭單元。
上述這種用指標來表示表的結構通常稱為單鏈結表,或簡稱為單鏈表或鍊錶
。單鏈表的邏輯結構如圖1所示。表示空表的單鏈表只有乙個單元,即表頭單元header,其中的指標是空指標nil。
圖1 單鏈表示意圖
為了便於實現表的各種運算,在單鏈表中位置變數的意義與用陣列實現的表不同。在單鏈表中位置i是乙個指標,它所指向的單元是元素ai-1所在的單元,而不是元素ai所在的單元(i=2,3,…,n)。位置1是指向表頭單元header的指標。位置end(l)是指向單鏈表l中最後乙個單元的指標。這樣做的目的是為了避免在修改單鏈表指標時需要找乙個元素的前驅元素的麻煩,因為在單鏈表中只設定指向後繼元素的指標,而沒有設定指向前驅元素的指標。
在單鏈表中,表型別tlist和位置型別tposition一樣,都是指向某一單元的指標。尤其可以是指向表頭單元的指標。
單鏈表結構的主要型別可形式地定義為:
type
celltype=record
element:telement;
next:^celltype;
end;
tlist=^celltype;
tposition=^celltype;
在單鏈表中,函式end(l)可實現如下
function end(l:tlist):tposition;
varq: tposition;
begin
q:=l;
while q^.next<>nil do q:=q^.next;
return(q)
end;
上述演算法中的指標q指向需要檢查的單元。由於檢查要從header開始,而l是指向表頭單元header的指標,所以q的初值為l。如果q所指的單元中的指標不是空指標,說明該單元不是表中的結束單元,因此要將q移向該單元的指標所指的下一單元,以便檢查下乙個單元。直到q所指的單元中的指標為nil時,那時的q值才是應返回的函式值。若表的長度為n,按這樣計算end(l)需要掃瞄整個鍊錶,因此需要o(n)時間,效率很低。如果需要頻繁地呼叫函式end,我們可以採用下面的兩種方法之一來提高效率。
在鍊錶結構中多設乙個指向鍊錶結束單元的表尾指標。對此,通過表尾指標就可以在o(1)時間內實現end(l)。
盡可能避免呼叫end(l)。例如purge程式的第(2)行中判斷條件p<>end(l)應該用p.next<>nil來代替。
下面討論單鏈表中insert,delete和locate三種運算的實現。
函式insert(x,p)功能
在表l的位置p處插入元素x,並將原來佔據位置p的元素及其後面的元素都向後推移乙個位置。例如,設l為a1,a2,…,an,那麼在執行insert(x,p)後,表l變為a1,a2,…ap-1,x,ap,…,an 。若p為end(l),那麼表l變為a1,a2,…,an,x 。若表l中沒有位置p,則該運算無定義。
實現
procedure insert(x:telement;p:tposition);
vartemp:tposition;
begin
temp:=p^.next;
new(p^.next);
p^.next^.element:=x;
p^.next^.next:=temp;
end;
說明
上述演算法中,鍊錶指標的修改情況見圖2。
(a)
(b)圖2 insert過程的指標修改示意圖
圖2(a)是執行insert運算之前的情況。我們要在指標p所指的單元之後插入乙個新元素x。圖2(b)是執行insert運算以後的結果,其中的虛線表示新的指標。
在上述insert演算法中,位置變數p指向單鏈表中乙個合法位置,要插入的新元素x應緊接在p所指單元的後面。指標p的合法性應在執行insert運算之前判定。往乙個單鏈表中插入新元素通常在表頭或表尾進行,因此p的合法性容易判定。
複雜性
insert運算所需的時間顯然為o(1)。
函式 delete(p)功能
從表l中刪除位置p處的元素。例如,當l為a1,a2,…,an時,執行delete(p)後,l變為a1,a2,…,ap-1,ap+1,…,an 。當l中沒有位置p或p=end(l)時,該運算無定義。
實現
procedure delete(p:tposition);
varq:tposition;
begin
q:=p^.next;
p^.next:=p^.next^.next;
dispose(p);
end;
說明
這個過程很簡單,其指標的修改如圖3所示。
圖3 delete過程的指標修改示意圖
若要從乙個表中刪除乙個元素x,但不知道它在表中的位置,則應先用locate(x,l)找出指示要刪除的元素的位置,然後再用delete刪除該位置指示的元素。
複雜性
delete過程所需的時間顯然也為o(1)。
函式locate(x,l)功能
這是乙個函式,函式值為元素x在l中的位置。若x在l中重複出現多次,則函式值為x第一次出現的位置。當x不在l中時,函式值為end(l)。
實現
function locate(x:telement;l:tlist):tposition;
varp:tposition;
begin
p:=l;
while p^.next<>nil do
if p^.next^.element = x then return(p)
else p:=p^.next;
return(p);
end;
說明
在單鏈表中實現locate的過程與用陣列實現表時的locate過程是類似的。
複雜性
容易看出,在最壞情況下和平均情況下,locate所需的時間均為o(n)。
對於其他基本的表運算實現起來都比較簡單,這裡從略。至於時間複雜性,在最壞情況下,printlist顯然需要o(n),而previous由於單鏈表沒有設定指向前驅的指標,得從頭到尾掃瞄,因此也需要o(n)。
用指標實現表
include stdafx.h include iostream.h templateclass list template class node template class list list bool empty const int size const bool retrieve int ...
使用指標實現的線性表 鍊錶
前一小節介紹使用陣列實現了線性表,這一小節使用指標來實現 先看那12個函式 include include typedef int elemtype typedef struct lnode lnode,linklist 初始化鍊錶 bool initlist linklist lst 刪除鍊錶 v...
使用指標實現的線性表 鍊錶
前一小節介紹使用陣列實現了線性表,這一小節使用指標來實現 先看那12個函式 include include typedef int elemtype typedef struct lnode lnode,linklist 初始化鍊錶 bool initlist linklist lst 刪除鍊錶 v...