數據結構與算法基礎
內容提要
- 數組與矩陣
- 線性表
- 廣義表
- 樹與二叉樹
- 圖
- 排序與查找
- 算法基礎及常見的算法
數組
- 數組類型:存儲地址計算
- 一維數組a[n]:a[i]的存儲地址為:a+i*len
- 二維數組a[m][n]:
- a[i][j]的存儲地址(按行存儲)為:a+(i*n+j)*len
- a[i][j]的存儲地址(按列存儲)為:a+(j*n+i)*len
稀疏矩陣

例題
設有如下所示的三角矩陣A[0..8,1..8],將該三角矩陣的非零元素(即行下標不小於列下標的所有元素)按行優先壓縮存儲在數組M[1..m]中,則元素A[i,j](0<=i,j<=i)存儲在數組M的(A)中。


數據結構的定義
1.數據結構的概念
2.數據邏輯結構
線性結構

非線性結構(樹形結構:無環路,圖:有環路)

線性表的定義
1、線性表的概念
(a1,a2,...,a3)
2、線性表常見的兩種存儲結構
- 順序存儲結構(順序表)
- 鏈式存儲結構(鏈表)
線性表
順序表

鏈表
- 單鏈表
- 循環鏈表
- 雙向鏈表

鏈表的基本操作
- 單鏈表刪除結點
- 單鏈表插入結點
- 雙向鏈表刪除結點
- 雙向鏈表插入結點

線性表-順序存儲與鏈式存儲對比

線性表-隊列與棧


例題
輸出受限的雙端隊列是指元素可以從隊列的兩端輸入,但只能從隊列的一端輸出,如下圖所示,若有e1,e2,e3,e4依次進入輸出受限的雙端隊列,則得不到輸出序列(D)

A:e4,e3,e2,e1
B:e4,e2,e1.e3
C:e4,e3,e1,e2
D:e4,e2,e3,e1
廣義表
廣義表是n個表元素組成的有限序列,是線性表的推廣。
通常用遞歸的形式進行定義,記做:LS (a0,a1,....,an)。
注:其中LS是表名,ai是表元素,它可以是表(稱做子表),也可以是數據元素(稱為原子)。其中n是廣義表的長度(也就是最外層包含的元素個數),n=0的廣義表為空表;而遞歸定義的重數就是廣義表的深度,直觀地說,就是定義中所含括號的重數(原子的深度為0,空表的深度為1)。
基本運算:取表頭head(Ls)和取表尾tail(Ls)。
若有:LS1=(a,(b,c),(d,e)) head(LS1)=a tail(LS1)=((b,c),(d,e))
例1,有廣義表LS1=(a,(b,c),(d,e)),則其長度為?深度為?答案:長度為3,深度為2.例2,有廣義表LS1= (a,(b,c),(d,e))),要將其中的b字母取出,操作就為?答案:head(head(tail(LS1)))
樹與二叉樹
- 結點的度
- 樹的度
- 葉子結點
- 分支結點
- 內部結點
- 父結點
- 子結點
- 兄弟結點
- 層次

樹與二叉樹

二叉樹的重要特性:
- 在二叉樹的第i層上最多有2^i-1個結點(i>=1);
- 深度為k的二叉樹最多有2k -1個結點(k>=1);
- 對任何棵二叉樹,如果其葉子結點數為n0,度為2的結點數為n2 ,則n0=n2+1。
- 如果對一棵有n個結點的完全二叉樹的結點按層序編號(從第1層到log2nJ+1層,每層從左到右),則對任一結點i(1<=i<=n),有:
- *如果i=1,則結點i無父結點,是二叉樹的根;如果i>1 ,則父結點是【i/2】;
- *如果2i>n,則結點為i葉子結點,無左子結點;否則,其左子結點是結點2i;
- *如果2i+1>n,則結點無右子葉點,否則,其右子結點是結點2i+1。
樹與二叉樹遍歷
- 前序遍歷 12457836
- 中序遍歷 42785136
- 后序遍歷 48752631
- 層次遍歷 12345678

