資料結構概念
程式設計 = 資料結構 + 演算法.
-
資料: 程式的操作物件,用於描述客觀事物.資料是資訊的載體,在電腦科學中是指所有能輸入到計算機中並能被計算機程式識別和處理的符號集合。可以將資料分為兩大類:一類是整數、實數等數值資料;另一類是文字、聲音、圖形和影像等非數值資料。
資料的特點: 可以輸入到計算機,可以被計算機處理
-
資料結構: 指資料物件中的資料元素之間的關係
-
資料物件: 多個資料元素的集合(類似於陣列)
- 資料元素: 組成資料物件的基本單位
- 資料項: 一個資料元素由若干資料項組成
結構體Teacher就是一種資料結構,定義的name、age是資料項,Teacher 建立的 t1、t2... 是具體的資料元素,由多個t1、t2...構成的陣列tArray[10] 即是資料物件
- 關係圖
邏輯結構
描述資料與資料之間邏輯關係的資料結構
集合結構
所有元素同屬一個集合,沒有先後順序,也不存在其他關係
線性結構
資料元素之間存在著“一對一”的線性關係,它是一種有序資料的集合。
常用的線性結構有:線性表,棧,佇列,雙佇列,陣列,字串。
特點:
1. 集合中必存在唯一的一個"第一個元素";
2.集合中必存在唯一的一個"最後的元素";
3.除最後元素之外,其它資料元素均有唯一的"後繼";
4.除第一元素之外,其它資料元素均有唯一的"前驅"。
複製程式碼
非線性結構
每個資料元素可能與零個或者多個其他資料元素髮生聯絡。即“一對多”關係。
常見的非線性結構有:二維陣列,多維陣列,廣義表,樹(二叉樹等),圖。
儲存結構
資料的邏輯結構在計算機中的表示。資料最終要儲存到計算機記憶體空間中,根據儲存方式的不同分為順序儲存結構 和 鏈式儲存結構。
順序儲存結構
資料元素存放在地址連續的儲存單元裡,其資料間的邏輯關係和物理關係是一致的。例如陣列。
需要在記憶體中先開闢一塊連續的儲存空間。
連結儲存結構
資料元素存放在任意的儲存單元中,這組儲存單元可以是連續的,也可以不是不連續的,資料元素的儲存關係並不能反映其邏輯關係,需要藉助指標來表示資料元素之間的邏輯關係。
每一個元素包含了下一個元素的地址資訊,通過指標儲存。物理上不是一一對應的,而邏輯上是一一對應的。
除了順序儲存方法和鏈式儲存方法外,有時為了查詢方便還採用 索引儲存方法(關鍵字與地址一一對應,屬於廣義上的對映) 和 雜湊表儲存方法(Hash雜湊-->存在函式(對映)關係,通過關鍵字查詢)。
討論資料結構的目的就是在計算機中實現對資料的操作,因此在討論資料的組織結構時必然要考慮在該結構上進行的操作(或稱運算)。
演算法
演算法(algorithm)是解決特定問題求解步驟的描述方法,在計算機中表現為有限的操作序列。演算法與資料結構是相輔相成的。
解決某一類特定問題的演算法可以選定不同的資料結構,而且選擇恰當與否直接影響演算法的效率。
演算法特性
- 有窮性:一個演算法必須在有窮步之後結束,即必須在有限時間內完成。
- 確定性:演算法的每一步必須有確切的定義,無二義性,且在任何條件下演算法只有唯一一條執行路徑,即對於相同的輸入只能得出相同的輸出。
- 可行性:演算法中的每一步都可以通過已經實現的基本運算的有限次執行得以實現。
- 輸入:一個演算法具有零個或多個輸入,這些輸入取自特定的資料物件集合。
- 輸出:一個演算法具有一個或多個輸出,這些輸出同輸入之間存在某種特定的關係。
設計要求
- 正確性:演算法的執行結果應當滿足預先規定的功能和效能要求。正確性要求表明演算法必須滿足實際需求,達到解決實際問題的目標。
- 可讀性:一個演算法應當思路清晰、層次分明、簡單明瞭、易讀易懂。可讀性要求表明演算法主要是人與人之間交流解題思路和進行軟體設計的工具,因此可讀性必須要強。同時一個可讀性強的演算法,其程式的可維護性、可擴充套件性都要好得多,因此,許多時候人們往往在一定程度上犧牲效率來提高可讀性。
- 健壯性:當輸入不合法資料時,應能適當處理,不至於引起嚴重後果。健壯性要求表明演算法要全面細緻地考慮所有可能的邊界情況,並對這些邊界條件做出完備的處理,儘可能使演算法沒有意外的情況。
- 高效性:時間效率高和儲存量低。高效性主要是指時間效率,即解決相同規模的問題時間儘可能短。
演算法效率衡量方法
一個演算法的優劣,可以從該演算法在計算機上執行的時間和所佔儲存空間來衡量和評判。
時間複雜度
大O表示法
若存在函式 f(n),使得當n趨近於無窮大時,T(n)/ f(n)的極限值為不等於零的常數,則稱 f(n)是T(n)的同數量級函式。記作 T(n)= O(f(n)),稱O(f(n)) 為演算法的漸進時間複雜度,簡稱時間複雜度。
推導時間複雜度遵守以下原則:
- 如果執行時間是常數量級,用常數1表示;3->1 O(1)
- 只保留時間函式中的最高階項;n^3+2n^2+5 -> O(n^3)
- 如果最高階項存在,則省去最高階項前面的係數。2n^3 -> n^3
/*
常數階O(1) 無論程式碼執行了多少行,
只要是沒有迴圈等複雜結構,
那這個程式碼的時間複雜度就都是O(1)
*/
//
void testSum1(int n){
int sum = 0; //執行1次
sum = (1+n)*n/2; //執行1次
printf("testSum1:%d\n",sum);//執行1次
}
void add(int x){
x = x+1;// 執行1次
}
複製程式碼
線性階O(n)
//執行n次
int sum = 0;
for (int i = 0; i < n; i++) {
sum += i;
}
複製程式碼
平方階O(n^2)
//x=x++; 執行n*n次
void add3(int x,int n){
for (int i = 0; i< n; i++) {
for (int j = 0; j < n ; j++) {
x=x+1;
}
}
}
//1+(n+1)+n(n+1)+n^2+n^2 = 2+3n^2+2n -> O(n^2)
void testSum5(int n){
int i,j,x=0,sum = 0; //執行1次
for (i = 1; i <= n; i++) { //執行n+1次
for (j = 1; j <= n; j++) { //執行n(n+1)
x++; //執行n*n次
sum = sum + x; //執行n*n次
}
}
printf("testSum5:%d\n",sum);
}
複製程式碼
立方階(n^3) 相當於三層n迴圈
void testB(int n){
int sum = 1; //執行1次
for (int i = 0; i < n; i++) { //執行n次
for (int j = 0 ; j < n; j++) { //執行n*n次
for (int k = 0; k < n; k++) {//執行n*n*n次
sum = sum * 2; //執行n*n*n次
}
}
}
}
複製程式碼
對數階 O(logn)
/*2的x次方等於n x = log2n ->O(logn)*/
void testA(int n){
int count = 1; //執行1次
while (count < n) {
count = count * 2;
}
}
複製程式碼
線性對數階O(nlogN)
/*將時間複雜度為O(logn)的程式碼迴圈N遍的話,那麼它的時間複雜度就是 n * O(logN),也就是了O(nlogN)*/
void testA(int n){
for(m=1; m<n; m++)
{
i = 1;
while(i<n)
{
i = i * 2;
}
}
}
複製程式碼
空間複雜度
程式執行從開始到結束所需的儲存量與問題規模的對應關係,記做: S(n)=Ο(f(n)) 其中n為問題的規模(或大小)。
程式空間計算因素:
1. 寄存本身的指令
2. 常數
3. 變數
4. 輸入
5. 對資料進行操作的輔助空間
考量演算法的空間複雜度,主要考慮演算法執行時所需要的輔助空間.
複製程式碼
int n = 5;
int a[10] = {1,2,3,4,5,6,7,8,9,10};
int temp;
for(int i = 0; i < n/2 ; i++){
temp = a[i];
a[i] = a[n-i-1];
a[n-i-1] = temp;
}
以上例子只需要用到輔助空間temp,所以空間複雜度為O(1)
int b[10] = {0};
for(int i = 0; i < n;i++){
b[i] = a[n-i-1];
}
for(int i = 0; i < n; i++){
a[i] = b[i];
}
這個例子中需要用到b(n)的輔助空間,所以空間複雜度為O(n)
複製程式碼