數據結構與算法核心知識(精簡版)

一、數據結構的理解

  簡單地說,數據結構是計算機組織數據和存儲數據的方式;即數據結構是指一組相互之間存在一種或多種特定關係的數據的組織方式和它們在計算機內的存儲方式,以及定義在該組數據上的一組操作。 

引申:

1、計算機解決一個具體問題時,一般需要經過以下幾個步驟:

  

    在每個步驟中,數據的表現形式都不相同,實際問題中的數據稱爲原始數據。在數學模型中,需要把原始數據按照某種方式組織起來,以便很好地體現數據之間的關係,數據及數據的組織方式稱爲數據的邏輯結構。爲了能用計算機加工處理,邏輯結構還必須轉換爲能被計算機存儲的存儲結構。

2、1976 年瑞士計算機科學家Niklaus Wirth曾提出一個著名公式: 算法+數據結構=程序,簡潔地描述了算法、數據結構和程序之間關係。 數據結構是相互之間存在一種或多種特定關係的數據元素的集合。它包括數據的邏輯結構、數據的存儲結構和數據的基本運算。

二、數據的邏輯結構  

1、數據的邏輯結構是指數據元素之間的邏輯關係。所謂邏輯關係是指數據元素之間的關聯方式或“鄰接關係”。

2、數據元素之間的關係,有四類基本的邏輯結構:

(1)集合中任意兩個結點之間都沒有鄰接關係,組織形式鬆散。

(2)線性結構中結點按邏輯關係依次排列形成一條“鏈”,結點之間一個一個依次相鄰接。

(3)樹形結構具有分支、層次特性,其形態像自然界中的樹,上層的結點可以和下層多個結點相鄰接,但下層結點只能和上層的一個結點相鄰接。

(4)圖結構最複雜,其中任何兩個結點都可以相鄰接。

三、數據的存儲結構

1、數據的邏輯結構在計算機中的實現稱爲數據的存儲結構(或物理結構)。一般情況下,一個存儲結構包括以下兩個部分:

 (1)存儲數據元素;(2)數據元素之間的關聯方式。

2、表示數據元素之間的關聯方式主要有順序存儲方式和鏈式存儲方式

   (1)順序存儲方式是指所有存儲結點存放在一個連續的存儲區裏。利用結點在存儲器中的相對位置來表示數據元素之間的邏輯關係。

   (2)鏈式存儲方式是指每個存儲結點除了含有一個數據元素外,還包含指針,每個指針指向一個與本結點有邏輯關係的結點,用指針表示數據元素之間的邏輯關係。除了上述兩種存儲方式之外,還有索引存儲方式和散列存儲方式。

3、一種邏輯結構可以採用一種或幾種存儲方式來表達數據元素之間的邏輯關係,相應的存儲結構稱爲給定邏輯結構的存儲實現或存儲映像。

四、運算

  運算是指在某種邏輯結構上施加的操作,即對邏輯結構的加工。一般來說,在每個邏輯結構上,都定義了一組基本運算,這些運算包括:建立、查找、讀取、插入和刪除等。

  注:線性表、棧和隊列中的元素具有相同的邏輯結構(即線性結構),但有不同的運算集,它們是不同的數據結構。

五、線性表

   線性表是一種線性結構,它是由 n(n≥0)個數據元素組成的有窮序列,數據元素又稱結點。線性表中結點具有一對一的關係,如果結點數不爲零,則除起始結點沒有直接前驅外,其他每個結點有且僅有一個直接前驅;除終端結點沒有直接後繼外,其他每個結點有且僅有一個直接後繼。

1、線性表的順序存儲

  將表中的結點依次存放在計算機內存中一組連續的存儲單元中,數據元素在線性表中的鄰接關係決定它們在存儲空間中的存儲位置,即邏輯結構中相鄰的結點其存儲位置也相鄰。用順序存儲實現的線性表稱爲順序表,一般使用數組來表示順序表

2、線性表的基本運算

1)、插入

  插入算法的基本步驟是:首先將結點 ai~an依次向後移動一個元素的位置,這樣空出第 i 個數據元素的位置;然後將 x 置入該空位,最後表長加 1。

  算法描述如下:

void InsertSeqlist(SeqList L,DataType x, int i)
{ //將元素 x 插入到順序表 L 的第 i 個數據元素之前
if (L. length==Maxsize) exit(“表已滿”);
if (i<1 || i>L. length+1) exit(“位置錯”);//檢查插入位置是否合法
for(j=L.length;j>=i;j--) //初始 i=L.length
L.data[j]=L.data[j-1]; //依次後移
L.data[i-1]=x; //元素 x 置入到下標爲 i-1 的位置
L.length++; //表長度加 1
}

2)、刪除

算法描述如下:

void DeleteSeqList(SeqList L,int i)
{ //刪除線性表 L 中的第 i 個數據結點
if(i<1 || i>L.length) //檢查位置是否合法
exit(“非法位置”);
for(j=i;j<L.length;j ++) //第 i 個元素的下標爲 i-1
L.data[j-1]=L.data[j]; //依次左移
L.length--; //表長度減 1
}

3)、定位

  定位運算 LocateSeqlist(SeqList L,DataType x)的功能是查找出線性表 L 中值等於 x 的結點序號的最小值,當找不到值爲 x 的結點時,返回 0。

  描述算法如下:

int LocateSeqlist(SeqList L, DataType x)
{
int i=0;
while ((i<L. length) && (L.data[i]!=x) ) //在順序表中查找值爲 x 的結點
i++;
if(i<L.length) return i+1; //若找到值爲 x 的兀素,返回兀素的序號
else return 0; //未查找到值爲 x 的兀素,返回 0
}

順序表的求表長操作,直接輸出 L.length 即可。

3、順序表實現算法的分析

在分析線性表的順序表實現算法時,一個重要指標就是數據元素的比較和移動的次數。

(1)設表的長度 length=n,在插入算法中,元素的移動次數不僅與順序表的長度 n 有關,還與插入的位置 i 有關。插入算法在最壞情況下,其時間複雜度爲 O(n)。一般情況下元素比較和移動的次數爲 n-i+1 次,插入算法的平均移動次數約爲 n/2,其時間複雜度是 O(n)。

(2)刪除算法 DeleteSeqlist,可得其在最壞情況下元素移動次數爲 n-1,時間複雜度爲O(n),元素平均移動次數約爲(n-1)/2,時間複雜度爲 O(n)。