樹與二叉樹-反向構造二叉樹
由前序序列為ABHFDECG;中序序列為HBEDFAGC構造二叉樹。


樹與二叉樹-樹轉二叉樹
- 孩子結點-左子樹結點
- 兄弟結點-右孩子結點

樹與二叉樹-查找二叉樹
- 二叉排序樹
- 左孩子小於根
- 右孩子大於根

插入結點:
- ①若該鍵值結點已存在,則不再插入,如: 48;
- ②若查找二叉樹為空樹,則以新結點為查找叉樹;
- ③將要插入結點鍵值與插入后父結點鍵值比較,就能確定新結點是父結點的左子結點,還是右子結點。
刪除結點:
- ①若待刪除結點是葉子結點,則直接刪除;
- ②若待刪除結點只有一個子結點,則將這個子結點與待刪除結點的父結點直接連接,如: 56 ;
- ③若待刪除的結點p有兩個子結點,則在其左子樹上,用中序遍歷尋找關鍵值最大的結點s ,用結點s的值代替結點p的值,然后刪除節點s ,節點s必屬於上述①,②情況之一,如89。
樹與二叉樹-最優二叉樹(哈夫曼樹)
(在多媒體的壓縮方式經常使用:無損壓縮)
需要了解的基本概念:
- 樹的路徑長度
- 權
- 帶權路徑長度
- 樹的帶權路徑長度(樹的代價)

例題:假如有一組權值5,29,7,8,14,23,3,11請嘗試構造哈夫曼樹。

樹與二叉樹-線索二叉樹
- 為什么要有線索二叉樹
- 線索二叉樹的概念
- 線索二叉樹的表示
- 如何將二叉樹轉化為線索二叉樹


樹與二叉樹-平衡二叉樹
- 平衡二叉樹的提出原因
- 平衡二叉樹的定義(
- 任意結點的左右子樹深度相差不超過1
- 每結點的平衡度只能為-1、0或1)
- 平衡樹的建立過程
- 動態調平衡問題
例題:對數列{1,5,7,9,8,39,73,88}構造序列二叉樹,可以構造出多顆形式不同的排序二叉樹。


圖-基本概念
完全圖
- 在無向圖中,若每對頂點之間都有一條邊相連,則稱該圖為完全圖(complete graph)

- 在有向圖中,若每對頂點之間都有兩條有向邊相互連接,則稱該圖為完全圖。

圖的存儲-鄰接矩陣
用一個n階方陣R來存放圖中各結點的關聯信息,其矩陣元素Rij定義為:


圖的存儲-鄰接表
首先把每個頂點的鄰接頂點用鏈表示出來,然后用一個一維數組來順序存儲上面每個鏈表的頭指針。


圖-圖的遍歷


圖-拓撲排序
我們把用有向邊表示活動之間開始的先后關系。這種有向圖稱為用頂點表示活動網絡,簡稱AOV網絡

上圖的拓撲序列有:02143567,01243657,02143657,01243567
圖的最小生成樹-普里姆算法


