寫在前面:
該系列博客,是本人系統學習數據結構以及算法的筆記類博客,用於記錄一些知識點以及自己所實現的代碼,以作備忘,歡迎批評交流。
下面開始正文,介紹一下數據結構及算法的入門定義。
目錄
1. 什麼是數據結構、算法
簡而言之,數據結構就是數據的存儲結構,算法就是操作這些數據的方法。
兩者都是前人爲了更高效、更節省空間地進行數據處理而創造的,所聚焦的是“快”和“省”兩個字。
2. 複雜度分析
既然已經知道,數據結構和算法是爲了快且省地進行數據操作,那麼就需要一個指標來評價你的算法到底有多快、多省。
複雜度分析即是爲此而生,包括時間複雜度分析、空間複雜度分析兩種。
主要用到大O分析法:,表示的是複雜度的量級,也即不是具體的時間、內存消耗,而是其消耗與數據規模相關的量級。
常用的複雜度有:
- 常量階:
- 對數階:
- 線性階:
- 線性對數階:
- k次方階:
- 指數階:
- 階乘階:
2.1 時間複雜度
分析時間複雜度時,需要假設每一個單元操作是一個單位時間,然後只需要統計整個程序的操作次數再取最大量級即可。
下面列舉幾種複雜度的程序,即可一目瞭然:
// 程序A:
int a=1;
int b=2;
// 程序B:
int i=1;
while (i <= n) {
i = i * 2;
}
// 程序C:
int i=1;
int sum=0;
while (i <= n) {
sum += i;
}
對於程序A,執行了兩句話,其量級是常量,因此時間複雜度爲;
對於程序B,執行了次,因此其時間複雜度爲;
對於程序C,則執行了次,因此其時間複雜度爲;
其他的時間複雜度同理,只需要記住:我們所求的只是最大量級。
此外,對於有兩個或者多個數據規模的情況,則需要對每個規模單獨計算時間複雜度,然後相加,如:
int cal(int m, int n) {
int sum_1 = 0;
int i = 1;
for (; i < m; ++i) {
sum_1 = sum_1 + i;
}
int sum_2 = 0;
int j = 1;
for (; j < n; ++j) {
sum_2 = sum_2 + j;
}
return sum_1 + sum_2;
}
上述代碼的時間複雜度爲:.
對於嵌套代碼的複雜度,等於嵌套內外代碼複雜度的乘積。這個不難理解,想象一個雙重for循環:
int cal(int n) {
int ret = 0;
int i = 1;
for (; i < n; ++i) {
ret = ret + f(i);
}
}
int f(int n) {
int sum = 0;
int i = 1;
for (; i < n; ++i) {
sum = sum + i;
}
return sum;
}
其時間複雜度爲:.
2.2. 空間複雜度
空間複雜度主要從程序運行過程所申請內存大小來判斷,表示算法的存儲空間與數據規模之間的增長關係。
以上述程序A爲例,其空間複雜度爲。再舉個例子:
int n = 1000;
int[] a = new int[n];
其空間複雜度則爲,因爲申請了個int型內存空間。
3. 最好、最壞、平均、均攤時間複雜度
空間複雜度不必多講,對於時間複雜度,有時候需要考慮不同情況下複雜度的不同,主要分爲:最好、最壞、平均、均攤幾種。
最好情況時間複雜度:在最理想的情況下,執行這段代碼的時間複雜度;
最壞情況時間複雜度:在最糟糕的情況下,執行這段代碼的時間複雜度;
平均時間複雜度:各種情況下的平均情況;
以代碼爲例:
// n表示數組array的長度
int find(int[] array, int n, int x) {
int i = 0;
int pos = -1;
for (; i < n; ++i) {
if (array[i] == x) {
pos = i;
break;
}
}
return pos;
}
可簡單得出:最好情況下,時間複雜度爲,最壞情況下爲。
而平均時間複雜度呢,上述代碼中,是用於查找數組中指定元素的程序,所以分兩種情況:x在數組中、x不在數組中,假設每種情況的概率爲,對於在數組中的情況,x在數組各個位置的概率又爲,因此把所有情況加起來即爲:
因此,平均時間複雜度也爲。
此外,還有一個概念——均攤時間複雜度。這個概念是用於描述好情況、壞情況有規律重複的時候,將壞情況複雜度直接均攤給所有簡單情況,從而計算時間複雜度的方法。
比如:對於一組規模爲n的數據,前n-1中情況,都是複雜度爲,第n種情況複雜度爲,則利用均攤計算方法,其均攤時間複雜度爲.