第一章 緒論
1.1數據結構討論的範疇
Niklaus Wirth:
Algorithm + Data Structures = Programs
程序設計:爲計算機處理問題編制一組指令集
算法:處理問題的策略
數據結構:問題的數學模型
例如:數值計算的程序設計問題
結構靜力分析計算
——線性代數方程組
全球天氣預報
——環流模式方程
非數值計算的程序設計問題
例一:求一組(n個)整數中的最大值
算法:基本操作是“比較兩個數的大小”
模型:計算機整數
例二:計算機對弈
算法:對弈的規則和策略
模型:棋盤和棋子的表示
例三:足協的數據庫管理
算法:需要管理的項目、如何管理、用戶界面
模型:各種各樣的表格和數據庫
概括地說,數據結構描述現實世界實體的數學模型(非數值計算)及其上的操作在計算機中的表示和實現。
1.2基本概念
一、數據與數據結構
數據:所有能被輸入到計算機中,且被計算機處理的符號的集合,計算機操作的對象的總稱,是計算機處理的信息的某種特定的符號表示形式。
數據元素:數據中的一個“個體”,數據結構中討論的基本單位但不是最小單位。
數據項:數據結構中討論的最小單位,數據元素是數據項的集合。
例如:運動員(數據元素)
姓名 |
俱樂部名稱 |
出生日期 |
參加日期 |
職務 |
業績 |
數據結構:帶結構的數據元素的集合
例如,一個含12位數的十進制數可以用三個4位的十進制數表示
3214,6587,9345——a1(3214),a2(6587),a3(9345)
在a1、a2和a3之間存在“次序”關係
<a1,a2>、<a2,a3>
3214,6587,9345 ≠ 6587,3214,9345
a1 a2 a3 a2 a1 a3
又例,2行3列的二維數組{a1,a2,a3,a4,a5,a6}
a1 |
a2 |
a3 |
a4 |
a5 |
a6 |
行的次序關係:
Row={<a1,a2>,<a2,a3>,<a4,a5><a5,a6>}
列的次序關係:
Col={<a1,a4>,<a2,a5>,<a3,a6>}
a1 a3 a5 ≠ a1 a2 a3
a2 a4 a6 a4 a5 a6
數據的邏輯結構可歸爲一下四類:
線性結構
樹形結構
圖狀結構
集合結構
數據結構的形式定義爲:
數據結構是一個二元組
Data_Structures = (D,S)
其中:D是數據元素的有限集,S是D上關係的有限集。
數據的存儲結構
——邏輯結構在存儲器中的映像
數據元素的映像方法:
用二進制位(bit)的位串表示數據元素
關係的映象方法:(表示<x,y>的方法)
順序映象:以存儲位置的相鄰表示後繼關係
y的存儲位置和x的存儲位置之間差一個常量C
而C是一個隱含值,整個存儲結構中只含數據元素本身的信息
鏈式映象:以附加信息(指針)表示後繼關係
需要用一個和x在一起的附加信息指示y的存儲位置
在不同的編程環境中,存儲結構可有不同的描述方法,當用高級程序設計語言進行編程是,通常可用高級編程語言中提供的數據類型描述之。
例如:以三個帶有次序關係的整數表示一個長整數時,可利用C語言中提供的整數數組類型,定義長整數爲:
typedfint Long_int [3]
二、數據類型
在用高級程序語言編寫的程序中,必須對程序中出現的每個變量,常量或表達式,明確說明它們所屬的數據類型。
數據類型是一個值的集合和定義在此集合上的一組操作的總稱。
三、抽象數據類型(Abstract Data Type 簡稱ADT)
是指一個數學模型以及定義在此數學模型上的一組操作。
ADT有兩個重要特徵:
數據抽象
用ADT描述程序處理的實體時,強調的是其本質的特徵、其所能完成的功能以及它和外部用戶的接口(即外界使用它的方法)。
數據封裝
將實體的外部特性和其內部實現細節分離,並且對外部用戶隱藏其內部實現細節
例如 抽象數據類型複數的定義:
ADT Complex{
數據對象:
D = {e1,e2 | e1,e2 ∈RealSet}
數據關係:
R1 = {<e1,e2>|e1是複數的實數部分,
|e2是複數的虛數部分}
基本操作:
InitComplex(&Z,v1,v2)
操作結果:構造複數Z,其實部和虛部分別被賦以參數v1和v2的值。
DestroyComplex(&Z)
操作結果:複數Z被銷燬。
GetReal(Z,&realPart)
初始條件:複數已存在。
操作結果:用realPart返回複數Z的實部值。
GetImag(Z,&ImagPart)
初始條件:複數已存在。
操作結果:用ImagPart返回複數Z的虛部值。
Add(z1,z2,&sum)
初始條件:z1,z2是複數。
操作結果:用sum返回兩個複數z1,z2的和值。
}ADT Complex
抽象數據類型的描述方法
抽象數據類型可用(D,S,P)三元組表示
其中,D是數據對象,
S是D上的關係集,
P是對D的基本操作集。
抽象數據類型的表示和實現
抽象數據類型需要通過固有數據類型(高級編程語言中已實現的數據類型)來實現。
1.3算法和算法的衡量
一、算法
算法是爲了解決某類問題而規定的一個有限長的操作序列。一個算法必須滿足一下五個重要特性:
1.有窮性 對於任意一組合法輸入值,在執行有窮步驟之後一定能結束,即:算法證的每個步驟都能在有限時間內完成;
2.確定性 對於每種情況下所應執行的操作,在算法中都有確切的規定,使算法的執行者或閱讀者都能明確其含義及如何執行。並且在任何條件下,算法都只有一條執行路徑;
3.可行性 算法中的所有操作都必須足夠基本,都可以通過已經實現的基本操作運算有限次實現之;
4.有輸入 作爲算法加工對象的量值,通常體現爲算法中的一組變量。有些輸入需要在算法執行過程中輸入,而有的算法表明上可以沒有輸入,實際上已被嵌入算法之中;
5.有輸出 它是一組與“輸入”與確定關係的量值,是算法進行信息加工後得到的結果,這種確定關係即爲算法的功能。
二、算法設計的原則
設計算法時,通常應考慮到以下目標:
1.正確性
首先,算法應當滿足以特定的“規格說明”方式給出的需求。
其次,對算法是否“正確”的理解可以有以下四個層次:
A.程序中不含語法錯誤;
B.程序對於幾組輸入數據能夠得出滿足要求的結果;
C.程序對於精心選擇的、典型、苛刻且帶有刁難性的幾組輸入數據能夠得出滿足要求的結果;
D.程序對於一切合法的輸入數據都能得出滿足要求的結果;
通常以第C層意義的正確性作爲衡量一個算法是否合格的標準。
2.可讀性
算法主要是爲了人的閱讀與交流,其次纔是爲計算機執行。因此算法應該易於人的理解;另一方面,晦澀難讀的程序易於隱藏較多錯誤而難以調試。
3.健壯性
當輸入數據非法時,算法應當恰當地作出反映或進行相應處理,而不是產生莫名其妙的輸出結果。並且,處理出錯的方法不應是中斷程序的執行,而應是返回一個表示錯誤或錯誤性質的值,以便在更高的抽象層次上進行處理。
4.高效率與低存儲量需求
通常,效率指的是算法執行時間;存儲量指的是算法執行過程中所需的最大存儲空間,兩者都與問題的規模有關。
三、算法效率的衡量方法和準則
通常有兩種衡量算法效率的方法:
事後統計法
缺點:1.必須執行程序
2.其它因素掩蓋算法本質
事前分析估算法
和算法執行時間相關的因素:
1.算法選用的策略
2.問題的規模
3.編寫程序的語言
4.編譯程序產生的機器代碼的質量
5.計算機執行指令的速度
算法的時間複雜度:
一個特定算法的“運行工作量”的大小,只依賴於問題的規模(通常用整數量n表示),或者說,它是問題規模的函數。
假如,隨着問題規模n的增長,算法執行時間的增長率和f(n)的增長率相同,則可記作:T(n)= O(f(n))
稱T(n)爲算法的(漸近)時間複雜度
如何估算算法的時間複雜度?
算法 = 控制結構 + 原操作(固有數據類型的操作)
算法的執行時間 = ∑原操作(i)的執行次數×原操作(i)的執行時間
算法的執行時間與原操作執行次數之和成正比
從算法中選取一種對於所研究的問題來說是基本操作的原操作,以該基本操作在算法中重複執行的次數作爲算法運行時間的衡量準則。
例一:
for(i = 1;i <= n; ++ i)
for(j = 1;j <= n; ++ j){
c[i,j] = 0;
for(k = 1;k <= n; ++ k)
c[i,j] += a[i,k] * b[k,j];
}
基本操作:乘法操作
時間複雜度:O(n3)
例二
void select_sort(int a[],int n){
//將a中整數序列重新排列成自小至大有序的整數序列。
for(i = 0;i < n-1; ++ i ){
j = i;
for(k = i + 1;k < n; ++ k)
if (a[k] < a[j]) j = k;
if(j != i) a[j] ←→ a[i]
}
}//select_sort
基本操作:比較(數據元素)操作
時間複雜度:O(n2)
例三
void bubble_sort(int a[],int n){
//將a中整數序列重新排列成自小至大有序的整數序列。
for(i = n-1,change = TRUE; i > 1 && change; -- i){
change = FALSE;
for(j = 0; j < i; ++j)
if(a[j] > a[j + 1]){
a[j] ←→ a[j+1];
change = TRUE;
}
}
}//bubble_sort
基本操作:賦值操作 時間複雜度:O(n2)
四、算法的存儲空間需求
算法的空間複雜度
S(n) = O(g(n))
表示隨着問題規模n的增大,算法運行所需存儲量的增長率與g(n)的增長率相同。
算法的存儲量包括:
1.輸入數據所佔空間;
2.程序本身所佔空間;
3.輔助變量所佔空間。
若輸入數據所佔空間只取決於問題本身,和算法無關,則只需要分析除輸入和程序之外的輔助變量所佔額外空間。
若所需額外空間相對於輸入數據量來說是常數,則稱此算法爲原地工作。
若所需存儲量依賴於特定的輸入,則通常按最壞情況考慮。