算法基礎-算法的特性
- 有窮性:執行有窮步之后結束
- 確定性:算法中每一條指令都必須有確切的含義,不能含糊不清。
- 輸入(>=0)
- 輸出 (>=1)
- 有效性:算法的每個步驟都能有效執行並能得到確定的結果。例如a=0,b/a就無效。
算法基礎-算法的復雜度
時間復雜度:
是指程序運行從開始到結束所需要的時間。通常分析時間復雜度的方法是從算法中選取一種對於所研究的問題來說是基本運算的操作,以該操作重復執行的次數作為算法的時間度量。一般來說,算法中原操作重復執行的次數是規模n的某個函數T(n)。由於許多情況下要精確計算T(n)是困難的,因此引入了漸進時間復雜度在數量上估計一個算法的執行時間。其定義如下:
如果存在兩個常數c和m ,對於所有的n ,當n>=m時有f(n)<=cg(n) ,則有f(n)=O(g(n))。也就是說,隨着n的增大,f(n)漸進地不大於g(n)。例如,一個程序的實際執行時間為T(n)=3n^3+2n^2+n ,則T(n)=O(n^3). 常見的對算法執行所需時間的度量: O(1)<0(log2n)<O(n)<O(nlog2n)<O(n^2)<O(n^3)<0(2^n)
空間復雜度:
是指對一個算法在運行過程中臨時占用存儲空間大小的度量。-個算法的空間復雜度只考慮在運行過程中為局部變量分配的存儲空間的大小。
查找-順序查找
順序查找的思路:將待查找的關鍵字為key的元素從頭到尾與表中元素進行比較,如果中間存在關鍵字為key的元素,則返回成功;否則,則查找失敗。
查找成功時,順序查找的平均查找長度為(等概率情況下):

查找-二分查找
二分法查找的基本思想是:(設R[low,..,high]是當前的查找區)
(1)確定該區間的中點位置:mid=[(low+high)/2];
(2)將待查的k值與R[mid].key比較,若相等,則查找成功並返回此位置,否則需確定新的查找區間,繼續二分查找,具體方法如下。
●若R[mid].key> k,則由表的有序性可知R[mid,..,n].key均大於k,因此若表中存在關鍵字等於k的結點,則該結點必定是在位置mid左邊的子表R[low,...,mid- 1]中。因此,新的查找區間是左子表R[low,..,high],其中high=mid-1。 ●若R[mid].key<k ,則要查找的k必在mid的右子表 R[mid+1...,high]中,即新的查找區間是右子表R[low,..,high],其中low=mid+1。 ●若R[mid].key=k,則查找成功,算法結束。
(3)下一-次查找是針對新的查找區間進行,重復步驟(1)和(2)。
(4)在查找過程中,low逐步增加,而high逐步減少。如果high<low ,則查找失敗,算法結束。
例題:
請給出在含有12個元素的有序表{1,4,10,16,17,18,23,29,33,40,50,51}中二分查找關鍵字17的過程。

查找-折半查找
折半查找在查找成功時關鍵字的比較次數最多為[log2n]=1次。
折半查找的時間復雜度為O(log2n)。
查找-散列表
散列表查找的基本思想是:已知關鍵字集合U,最大關鍵字為m,設計一個函數Hash,它以關鍵字為自變量,關鍵字的存儲地址為因變量,將關鍵字映射到一個有限的、地址連續的區間T[0..n-1](n<<m)中,這個區間就稱為散列表,散列查找中使用的轉換函數稱為散列函數。
查找-散列表沖突的解決方法
開放定址法是指當構造散列表發生沖突時,使用某種探測手段,產生-個探測的散列地址序列,並且逐個查找此地址中是否存儲了數據元素,如果沒有,則稱該散列地址開放,並將關鍵字存入,否則繼續查找下-個地址。只要散列表足夠大,總能找到空的散列地址將數據元素存入。
- 線性探測法
- 偽隨機數法
例:記錄關鍵碼為(3,8, 12, 17,9),取m=10(存儲空間為10),p=5,散列函數h =key%p。

排序
1、排序的概念
- 穩定與不穩定排序
- 內排序與外排序
2、排序方法分類
- 插入類排序
- (直接插入排序
- 希爾排序)
- 交換類排序
- (冒泡排序
- 快速排序)
- 選擇類排序
- (簡單選擇排序
- 推排序)
- 歸並排序
- 基數排序
排序-直接插入排序
直接插入排序:即當插入第i個記錄,R1,R2,...,Ri-1均已排好序,因此,將第i個記錄Ri依次與Ri-1,...,R2 ,R1進行比較,找到合適的位置插入。它簡單明了,但速度很慢。