(3)對於定位算法,需要掃描順序表中的元素。以參數 x 與表中結點值的比較爲標準操作,平均時間複雜度爲 O(n)。求表長和讀表元素算法的時間複雜度爲 O(1),就階數而言,己達到最低。

4、線性表的鏈接存儲

   線性表的鏈接存儲是指它的存儲結構是鏈式的。線性表常見的鏈式存儲結構有單鏈表、循環鏈表和雙向循環鏈表,其中最簡單的是單鏈表。

1)、單鏈表的類型定義

1.1)、結點結構

(1)data 部分稱爲數據域,用於存儲線性表的一個數據元素。

(2)next 部分稱爲指針域或鏈域,用於存放一個指針,該指針指向本結點所含數據元素的直接後繼結點。

2)、單鏈表

  所有結點通過指針鏈接形成鏈表。

(1)head 稱爲頭指針變量,該變量的值是指向單鏈表的第一個結點的指針。可以用頭指針變量來命名單鏈表。

(2)鏈表中第一個數據元素結點稱爲鏈表的首結點。

(3)鏈表中最後一個數據元素結點稱爲尾結點或終端結點。尾結點指針域的值 NULL 稱爲空指針,它不指向任何結點,表示鏈表結束。如果 head 等於 NULL,則表示該鏈表無任何結點,是空單鏈表。

3)、單鏈表的類型定義如下:

typedef struct node
{ DataType data; //數據域
struct node * next; //指針域
}Node, *LinkList;

4)、帶頭結點的單鏈表

  在單鏈表的第一個結點之前增設一個類型相同的結點,稱之爲頭結點,其他結點稱爲表結點。表結點的第一個和最後一個結點分別就是首結點和尾結點。

七、線性表的基本運算在單鏈表上的實現

1、初始化

  空表由一個頭指針和一個頭結點組成。

  算法描述如下:

LinkList InitiateLinkList()
//建立一個空的單鏈表
{
LinkList head; //頭指針
head=malloc (sizeof (Node) ) ; //動態構建一結點,它是頭結點
head->next=NULL;
return head;
}

在算法中,變量 head 是鏈表的頭指針,它指向新創建的結點,即頭結點。一個空單鏈表僅有一個頭結點,它的指針域爲 NULL。

2、求表長

  在單鏈表存儲結構中,線性表的表長等於單鏈表中數據元素的結點個數,即除了頭結點以外的結點的個數。設置一個工作指針 p,初始時,p 指向頭結點,並設置一個計數器 cnt,初值設置爲 0。然後,讓工作指針 p 通過指針域逐個結點向尾結點移動,工作指針每向尾部移動一個結點,讓計數器加 1。直到工作指針 p->next 爲 NULL 時,說明已經走到了表的尾部,這時已完成對所有結點的訪問,計數器 cut 的值即是表的長度。

3、讀表元素

   通常給定一個序號 i,查找線性表的第 i 個元素。在鏈表中,任何相鄰的兩個結點通過一個指針相連,一個結點的位置包含在直接前驅結點的 next 域中。所以,必須從頭指針出發,一直往後移動,直到第 i 個結點。

4、定位

  線性表的定位運算,就是對給定表元素的值,找出這個元素的位置。在單鏈表的實現中,則是給定一個結點的值,找出這個結點是單鏈表的第幾個結點。定位運算又稱作按值查找。在定位運算中,也需要從頭至尾訪問鏈表,直至找到需要的結點,返回其序號。若未找到,返回 0。

定位運算算法描述如下:

int LocateLinklist(LinkList head, DataType x)
//求表 head 中第一個值等於 x 的結點的序號,若不存在這種結點,返回結果爲 0
{
Node *p=head; //p 是工作指針
p=p->next; //初始時 p 指向首結點
int i=0; //i 代表結點的序號,這裏置初值爲◦ while (p != NULL && p->data != x) //訪問鏈表
{
i++;
p=p->next;
}
if (p!=NULL) return i+1;
else return 0;
}

5、插入

   單鏈表的插入運算是將給定值爲 x 的元素插入到鏈表 head 的第 i 個結點之前。

  插入運算描述如下:

void InsertLinklist (LinkList head, DataType x, int i)
//在表 head 的第 i 個數據元素結點之前插入一個以 x 爲值的新結點
{
Node *p,*q;
if (i==1) q=head;
else q=GetLinklist (head, i-1); //找第 i-1 個數據元素結點
if (q==NULL) //第 i-1 個結點不存在
  exit(“找不到插入的位置”);
else{
  p=malloc(sizeof (Node) );p->data=x; //生成新結點
  p->next=q->next; //新結點鏈域指向*q 的後繼結點
  q->next=p; //修改*q 的鏈域
 }
}

注意:鏈接操作 p->next=q->next 和 q->next=p 兩條語句的執行順序不能顛倒,否則結點*q 的鏈域值(即指向原表第 i 個結點的指針)將丟失。

6、刪除

  刪除運算是給定一個值 i,將鏈表中第 i 個結點從鏈表中移出,並修改相關結點的指針域,以維持剩餘結點的鏈接關係。

       單鏈表的刪除運算算法描述如下:

void DeleteLinklist(LinkList head, int i)
//刪除表 head 的第 i 個結點
{
Node *q;
if(i==1) q=head;
else q=GetLinklist(head, i-1); //先找待刪結點的直接前驅
if(q !== NULL && q->next != NULL) //若直接前驅存在且待刪結點存在
{
  p=q->next; //p 指向待刪結點
  q->next=p->next; //移出待刪結點
  free(p); //釋放已移出結點p的空間
}
else exit (“找不到要刪除的結點”); //結點不存在
}

八、 其他鏈表

1、循環鏈表:在單鏈表中,如果讓最後一個結點的指針域指向第一個結點可以構成循環鏈表。在循環鏈表中,從任一結點出發能夠掃描整個鏈表。

2、雙向循環鏈表

   在單鏈表的每個結點中再設置一個指向其直接前驅結點的指針域 prior,這樣每個結點有兩個指針,其結點結構如圖:

     

  prior 與 next 類型相同,它指向直接前驅結點。頭結點的 prior 指向最後一個結點,最後一個結點的 next 指向頭結點,由這種結點構成的鏈表稱爲雙向循環鏈表,雙向循環鏈表適合應用在需要經常查找結點的前驅和後繼的場合。雙向循環鏈表的對稱性可以用下列等式表示:p=p->prior->next=p->next->prior。即結點 p 的前驅結點的 next 和後繼結點的 prior 都指向結點 p。

九、順序實現與鏈接實現的比較

