寫在最開始的那段話:
整理完了對於C語言基礎和一些提升部分的筆記內容,真的是感覺自己漏洞百出,不整理不知道,一整理嚇一跳,對於以前很多的知識點都在忘記,所以也是趕快將這最重要的數據結構撿起來,通過以前的筆記和一些代碼,將這些內容整理出來,方便自己,也方便初學者的朋友們,還是那句話,有則改之無則加勉!
文章目錄
序
1.什麼是數據結構?
數據結構:是計算機存儲,組織數據的方式,指相互之間存在一種或多種特定關係的數據元素的結合。
2. 什麼是算法?
算法:是定義良好的計算過程,他取一個或以組的值爲輸入,並產生出一個或一組的值爲輸出,簡單的來說算法就是一系列的計算步驟,用來將輸入數據轉化成輸出結構。
第一節:時間複雜度和空間複雜度
1. 算法效率
算法效率分析分爲兩種:第一種是時間效率,第二種是空間效率。時間效率被稱爲時間複雜度,而空間效率被稱作空間複雜度。 時間複雜度主要衡量的是一個算法的運行速度,而空間複雜度主要衡量一個算法所需要的額外空間,在計算機發展的早期,計算機的存儲容量很小。所以對空間複雜度很是在乎。但是經過計算機行業的迅速發展,計算機的存儲容量已經達到了很高的程度。所以我們如今已經不需要再特別關注一個算法的空間複雜度。
2. 時間複雜度
算法中的基本操作的執行次數,爲算法的時間複雜度
1. 時間複雜度:
- 需要關注點:操作的數量級,基本操作的執行次數。其中基本操作包含了(一條指令,一組指令和函數調用)。
- 不關注的點:具體得執行時間:
1. 執行時間和硬件資源強相關,不同的硬件處理速度差異可能很大。
2. cpu每秒鐘執行的操作在億計以上,差異不大得操作次數,在直觀感受上得區別微乎其微。
2. 大O漸進表示法
大O符號(Big O notation):是用於描述函數漸進行爲的數學符號。
通過上面我們會發現大O的漸進表示法去掉了那些對結果影響不大的項,簡潔明瞭的表示出了執行次數,因此這個結果就是O(N^2)
2.2 最高次項係數
例一:那麼這個程序得時間複雜度是多少呢?
因此這個程序的時間複雜度就是 O(N)
。
練習題1
void Func(int N)
{
int count = 0;
for (int k = 0; k < 2 * N ; ++ k)
{
++count;
}
int M = 10;
while (M--)
{
++count;
}
printf("%d\n", count);
}
練習題1:基本操作執行了2N+10次,通過推導大O階方法知道,時間複雜度爲 O(N)。
練習題2
void Func(int N, int M)
{
int count = 0;
for (int k = 0; k < M; ++ k)
{
++count;
}
for (int k = 0; k < N ; ++ k)
{
++count;
}
printf("%d\n", count);
}
練習2基本操作執行了M+N次,有兩個未知數M和N,時間複雜度爲 O(N+M)。
2.2 常數項
兩者進行對比,什麼時候所展示出來的是屬於常數,而何時又不屬於常數
練習題1
void Func(int N)
{
int count = 0;
for (int k = 0; k < 100; ++ k)
{
++count;
}
printf("%d\n", count);
}
練習題1基本操作執行了100次,通過推導大O階方法,時間複雜度爲 O(1)。
練習題2
void BubbleSort(int* a, int n)
{
assert(a);
for (size_t end = n; end > 0; --end)
{
int exchange = 0;
for (size_t i = 1; i < end; ++i)
{
if (a[i-1] > a[i])
{
Swap(&a[i-1], &a[i]);
exchange = 1;
}
}
if (exchange == 0)
break;
}
}
練習題2基本操作執行最好N次,最壞執行了(N*(N+1)/2次,通過推導大O階方法+時間複雜度一般看最壞,時間複雜度爲 O(N^2)。
2. 3 對數項時間複雜度
練習題1
int BinarySearch(int* a, int n, int x)
{
assert(a);
int begin = 0;
int end = n-1;
while (begin <= end)
{
int mid = begin + ((end-begin)>>1);
if (a[mid] < x)
begin = mid+1;
else if (a[mid] > x)
end = mid;
else
return mid;
}
return -1;
}
練習題1基本操作執行最好1次,最壞O(logN)次,時間複雜度爲 O(logN) ps:logN在算法分析中表示是底數爲2,對數爲N。有些地方會寫成lgN。(建議通過摺紙查找的方式講解logN是怎麼計算出來的)因爲每一次都是一半一半的進行查找,因此算下來也就是2的多少次方等於整體N。
2.4 函數調用N次
函數每調用一次,算作一次,因此在遞歸函數之中對於函數進行N次調用,則爲O(N)
。
練習題1
long long Factorial(size_t N)
{
return N < 2 ? N : Factorial(N-1)*N;
}
練習題1通過計算分析發現基本操作遞歸了N次,時間複雜度爲O(N)。
練習題2
long long Fibonacci(size_t N)
{
return N < 2 ? N : Fibonacci(N-1)+Fibonacci(N-2);
}
練習題2通過計算分析發現基本操作遞歸了2N次,時間複雜度爲O(2N)。(建議畫圖遞歸棧幀的二叉樹講解).
3. 空間複雜度
空間複雜度是對一個算法在運行過程中臨時佔用存儲空間大小的量度 。空間複雜度不是程序佔用了多少bytes的空間,因爲這個也沒太大意義,所以空間複雜度算的是變量的個數。空間複雜度計算規則基本跟實踐複雜度類似,也使用大O漸進表示法。
練習題1
void BubbleSort(int* a, int n)
{
assert(a);
for (size_t end = n; end > 0; --end)
{
int exchange = 0;
for (size_t i = 1; i < end; ++i)
{
if (a[i-1] > a[i])
{
Swap(&a[i-1], &a[i]);
exchange = 1;
}
}
if (exchange == 0)
break;
}
}
練習題1使用了常數個額外空間,所以空間複雜度爲 O(1)。
練習題2
long long* Fibonacci(size_t n)
{
if(n==0)
return NULL;
long long * fibArray =
(long long *)malloc((n+1) * sizeof(long long));
fibArray[0] = 0;
fibArray[1] = 1;for (int i = 2; i <= n ; ++i)
{
fibArray[i ] = fibArray[ i - 1] + fibArray [i - 2];
}
return fibArray ;
}
練習題2動態開闢了N個空間,空間複雜度爲 O(N)。