排序-希爾排序
希爾(Shell)排序:先取-個小於n的整數d1作為第-個增量,把文件的全部記錄分成d1個組。所有距離為d1的倍數的記錄放在同一個組中。先在各組內進行直接插入排序;然后,取第二個增量d2 <d1,重復上述的分組和排序,直至所取的增量dt=1(dt<dt-1<O<d2<d1) ,即所有記錄放在同組中進行直接插入排序為止。該方法實質上是一種分組插入方法。

排序-直接選擇排序
直接選擇排序的過程是,首先在所有記錄中選出排序碼最小的記錄,把它與第一個記錄交換,然后在其余的記錄內選出排序碼最小的記錄,與第二個記錄交換......依次類推,直到所有記錄排完為止。

排序-推排序的概念
(最復雜的一種)
設有n個元素的序列{K1,K2,...,Kn},當且僅當滿足下述關系之一時,稱之為推。(1)ki<=k2i且ki<=k2i+1;(2)ki>=k2i且ki>=k2i+1;其中(1)稱為小頂堆,(2)稱為大頂堆

排序-堆排序
堆排序的基本思想為:先將序列建立堆,然后輸出堆頂元素,再將剩下的序列建立堆,然后再輸出堆頂元素,依此類推,直到所有元素均輸出為止,此時元素輸出的序列就是一個有序序列。
堆排序的算法步驟如下(以大頂堆為例):
(1)初始時將順序表R[1..n]中元素建立為一個大頂堆,堆頂位於R[1]待序區為R[1..n]. (2)循環執行步驟3 ~步驟4 ,共n-1次。 (3)假設為第次運行,則待序區為R[1..n-i+1],將堆頂元素R[1]與待序區尾元素R[n-i+1]交換,此時頂點元素被輸出,新的待序區為R[1..n-i]。 (4)待序區對應的堆已經被破壞,將之重新調整為大頂堆。
例題:
假設有數組A={1,3,4,5,7,2,6,8,0},初建推過程如下:

例題
將順序表R{80,60,16,50,45,10,15,30,40,20}進行堆排序。

排序-冒泡排序
冒泡排序的基本思想是,通過相鄰元素之間的比較和交換,將排序碼較小的元素逐漸從底部移向頂部。由於整個排序的過程就像水底下的氣泡一樣逐漸向上冒,因此稱為冒泡算法。

排序-快速排序
快速排序采用的是分治法,其基本思想是將原問題分解成若干個規模更小但結構與原問題相似的子問題。通過遞歸地解決這些子問題,然后再將這些子問題的解組合成原問題的解。
快速排序通常包括兩個步驟:
第一步,在待排序的n個記錄中任取一個記錄,以該記錄的排序碼為准,將所有記錄都分成兩組,第1組都小於該數,第2組都大於該數,如圖所示。 第二步,采用相同的方法對左、右兩組分別進行排序,直到所有記錄都排到相應的位置為止。

排序-歸並排序
歸並也稱為合並,是將兩個或兩個以上的有序子表合並成-個新的有序表。若將兩個有序表合並成個有序表,則稱為二路合並。合並的過程是:比較A[i]和A[j]的排序碼大小,若A[i]的排序碼小於等於A[j]的排序碼,則將第一個有序表中的元素A[i]復制到R[k]中,並令i和k分別加1;如此循環下去,直到其中一個有序表比較和復制完,然后再將另一個有序表的剩余元素復制到R中。

排序-基數排序
基數排序是一種借助多關鍵字排序思想對單邏輯關鍵字進行排序的方法。基數排序不是基於關鍵字比較的排序方法,它適合於元素很多而關鍵字較少的序列。基數的選擇和關鍵字的分解是根據關鍵字的類型來決定的,例如關鍵字是十進制數,則按個位、十位來分解。

排序

更多軟考自查在我的主頁 “文章”中可以查看!!!