1、線性表和鏈表的時間性能和空間性能進行比較

(1) 對於按位置查找運算,順序表是隨機存取,時間複雜度爲 O(1)。單鏈表需要對錶元素進行掃描,它時間爲複雜度爲 O(n)。

(2) 對於定位運算,基本操作是比較,順序表和單鏈表上的實現算法的時間複雜度都是相同的,均爲 O(n)。

(3) 對於插入、刪除運算。在順序表中,平均時間複雜度爲 O(n)。在單鏈表中,其平均時間複雜度仍然爲 O(n)。

2、線性表與鏈表的優缺點

(1) 單鏈表的每個結點包括數據域與指針域,指針域需要佔用額外空間

(2) 從整體考慮,順序表要預分配存儲空間,如果預先分配得過大,將造成浪費,若分配得過小,又將發生上溢;單鏈表不需要預先分配空間,只要內存空間沒有耗盡,單鏈表中的結點個數就沒有限制。

十、棧、 隊列和數組

  棧和隊列可看作是特殊的線性表。它們的特殊性表現在它們的基本運算是線性表運算的子集,它們是運算受限的線性表。棧和隊列是計算機科學中使用得較爲廣泛的兩種結構。

1、棧

  棧是運算受限的線性表,這種線性表上的插入和刪除運算限定在表的某一端進行。允許進行插入和刪除的一端稱爲棧頂,另一端稱爲棧底。棧的修改原則是後進先出,因此,棧又稱爲後進先出線性表,簡稱後進先出表。

2、棧的順序實現

  桟的順序存儲結構是用一組連續的存儲單元依次存放桟中的每個元素,並用始端作爲棧底。棧的順序實現稱爲順序棧。通常用一個一維數組和一個記錄棧頂位置的變量來實現棧的順序存儲。

3、雙棧

  在某些應用中,爲了節省空間,讓兩個數據元素類型一致的棧共享一維數組空間 data[max],成爲雙棧,兩個棧的棧底分別設在數組兩端,讓兩個棧彼此迎面“增長”,兩個棧的棧頂變量分別爲 top1、top2,僅當兩個棧的棧頂位置在中間相遇時(top1 + 1 =top2)才發生“上溢”,雙棧如圖:

注意:判斷上溢爲 top1 + 1=top2。判棧空時,兩個棧不同,當 top1=0 時棧 1 爲空棧,top2=max-1 時棧 2 爲空桟。

4、棧的鏈接實現

  棧的鏈接實現稱爲鏈棧,鏈棧可以用帶頭結點的單鏈表來實現,LS 指向鏈表的頭結點,首結點是棧頂結點,LS->next 指向棧頂結點,尾結點爲棧底結點。各結點通過鏈域的連接組成棧,由於每個結點空間都是動態分配產生,鏈棧不用預先考慮容量的大小。

5、隊列

  隊列是有限個同類型數據元素的線性序列,是一種先進先出的線性表,出隊列的數據元素在隊列首部被刪除。排隊的規則是不允許插隊。

6、隊列的順序實現

  順序存儲實現的隊列稱爲順序隊列,它由一個一維數組(用於存儲隊列中元素)及兩個分別指示隊列首和隊列尾元素的變量組成,這兩個變量分別稱爲“隊列首指針”和“隊列尾指針。

7、循環隊列

  爲了避免元素的移動,可以將存儲隊列元素的一維數組首尾相接,形成一個環狀;如圖所示:

  

8、隊列的鏈接實現

  隊列的鏈接實現實際上是使用一個帶有頭結點的單鏈表來表示隊列,稱爲鏈隊列。頭指針指向鏈表的頭結點,單鏈表的頭結點的 next 域指向隊列首結點,尾指針指向隊列尾結點,即單鏈表的最後一個結點,如圖所示:

  

9、數組

   數組可以看成線性表的一種推廣。數組類型是許多程序設計語言的基本類型。常用的有一維數組和二維數組。一維數組又稱向量,它由一組具有相同類型的數據元素組成,並存儲在一組連續的存儲單元中。若一維數組中的數據元素又是一維數組結構,則稱爲二維數組;依此類推,若一維數組中的元素又是一個二維數組結構,則稱作三維數組。一般地,一個 n 維數組可以看成元素爲 n-1 維數組的線性表。

10、數組通常只有兩種基本運算:

(1)讀:給定一組下標,返回該位置的元素內容;(2)寫:給定一組下標,修改該位置的元素內容。

11、數組的存儲結構

  一維數組元素的內存單元地址是連續的,二維數組可有兩種存儲方法:一種是以列序爲主序的存儲;另一種是以行序爲主序的存儲。數組元素的存儲位置是下標的線性函數。

十一、矩陣的壓縮存儲

  矩陣可以用二維數組來表示。在數值分析中經常出現一些高階矩陣,這些高階矩陣中有許多值相同的元素或零元素,爲了節省存儲空間,對這類矩陣採用多個值相同的元素只分配一個存儲空間,零元素不存儲的策略,這一方法稱爲矩陣的壓縮存儲。如果值相同的元素或者零元素在矩陣中的分佈有一定規律,稱此類矩陣爲特殊矩陣。矩陣的非零元素個數很少的矩陣稱爲稀疏矩陣。

1、特殊矩陣

(1)對稱矩陣:若一個 n 階方陣 A 中的元素滿足下述條件:aij=aji 0≤i,j≤n-1,則 A稱爲對稱矩陣。

(2)三角矩陣:以主對角線爲界的上(下)半部分是一個固定的值 c 或零,這樣的矩陣叫做下(上)三角矩陣。

2、稀疏矩陣:假設 m 行 n 列的矩陣有 t 個非零元素,當 t<<m*n 時,則稱矩陣爲稀疏矩陣。

十二、樹和二叉樹

1、樹(Tree)是一類重要的數據結構,其定義如下:樹是 n(n 多 0)個結點的有限集合,一棵樹滿足以下兩個條件:

(1)當 n=0 時,稱爲空樹;

(2)當 n>0 時,有且僅有一個稱爲根的結點,除根結點外,真餘結點分 m(m≥0)個互不相交的非空集合 T1,T2,…,Tm,這些集合中的每一個都是一棵樹,稱爲根的子樹。

2、森林(Forest)是 m(m>0)棵互不相交的樹的集合。樹的每個結點的子樹是森林。刪除一個非空樹的根結點,它的子樹便構成森林。

3、基本術語:

(1) 結點的度:樹上任一結點所擁有的子樹的數目稱爲該結點的度。

