將乙個表儲存到計算機中,可以採用許多不同的方法,其中既簡單又自然的是順序儲存方法,即將表中的元素逐個存放於陣列的一些連續的儲存單元中。在這種表示方式下,容易實現對錶的遍歷。要在表的尾部插入乙個新元素,也很容易。但是要在表的中間位置插入乙個新元素,就必須先將其後面的所有元素都後移乙個單元,才能騰出新元素所需的位置。執行刪除運算的情形類似。如果被刪除的元素不是表中最後乙個元素,則必須將它後面的所有元素前移乙個位置,以填補由於刪除所造成的空缺。
用陣列實現表時,我們將表型別tlist定義為乙個記錄。它有兩個域,第乙個域是乙個陣列,用於儲存表中的元素,陣列的大小根據表可能達到的最大長度而定;第二個域是乙個整型變數last,用於指出表中結束元素在陣列中的位置。表中第i個元素(1≤i≤last)儲存在陣列的第i個單元中。
在這種情況下,位置變數的型別tposition是整型,位置變數p表示陣列的第p個單元即表中第p個元素的位置。end(l)的函式值為last+1。這些型別可用pascal語言說明如下:
const
maxlength=100;
type
tposition = integer;
telement= ...
tlist = record
elements: array [1..maxlength] of telement;
last: tposition;
end;
定義了實現表的陣列結構後,我們就可以討論在這種結構上如何具體地實現表的基本運算了。
函式 insert(x,p,l) 功能
在表l的位置p處插入元素x,並將原來佔據位置p的元素及其後面的元素都向後推移乙個位置。例如,設l為a1,a2,…,an,那麼在執行insert(x,p,l)後,表l變為a1,a2,…ap-1,x,ap,…,an 。若p為end(l),那麼表l變為a1,a2,…,an,x 。若表l中沒有位置p,則該運算無定義。
實現
procedure insert(x:telement;p:tposition;l:tlist);
var q: tposition;
begin
if length(l)>=maxlength
then
error('list is full')
else if (p>end(l))or(pthen
error('position was not defined.')
else
begin
for q←l.last downto p do
l[q]←l[q+1];
l.last←l.last+1;
l.elements[p]← x;
end;
end;
說明:
演算法insert將位於p,p+1,…,last 中的元素分別移到位置p+1,p+2,…,last+1 ,然後將新元素插入位置p。注意演算法中元素後移的順序,必須從表中最後乙個位置開始後移,直至將位置p處的元素後移為止。如果新元素的插入位置不合法,則呼叫子程式error輸出錯誤資訊,然後終止程式。
複雜性:
現在我們來分析演算法的時間複雜性。這裡問題的規模是表的長度length(l)=l.last,設它的值為n。顯然該演算法的主要時間花費在for迴圈的元素後移上,該語句的執行次數為n-p+1。由此可看出,所需移動元素位置的次數不僅依賴於表的長度,而且還與插人的位置p有關。當p=n+1時由於迴圈變數的終值大於初值,元素後移語句將不執行,無須移動元素;若p=1,則元素後移語句將迴圈執行n次,需移動表中所有元素。也就是說該演算法在最好情況下需要ο(1)時間,在最壞情況下需要ο(n)時間。由於插入可能在表中任何位置上進行,因此,有必要分析演算法的平均效能。
設在長度為n的表中進行插入運算所需的元素移動次數的平均值為e
in(n)。由於在表中第i個位置上插入元素需要的移動次數為n-i+1,故
其中,pi
表示在表中第i個位置上插入元素的概率。考慮最簡單的情形即假設在表中任何合法位置i (1≤i≤n+l)上插入元素的機會是均等的,則:
從而,在等概率插入的情況下,
也就是說,用陣列實現表時,在表中做插入運算,平均要移動表中一半的元素,因而演算法所需的平均時間仍為ο(n)。
函式 delete(p,l) 功能
從表l中刪除位置p處的元素。例如,當l為a1,a2,…,an時,執行delete(p,l)後,l變為a1,a2,…,ap-1,ap+1,…,an 。當l中沒有位置p或p=end(l)時,該運算無定義。
實現
procedure delete(p:tposition;var l:tlist);
begin
if (p>end(l)) or (pthen error('position does not exist.')
else
begin
l.last ← l.last-1;
for q ← p to l.last do
l.elements[q] ← l.elements[q+1];
end;
說明
演算法delete通過將位於p+1,p+2,…,last中的元素分別移到位置p,p+1,…, last-1來刪除原來位置p處的元素。
複雜性
該演算法的時間分析與插入演算法類似,元素的移動次數也是由表長n和位置p決定的。若p=n,則由於迴圈變數的初值大於終值,前移語句將不執行,無須移動元素;若p=1,則前移語句將迴圈執行n-1次,需要移動表中除刪除元素外的所有元素。因此,演算法在最好情況下需要o(1)時間,而在最壞情況下需要o(n)時間。
刪除運算的平均效能分析與插入運算類似。設在長度為n的表中刪除乙個元素所需的平均移動次數為ede(n)。由於刪除表中第i個位置上的元素需要的移動次數為n-i,故
其中,pi表示刪除表中第i個位置上元素的概率。在等概率的假設下,
這時
也就是說用陣列實現表時,在表中做刪除運算,平均要移動表中約一半的元素,因而刪除運算所需的平均時間為o(n)。
函式 locate(x,l)
功能這是乙個函式,函式值為元素x在l中的位置。若x在l中重複出現多次,則函式值為x第一次出現的位置。當x不在l中時,函式值為end(l)。
實現function locate(x:telement;var l:tlist):tposition;
varq:tposition;
begin
for q:=1 to l.last do
if l.elements[q]=x then return (q);
return(l.last+1);
end;
說明
演算法locate在陣列中從前向後通過比較來查詢給定元素的位置。如果在表中沒有找到這樣的元素,則返回last+1。
複雜性
該演算法中的基本運算是兩個元素的比較。若在表中位置i找到給定元素,則需要進行i次比較,否則需要進行n次比較,n為表的長度。與演算法insert和delete的時間分析類似,演算法locate在最好情況下需要o(l)時間,在最壞情況和平均情況下都需要o(n)時間。
用陣列來實現表時,我們利用了陣列單元在物理位置上的鄰接關係來表示表中元素之間的邏輯關係。由於這個原因,用陣列來實現表有如下的優缺點。
優點是:
無須為表示表中元素之間的邏輯關係增加額外的儲存空間;
可以方便地隨機訪問表中任一位置的元素。
缺點是:
插入和刪除運算不方便,除表尾的位置外,在表的其他位置上進行插入或刪除操作都必須移動大量元素,其效率較低;
由於陣列要求占用連續的儲存空間,儲存分配只能預先進行靜態分配。因此,當表長變化較大時,難以確定陣列的合適的大小。確定大了將造成浪費。
鍊錶的陣列實現
實現乙個單鏈表,鍊錶初始為空,支援三種操作 1 向煉表頭插入乙個數 2 刪除第k個插入的數後面的數 3 在第k個插入的數後插入乙個數 現在要對該鍊錶進行m次操作,進行完所有操作後,從頭到尾輸出整個鍊錶。注意 題目中第k個插入的數並不是指當前鍊錶的第k個數。例如操作過程中一共插入了n個數,則按照插入的...
鄰接表的陣列實現
之前我們介紹過圖的鄰接矩陣儲存法,它的空間和時間複雜度都是n2,現在我來介紹另外一種儲存圖的方法 鄰接表,這樣空間和時間複雜度就都是m。對於稀疏圖來說,m要遠遠小於n2。先上資料,如下。4 51 4 9 4 3 8 1 2 5 2 4 6 1 3 7 複製 第一行兩個整數n m。n表示頂點個數 頂點...
鍊錶的陣列實現
include using namespace std define maxsize 100 鍊錶的陣列實現 typedef struct list,plist plist createemptylist int findlistelement int x,plist pl if i pl last...