(2) 葉子:度爲 0 的結點稱爲葉子或終端結點。

(3) 樹的度:一棵樹中所有結點的度的最大值稱爲該樹的度。

(4) 一個結點的子樹的根稱爲該結點的孩子(或稱子結點)。相應地該結點稱爲孩子的雙親(也稱父結點)。

(5) 結點的層次:從根開始算起,根的層次爲 1,其餘結點的層次爲其雙親的層次加 1。

(6) 樹的高度:一棵樹中所有結點層次數的最大值稱爲該樹的高度或深度。

(7) 有序樹:若樹中各結點的子樹從左到右是有次序的,不能互換,稱爲有序樹。有序樹中最左邊子樹的根稱爲第 1 個孩子,左邊第 i 個子樹的根稱爲第 i 個孩子。

(8) 無序樹:若樹中各結點的子樹是無次序的,可以互換,則稱爲無序樹。

十三、二叉樹

1、二叉樹(Binary Tree)是 n(n≥0)個元素的有限集合,該集合或者爲空,或者由一個根及兩棵互不相交的左子樹和右子樹組成,其中左子樹和右子樹也均爲二叉樹。

 注意:二叉樹的任一結點都有兩棵子樹(它們中的任何一個都可以是空子樹),並且這兩棵子樹之間有次序關係,即如果互換了位置就成爲一棵不同的二叉樹。二叉樹上任一結點左、右子樹的根分別稱爲該結點的左孩子和右孩子。

2、二叉樹有五種基本形態。如圖所示,其中方塊表示子樹。

 

3、二叉樹的基本運算包括:

(1) 初始化 Initiate(BT):建立一棵空二叉樹,BT=0。

(2) 求雙親 Parent(BT, X):求出二叉樹 BT 上結點 X 的雙親結點,若 X 是 BT 的根或 X 根本不是 BT 上的結點,運算結果爲 NULL。

(3) 求左孩子 Lchild(BT,X)和求右孩子 Rchild(BT,X):分別求出二叉樹 BT 上結點 X 的左、右孩子;若 X 爲 BT 的葉子或 X 不在 BT 上,運算結果爲 NULL。

(4) 建二叉樹 Create (BT):建立一棵二叉樹 BT。

(5) 先序遍歷 PreOrder(BT):按先序對二叉樹 BT 進行遍歷,每個結點被訪問一次且僅被訪問一次,若 BT 爲空,則運算爲空操作。

(6) 中序遍歷 InOrder (BT):按中序對二叉樹 BT 進行遍歷,每個結點被訪問一次且僅被訪問一次,若 BT 爲空,則運算爲空操作。

(7) 後序遍歷 PostOrder(BT):按後序對二叉樹 BT 進行遍歷,每個結點被訪問一次且僅被訪問一次,若 BT 爲空,則運算爲空操作。

(8) 層次遍歷 LevelOrder(BT):按層從上往下,同一層中結點按從左往右的順序,對二叉樹進行遍歷,每個結點被訪問一次且僅被訪問一次,若 BT 爲空,則運算爲空操作。

4、二叉樹的性質

性質 1:二叉樹第 i(i≥1)層上至多有個結點。

性質 2:深度爲 k(k≥1)的二叉樹至多有 2k-1 個結點。

性質 3:對任何一棵二叉樹,若度數爲 0 的結點(葉結點)個數爲 n0,度數爲 2 的結點個數爲 n2,則 n0=n2+1。

滿二叉樹:深度爲 k(k≥1)且有 2k-1 個結點的二叉樹稱爲滿二叉樹。由性質 2 知,滿二叉樹上的結點數已達到了二叉樹可以容納的最大值。

完全二叉樹:如果對滿二叉樹按從上到下,從左到右的順序編號,並在最下一層刪去部分結點(刪後最後一層仍有結點),如果刪除的這些結點的編號是連續的且刪除的結點中含有最大編號的結點,那麼這棵二叉樹就是完全二叉樹。滿二叉樹一定是完全二叉樹, 完全二叉樹不一定是滿二叉樹。

性質 4:含有 n 個結點的完全二叉樹的深度爲⌊ log2n⌋ +1。

性質 5:如果將一棵有 n 個結點的完全二叉樹按層編號,按層編號是指:將一棵二叉樹中的所有 n 個結點按從第一層到最大層,每層從左到右的順序依次標記爲 1, 2,…, n。則對任一編號爲 i(1≤i≤n)的結點 A 有:

  (1) 若 i=1,則結點 A 是根;若 i>1,則 A 的雙親 Parent(A)的編號爲⌊ i/2⌋ ;

  (2) 若 2*i>n,則結點 A 既無左孩子,也無右孩子;否則 A 的左孩子 Lchild(X)的編號爲 2*i;

  (3) 若 2*i+l>n,則結點 A 無右孩子;否則,A 的右孩子 Rchild(X)的編號爲 2*i + 1。

5、二叉樹的存儲結構

   二叉樹通常有兩類存儲結構:順序存儲結構和鏈式存儲結構。鏈式存儲結構在插入刪除結點時較方便,在某些情況下,二叉樹的順序存儲結構也很有用。 二叉樹的順序存儲結構二叉樹的順序存儲結構可以用一維數組來實現,二叉樹上的結點按某種次序分別存入該數組的各個單元中。由二叉樹的性質 可知,如果對任一完全二叉樹上的所有結點按層編號,則結點編號之間的關係可以準確地反映結點之間的邏輯關係。對於任何完全二叉樹來說,可以採用以編號作爲數組的下標的方法將結點存入一維數組中,也就是將編號爲 i 的結點存入一維數組的以 i爲下標的數組元素中。

  如果需要順序存儲的非完全二叉樹,首先必須用某種方法將其轉化爲完全二叉樹,爲此可增設若干個虛擬結點。這樣可以用與處理完全二叉樹相同的方法實現二叉樹的基本運算。但這種方法的缺點是造成了空間的浪費。如圖所示:

6、二叉樹的鏈式存儲結構

1)、二叉樹有不同的鏈式存儲結構,其中最常用的是二叉鏈表與三叉鏈表。

2)、每個二叉鏈表還必須有一個指向根結點的指針,該指針稱爲根指針。與鏈表頭指針類似,根指針具有標識二叉鏈表的作用,對二叉鏈表的訪問只能從根指針開始。如果某個結點的左孩子或右孩子不存在時,則相應指針域值爲空,可知葉結點的左右指針必爲空(NULL)。在三叉鏈表中每個結點增加一個指針域 parent,用於指向該結點的雙親。

3)、類型定義

(1)二叉鏈表

typedef struct btnode {
DataType data;
struct btnode *lchild, *rchild; //指向左右孩子的指針
}*BinTree;

BinTree root;//root 爲指向根結點的指針,BinTree 爲指向二叉鏈表結點的指針類型 若二叉樹爲空,則 root=NULL。若某結點的某個孩子不存在,則相應的指針爲空。具有 n 個結點的二叉樹中,有 2n 個指針域,其中只有 n-1 個用來指向結點的左、右孩子,其餘的 n+1 個指針域爲 NULL。

(2)三叉鏈表

typedef struct ttnode {
datatype data;
struct ttnode *lchild,*parent,*rchild;
}*TBinTree;
TBinTree root;

二叉樹的鏈式存儲結構操作方便,結點間的父子關係在二叉鏈表和三叉鏈表中被直接表達成對應存儲結點之間的指針,因而成爲二叉樹最常用的存儲結構。

7、二叉樹的遍歷

 二叉樹遍歷的遞歸實現

1)、先序遍歷

  若被遍歷的二叉樹爲空,執行空操作;否則,依次執行:(1) 訪問根結點;(2) 先序遍歷左子樹;(3) 先序遍歷右子樹。

2)、中序遍歷

  若被遍歷的二叉樹爲空,執行空操作;否則,依次執行:(1) 中序遍歷左子樹;(2) 訪問根結點;(3) 中序遍歷右子樹。

4)、後序遍歷

  若被遍歷的二叉樹爲空,執行空操作;否則,依次執行:(1) 後序遍歷左子樹;(2) 後序遍歷右子樹;(3) 訪問根結點。

5)、二叉樹的層次遍歷

  二叉樹的層次遍歷,是指從二叉樹的根結點的這一層開始,逐層向下遍歷,在每一層上按從左到右的順序對結點逐個訪問。層次遍歷可以用一個隊列來實現。

6)、二叉樹遍歷的非遞歸實現

  藉助棧實現二叉樹遍歷的非遞歸過程。實現中序遍歷的非遞歸算法,只需將先序遍歷的非遞歸算法中的 Visit(p->data)移動到Pop (LS,p)和 p=p->rchild 之間即可。後序遍歷與先序遍歷和中序遍歷不同,在後序遍歷過程中,結點在第一次出棧後,還需再次入棧,也就是說,結點要兩次入棧,兩次出棧,遍歷結點是在結點的第二次出棧時進行。

十四、樹和森林

1、樹的存儲結構

   樹是一種常用的數據結構。它有如下三種常用的存儲結構:

 1)、孩子鏈表表示法

  孩子鏈表表示法是樹的一種鏈式存儲結構。它的主體是一個數組元素個數和樹中結 點個數相同的一維數組。樹上的一個結點 X 以及該結點的所有孩子結點組成一個帶頭結點的單鏈表,單鏈表的頭結點含有兩個域:數據域和指針域。其中,數據域用於存儲結點 X 中的數據元素,指針域用於存儲指向 X 第一個孩子結點的指針。除頭結點外,其餘所有表結點也含兩個域:孩子域(child)和指針域(next),孩子域存儲其中一個孩子的序號,指針域指向其下一個孩子的結點。爲了便於找到雙親,可在各個頭結點中增加一個雙親域以存儲雙親在頭結點數組中 的下標值。這種存儲結構稱爲帶雙親的孩子鏈表表示法。

 2)、孩子兄弟鏈表表示法

  存儲時每個結點除了數據域外,還有指向該結點的第一個孩子和下一個兄弟結點的指針。

  注意:孩子兄弟鏈表的結構形式與二叉鏈表完全相同,但結點中指針的含義不同。二叉鏈表中結點的左、右指針分別指向左、右孩子;而孩子兄弟鏈表中結點的兩個指針分別指向孩子和兄弟。

 3)、雙親表示法

  雙親表示法由一個一維數組構成。數組的每個分量包含兩個域:數據域和雙親域。數據域用於存儲樹上一個結點中數據元素,雙親域用於存儲本結點的雙親結點在數組中的序號(下標值)。

十五、樹、森林與二叉樹的關係

1、樹轉換成二叉樹:任何一棵樹可唯一地與一棵二叉樹對應。相應地,一棵二叉樹也唯一地對應一棵樹,即樹與二叉樹可以互相轉換。

2、森林轉換成二叉樹

3、二叉樹轉換成森林

4、樹和森林的遍歷

5、樹的遍歷

(1)先序遍歷。若樹非空,則①訪問根結點;②依次先序遍歷根的各棵子樹 T1,…,Tm。

(2)後序遍歷。若樹非空,則①依次後序遍歷根的各棵子樹 T1,…,Tm。②訪問根結點。

(3)層次遍歷。①若樹非空,訪問根結點;②若第 i(i≥1)層結點已被訪問,第 i+1 層結點尚未訪問,則從左到右依次訪問第 i+1 層結點。

6、森林的遍歷

(1)先序遍歷森林。若森林非空,則 ①訪問森林中第一棵樹的根結點;②先序遍歷森林第一棵樹的根結點的子樹組成的森林;③先序遍歷除去第一棵樹之外其餘的樹組成的森林。

(2)中序遍歷森林。若森林非空,則 ①中序遍歷森林中第一棵樹的根結點的子樹組成的森林;②訪問第一棵樹的根結點;③中序遍歷除去第一棵樹之外其餘的樹組成的森林。

十六、判定樹和哈夫曼樹

1、分類與判定樹

   分類是一種常用運算,其作用是將輸入數據按預定的標準劃分成不同的種類。用於描述分類過程的二叉樹稱爲判定樹。

2、哈夫曼樹與哈夫曼算法給定一組值 p1…,pk,如何構造一棵有 k 個葉子且分別以這些值爲權的判定樹,使得其平均比較次數最小。滿足上述條件的判定樹稱爲哈夫曼樹。哈夫曼率先給出了一個求哈夫曼樹的簡單而有效的方法,稱爲哈夫曼算法。應用於哈夫曼編碼。

十七、圖

  在樹形結構中,結點間具有層次關係,每一層結點只能和上一層中的至多一個結點相關,但可能和下一層的多個結點相關。而在圖結構中,任意兩個結點之間都可能相關,即結點之間的鄰接關係可以是任意的。圖結構可以描述多種複雜的數據對象,應用較爲廣泛。圖結構中的圓圈稱爲頂點,連線稱爲邊,連線附帶的數值稱爲邊的權。

1、有向圖、無向圖

  圖 G 由兩個集合 V 和 E 組成,記爲 G=(V,E),其中,V 是頂點的有窮非空集合;E 是邊的集合,邊是 V 中頂點的偶對。若頂點的偶對是有序的,則稱此圖爲有向圖,有序偶對用尖括號<>括起來;若頂點的偶對是無序的,則稱此圖爲無向圖,無序偶對用圓括號()括起來。

3、無向完全圖、有向完全圖

  任何兩點之間都有邊的無向圖稱爲無向完全圖。一個具有 n 個頂點的無向完全圖的邊數爲n(n-1)/2。

  任何兩點之間都有弧的有向圖稱爲有向完全圖。一個具有 n 個頂點的有向完全圖的弧數爲n(n-1)。

4、權、帶權圖

  圖的邊附帶數值,這個數值叫權。權在實際應用中可表示從一個頂點到另一個頂點的距離、代價或耗費等。每條邊都帶權的圖稱爲帶權圖。

5、頂點的度、入度、出度

  無向圖中頂點 v 的度是與該頂點相關聯的邊的數目,記爲 D(v)。如果 G 是一個有向圖,則把以頂點 v 爲終點的弧的數目稱爲 v 的入度,記爲 ID(v);把以頂點 v 爲始點的弧的數目稱爲 v 的出度,記爲 OD(v)。有向圖中頂點 v 的度爲入度與出度的和,即 D(v)=ID(v)+OD(v)。

6、子圖

  設 G=(V,E)是一個圖,若 E'是 E 的子集,V'是 V 的子集,並且 E'中的邊僅與 V'中的頂點相關聯,則圖 G'=(V',E')稱爲圖 G 的子圖。

7、簡單路徑、迴路、簡單迴路

  序列中頂點不重複出現的路徑稱爲簡單路徑。第一個頂點和最後一個頂點相問的路徑稱爲迴路或環。除了第一個頂點和最後一個頂點外,其餘頂點不重複的迴路,稱爲簡單迴路或簡單環。

8、連通、連通圖、連通分量

  在無向圖中,如果從頂點 v 到頂點 v'有路徑,則稱 v 和 v'是連通的。如果圖中的任意兩個頂點 vi和 vj都是連通的,則稱 G 爲連通圖。連通分量是無向圖中的極大連通子圖。

9、強連通、強連通圖、強連通分量

  對於有向圖來說,如果圖中任意一對頂點 vi和 vj (其中 i≠j)都有頂點 vi 到頂點 vj 的路徑,也有從 vj 到 vi的路徑,即兩個頂點間雙向連通,那麼稱該有向圖是強連通圖。有向圖的極大強連通子圖稱爲強連通分量。

10、生成樹、生成森林

  一個連通圖的生成樹,是含有該連通圖的全部頂點的一個極小連通子圖。若連通圖 G 的頂點個數爲 n,則 G 的生成樹的邊數爲 n-1。如果 G 的一個子圖 G'的邊數大於 n-1,則 G'中一定有環。相反,如果 G'的邊數小於 n-1,則 G'—定不連通。在非連通圖中,由每個連通分量都可得到一個極小連通子圖,即一棵生成樹。那麼這些連通分量的生成樹就組成了一個非連通圖的生成森林。

11、圖的基本運算:

(1) 建立圖 CreateGraph(G,V,E):建立一個圖 G,其中 V 是 G 的頂點集合,E 是 G 的邊的集合;

(2) 取頂點信息 GetVex(G,u):獲取圖 G 中頂點 u 的信息;

(3) 取邊信息 Getarc(G,v):獲取圖 G 中邊(u,v)或<u, v>的信息;

(4) 查詢第一個鄰接點 FirstVex(G,u):獲取圖 G 中頂點 u 的第一個鄰接點;

(5) 查詢下一個鄰接點 NextVex(G, u,v):已知 v 是 u 的一個鄰接點,獲取圖 G 中頂點 u的下一個鄰接點;

(6) 插入頂點 InsertVex(G,v):在圖 G 中插入一個頂點 v;

(7) 刪除頂點 DeleteVex(G,v):在圖 G 中刪除一個頂點 v;

(8) 插入邊 InsertArc(G,v,w):在圖 G 中插入一條邊(v,w)或<v,w>;

(9) 刪除邊 DeleteArc(G,v,w):在圖 G 中刪除一條邊(v,w)或<v,w>;

(10) 遍歷圖 Traverse(G, tag):遍歷圖 G,使 G 中每個頂點被訪問一次且僅被訪問一次,當tag=0,則遍歷的方法爲深度優先搜索,當 tag=1,則遍歷的方法爲廣度優先搜索。

十八、圖的存儲結構

鄰接矩陣

1、鄰接矩陣就是用矩陣來描述圖中頂點之間的關聯關係,在程序設計語言中很容易用二維數組來實現矩陣。設 G=(V,E)是一個圖,其中 V={v0, v1,…,vn-1},那麼 G 的鄰接矩陣 A 定義爲如下的 n階方陣:

注意:無向圖的鄰接矩陣是一個對稱矩陣。

2、利用鄰接矩陣可以判定任意兩個頂點之間是否有邊,並容易求得各個頂點的度。對於有向圖,頂點 vi 的出度 OD (vi)是鄰接矩陣中第 i 行元素之和,頂點 vi 的入度 ID (vi) 是

鄰接矩陣中第 i 列元素之和。

3、無向帶權圖的鄰接矩陣的建立方法是:首先將矩陣 A 的每個元素都初始化爲最大值,然後讀入邊及權值(i,j, wij),將 A 的相應元素置成 wij。

鄰接表

1、鄰接表是順序存儲與鏈式存儲相結合的存儲方法

    在鄰接表中,對圖中每個頂點建立一個單鏈表。對於無向圖,第 i 個單鏈表中的結點表示依賴於頂點 vi 的邊,對於有向圖是以頂點 vi爲尾的弧,這個單鏈表稱爲頂點 vi的鄰接表。每一個單鏈表設一個表頭結點,每一個表頭結點有兩個域 vertex 和 firstarc,vertex 用來存放頂點 vi 的信息,firstarc 用來指向鄰接表中的第一個結點。爲了便於隨機訪問,將所有單鏈表的頭結點組成一個一維數組 Adjlist。單鏈表中每一個結點稱爲表結點,包括兩個域:鄰接點域(adjvex)和鏈域(nextarc),鄰接點域用以存放與 vi相鄰接的頂點在數組 Adjlist 中的序號,鏈域用以指向同 vi鄰接的下一個結點。在帶權圖的表結點中增加一個權值域,用於存儲邊的權值 (weight)。

2、鄰接表的類型定義如下

  如果一個無向圖有 n 個頂點,e 條邊,那麼它的鄰接表需要 n 個頭結點和 2e 個表結點。顯然,在邊稀疏(e<<n(n-1)/2)的情況下,用鄰接表表示比用鄰接矩陣節省存儲空間。

4、逆鄰接表

  對於有向圖,有時需要建立一個逆鄰接表,即對每個頂點 vi 建立一個以 vi 爲弧頭的鄰接點的鏈表。這同鄰接表正好相反。對於逆鄰接表可以很容易求出 vi的入度。

十九、 圖的遍歷

  圖的遍歷是指從圖的某個頂點出發,系統地訪問圖的每個頂點,並且每個頂點只被訪問一次。遍歷圖的基本方法有兩種:深度優先搜索和廣度優先搜索。這兩種方法都適用於有向圖和無向圖。

1、連通圖的深度優先搜索

  連通圖深度優先搜索的基本思想:假定以圖中某個頂點 vi爲出發點,首先訪問出發點 vi,然後任選一個 vi 的未訪問過的鄰接點 vj,以 vj 爲新的出發點繼續進行深度優先搜索,依此類推,直至圖中所有頂點都被訪問過。深度優先搜索遍歷類似於樹的先序遍歷。

2、連通圖的廣度優先搜索

1)、連通圖廣度優先搜索的基本思想是:從圖中某個頂點 vi出發,在訪問了 vi 之後依次訪問vi的所有鄰接點,然後依次從這些鄰接點出發按廣度優先搜索方法遍歷圖的其他頂點,重複這一過程,直至所有頂點都被訪問到。廣度優先搜索遍歷類似於樹的按層次遍歷的過程。

2)、在廣度優先搜索中,若對 x 的訪問先於 y,則對 x 的鄰接點的訪問也先於對 y 的鄰接點的訪問,也就是說廣度優先搜索鄰接點的尋找具有先進先出的特徵。因此,爲了保證結點的這種先後關係,可採用隊列來暫存那些剛訪問過的頂點。

3)、若圖的存儲結構爲鄰接表,隊採用鏈隊列。

4)、如果採用鄰接矩陣作爲存儲結構,查找鄰接點操作實際上通過循環語句順序訪問鄰接矩陣的某一行。

二十、圖的應用

  最小生成樹:一個圖的最小生成樹是圖所有生成樹中權總和最小的生成樹。

二十一、拓撲排序

1、 AOV 網

    如果以圖中的頂點來表示活動,有向邊表示活動之間的優先關係,這種用頂點表示活動的有向圖稱爲 AOV 網。AOV 網中的弧表示了活動之間存在着的制約關係。

2、拓撲排序

   設 G=(V,E)是一個具有 n 個頂點的有向圖,V 中頂點的序列 v1,v2,…,vn 稱爲一個拓撲序列,當且僅當該頂點序列滿足下列條件:若在有向圖 G 中,從頂點 vi到 vj 有一條路徑,則在拓撲序列中頂點 vi必須排在頂點 Vj 之前。找一個有向圖的一個拓撲序列的過程稱爲拓撲排序。完成拓撲排序的前提條件是 AOV 網中不允許出現迴路。

可以證明,任何一個無環有向圖,其全部頂點可以排成一個拓撲序列。

二十二、查找

1、查找表

    查找表(Search Table)是由同一類型的數據元素構成的集合,它是一種以查找爲“核心”,同時包括其他運算的非常靈活的數據結構。

2、靜態查找表、動態查找表

   作爲一種數據結構,查找表的邏輯結構是集合,對查找表進行的操作包括查找表中某一元素、讀取表中“特定”數據元素、插入和刪除一個數據元素等。若對查找表只進行前兩項操作,則稱此類查找表爲靜態查找表。若在查找過程中,向表中插入不存在的數據元素,或者從表中刪除某個數據元素,則稱此類表爲動態查找表。

二十三、靜態查找表

1、順序表上的查找

1)、靜態查找表最簡單的實現方法是以順序表作爲存儲結構。 靜態查找表順序存儲結構的類型定義如下:

const int Maxsize=20; //靜態查找表的表長
typedef struct {
KeyType key; //關鍵字//其他域
}TableElem;
typedef struct {
TableElem elem[Maxsize+l];
int n; //最後一個數據元素的下標
}SqTable;

這裏,將靜態查找表中的數據元素存放在上述數組的第 1 到第 n 個單元中,第 n+1 到最後一個單元爲備用區(空閒區)。

2)、查找長度

    用“數據元素的鍵值與給定值的比較次數”作爲衡量查找算法好壞的依據,稱上述比較次數爲查找長度。將“查找成功時的平均查找長度”(記爲 ASL)作爲評價查找算法時間性能的度量。

2、有序表上的查找

1)、如果順序表中數據元素是按照鍵值大小的順序排列的,則稱爲有序表。二分查找(Binary Search)的查找過程爲每次用給定值與處在表的中間位置的數據元素的鍵值進行比較,確定給定值的所在區間,然後逐步縮小查找區間。

2)、平均查找長度

    二分查找算法每進行一次鍵值與給定值的比較,查找區間的長度至少減小爲原來二分之一,“二分查找”由此得名。由此易推算出二分查找的查找長度不超過 Llog2n」+1。 二分查找的平均查找長度爲ASLb≈log2(n+1)-1

3、索引順序表上的查找

1)、索引順序表是結合了順序查找和二分查找的優點構造的一種帶索引的存儲結構。

  

  一個索引順序表由兩部分組成:一個索引表和一個順序表。其中的順序表在組織形式上與普通的順序表完全相同,而索引表本身在組織形式上也是一個順序表。索引表通過索引將順序表分割爲若干塊,而順序表呈現出“按塊有序”的性質。

2)、若靜態查找表用索引順序表表示,則查找操作可用分塊查找來實現,也稱爲索引順序查找。

3)、平均查找長度:分塊查找的平均查找長度等於兩階段各自的查找長度之和。

4)、優缺點

  靜態查找表的上述三種不同實現各有優缺點。其中,順序查找效率最低但限制最少。二分查找效率最高,但限制最強。而分塊查找則介於上述二者之間。在實際應用中應根據需要加以選擇。

二十四、二叉排序樹

  一種實現動態查找的樹表,這種樹表的結構本身是在查找過程中動態生成的,即對於給定 key,若表中存在與 key 相等的元素,則查找成功,不然,插入關鍵字等於key 的元素。一棵二叉排序樹(又稱二叉查找樹)或者是一棵空二叉樹,或者是具有下列性質的二叉樹:

(1) 若它的左子樹不空,則左子樹上所有結點的鍵值均小於它的根結點鍵值;

(2) 若它的右子樹不空,則右子樹上所有結點的鍵值均大於它的根結點鍵值;

(3) 根的左、右子樹也分別爲二叉排序樹。

1、二叉排序樹上的查找

  由於二叉排序樹的特點,要找鍵值比某結點鍵值小(大)的結點,只需通過此結點的左指針(右指針)到它的左(右)子樹中去找。所以查找操作的遞歸實現較爲簡單。

由上面的查找過程可知:在二叉排序樹上進行查找,若查找成功,則是從根結點出發走了一條從根結點到待查結點的路徑;若查找不成功,則是從根結點出發走了一條從根到某個葉子的路徑。因此與二分查找類似,關鍵字比較的次數不超過二叉樹的深度。

2、二叉排序樹的插入

  在二叉排序樹上進行插入的原則是:必須要保證插入一個新結點後,仍爲一棵二叉排序樹。這個結點是查找不成功時查找路徑上訪問的最後一個結點的左孩子或右孩子。

3、二叉排序樹的查找分析

  二叉排序樹上的平均查找長度是介於 O(n)和 O(log2n)之間的,其查找效率與樹的形態有關。二叉排序樹的平均查找長度ASL≤1+log2n。

二十五、 散列表

  爲了使數據元素的存儲位置和鍵值之間建立某種聯繫,以減少比較次數,使用散列技術實現動態查找表。數據元素的鍵值和存儲位置之間建立的對應關係 H 稱爲散列函數,用鍵值通過散列函數獲取存儲位置的這種存儲方式構造的存儲結構稱爲散列表(Hash Table),這一映射過程稱爲散列。

  如果選定了某個散列函數 H 及相應的散列表 L,則對每個數據元素 X,函數值 H(X.key)就是 X 在散列表 L 中的存儲位置,這個存儲位置也稱爲散列地址。設有散列函數 H 和鍵值 k1、 k2(k1≠k2), 若 H(k1)=H(k2), 則這種現象稱爲“衝突”,且稱鍵值 k1 和 k2 互爲同義詞。

1、常用散列法

  若對於鍵值集合中的任一個鍵值,經散列函數映射到地址集合中任何一個地址的概率是相等的,則稱此種散列函數是均勻的。以下假定散列地址是自然數,另外,假定鍵值也都是自然數。

1)、數字分析法

  數字分析法又稱數字選擇法,其方法是收集所有可能出現的鍵值,排列在一起,對鍵值的每一位進行分析,選擇分佈較均勻的若干位組成散列地址。所取的位數取決於散列表的表長,見表長爲 100,則取 2 位即可。

2)、除留餘數法

  除留餘數法是一種簡單有效且最常用的構造方法,其方法是選擇一個不大於散列表長 n 的正整數 p,以鍵值除以 p 所得的餘數作爲散列地址,即H (key) = key mod p (p≤n)

注意:通常選 p 爲小於散列表長度 n 的素數。

3)、平方取中法

  平方取中法以鍵值平方的中間幾位作爲散列地址。這一方法計算簡單,是一種較常 用的構造散列函數的方法,通常在選定散列函數時不一定能知道鍵值的分佈情況。取其中哪幾位也不一定合適,而一個數平方的中間幾位與這個數的每一位都有關,所得散列地址比較均勻。

4)、基數轉換法

  將鍵值看成另一種進制的數再轉換成原來進制的數,然後選其中幾位作爲散列地址。

2、解決散列表的衝突的方式

  假設散列表的地址集爲 0〜(n-1),衝突是指由鍵值得到的散列地址上己存有元素,則解決衝突就是爲該鍵值的元素找到一個空閒單元的散列地址。通常用來解決衝突的方法有以下幾種:

1)、線性探測法

  對任何鍵值 key,設 H (key) =d,設散列表的容量爲 m,則線性探測法生成的後繼散列地址序列爲d+1,d+2,…,m-1,0,1,…,d-1

2)、二次探測法

  二次探測法的基本思想:生成的後繼散列地址不是連續的,而是跳躍式的,以便爲後續數據元素留下空間從而減少堆積。二次探測法的缺點是不易探測到整個散列表的所有空間,也就是說,上述後繼散列地址可能難以包括散列表的所有存儲位置。

3)、鏈地址法

  鏈地址是對每一個同義詞都建一個單鏈表來解決衝突,其組織方式如下:設選定的散列函數爲 H, H 的值域(即散列地址的範圍)爲 0〜(n-1)。設置一個“指針向量”

Pointer HP [n],其中的每個指針 HP[i]指向一個單鏈表,該單鏈表用於存儲所有散列地址爲 i 的數據元素。每一個這樣的單鏈表稱爲一個同義詞子表。

4)、多重散列法

  此法要求設立多個散列函數 Hi,i=1,…,k。當給定值 key 與散列表中的某個鍵值是相對於某個散列函數氏的同義詞而發生衝突時,繼續計算這個給定值 key 在下一個散列函數 Hi+1下的散列地址,直到不再產生衝突爲止。這種方法的優點是不易產生“堆積”,缺點是計算量較大。

5)、公共溢出區法

  按這種方法,散列表由兩個一維數組組成。一個稱爲基本表,它實際上就是上面所說的散列表,另一個稱爲溢出表。插入首先在基本表上進行,假如發生衝突,則將同義詞存入溢出表。這樣,基本表不可能發生“堆積”。

3、散列表的基本操作算法

1)、鏈地址法散列表

(1)查找

  首先計算給定值 key 的散列地址 i,由它到指針向量中找到指向 key 的同義詞子表的表頭指針。然後,在該同義詞子表中順序查找鍵值爲 key的結點。

(2)插入

  散列表上的插入算法應包含查找功能,並在查找不成功時申請一個結點來存儲這個元素,實施新結點的鏈入操作。用“前插法”插入新結點,即將新結點插到同義詞子表的表頭結點之後、原表首結點(若存在的話)之前。

(3)刪除

2)、線性探測法散列表

 

 感謝閱讀,借鑑了不少大佬資料,如需轉載,請註明出處,謝謝!https://www.cnblogs.com/huyangshu-fs/p/14321997.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章