本筆記來自於浙大陳越,何欽銘老師https://www.bilibili.com/video/av55114968?p=1
一、數據結構
1.到底什麼是數據結構?
- 數據對象在計算機中的組織方式
- 邏輯結構
- 物理存儲結構
2.抽象數據類型(Abstarct Data Type)
- 數據類型
- 數據對象集
- 數據集合相關聯的操作集
- 抽象:描述數據類型的方法不依賴於具體實現
- 與存放數據的機器無關
- 與數據存儲的物理結構無關
- 與實現操作的算法和編程語言均無關
只描述數據對象集和相關操作集“是什麼”,並不涉及“如何做到”的問題。
二、算法(Algorithm)
1.定義
- 一個有限指令集
- 接受一些輸入(有些情況下不需要輸入)
- 產生輸出
- 一定在有限步驟之後終止
- 每一條指令必須:
- 有充分明確的目標,不可以有歧義
- 計算機能處理範圍內
- 描述應不依賴於任何一種計算機語言以及具體的實現手段
2.評價算法好壞
- 空間複雜度 S(n) ——————根據算法寫成的程序在執行時佔用存儲空間的長度
- 時間複雜度 T(n) ——————根據算法寫成的程序在執行時耗費的時間的長度
3.分析算法效率
- 最壞情況複雜度Tworst(n)
- 平均複雜度Tavg(n) Tavg(n)≤ Tworst(n)
一般情況下我們更關心最壞情況複雜度。
4.複雜度的漸進表示法
- T(n)=O(f(n))表示存在常數C>0,n0 >0,使得當n>n0時有T(n)≤C·f(n)
5.複雜度分析小竅門
應用實例
1. 最大子列和問題
給定N個整數的序列{A1,A2…AN},求函數的最大值。(wordpress的markdown插件有問題實在打不出公式)
算法一:
思路:把所有的連續子列和全部算出來,從中找最大的一個
int MaxsubseqSum1(int A[], int N)
{
int ThisSum, MaxSum = 0;
int i, j, k;
for (i = 0; i < j; i++)//i是子列左端位置 A[i]
{
for (j = i; j < N; j++)//j是子列右端位置 A[j]
{
ThisSum = 0; //ThisSum是從A[i]到A[j]的子列和
for (k = i; k <= j; k++)
{
ThisSum += A[k];
}
if (ThisSum > MaxSum)//如果剛剛得到的子列更大
{
MaxSum = ThisSum;//則更新結果
}
}//j循環結束
}//i循環結束
return MaxSum;
}
複雜度分析:T(N)=O(N3) 三層嵌套,非常笨拙
算法二:
分析:優化一,一中k循環可以改爲j循環時加上A[j]的值,直接求出i到j的和,不需要從頭開始算
int MaxsubseqSum2(int A[], int N)
{
int ThisSum, MaxSum = 0;
int i, j, k;
for (i = 0; i < j; i++)//i是子列左端位置 A[i]
{
ThisSum = 0; //ThisSum是從A[i]到A[j]的子列和
for (j = i; j < N; j++)//j是子列右端位置 A[j]
{
ThisSum += A[j];//對於相同的i,不同的j,只需要在j-1次循環的基礎上累加一次即可
if (ThisSum > MaxSum)
MaxSum = ThisSum;
}//j循環結束
}//i循環結束
return MaxSum;
}
複雜度分析:T(N)=O(N2) 兩層嵌套
當一個程序的複雜度爲(O(N2)時,應該想能否將複雜度改爲
算法三:分而治之
分析: 分而治之
所謂“分而治之” 就是把一個複雜的算法問題按一定的“分解”方法分爲等價的規模較小的若干部分,然後逐個解決,分別找出各部分的解,把各部分的解組成整個問題的解百度百科 https://www.jianshu.com/p/038352946ba3這篇文章講的很好
如這個問題可以把它分解爲最小,即兩個元素。
//分成最小,返回最大值
int Max3(int A, int B,int C)
{
return A > B ? A > C ? A : C : B > C ? B : C;
// // 即A > B ? (A > C ? A : C) : (B > C ? B : C) ;
}
/*分治法*/
int DivideAndConquer(int List[], int left, int right)//這個函數的目的是求最大子列和
{
//分
//分解到最後剩一個數字時,停止,終止條件
if (List[left] > 0)
return List[left];
else
return 0;
//先找左右最大子列和
int center = (left + right) / 2;//找中心點
int MaxLeftSum = DivideAndConquer(List, left, center);//遞歸調用求最大子列和
int MaxRightSum = DivideAndConquer(List, center + 1, right);//記得加一
//再求跨分界線的最大子列和
//從邊界出發向左找最大子列和
int MaxLeftBorderSum = 0;
int LeftBorderSum = 0;
for (int i = center; i >= left; i--)
{
/*中線向左掃描*/
LeftBorderSum += List[i];
if (LeftBorderSum > MaxLeftBorderSum)
MaxLeftBorderSum = LeftBorderSum;
}
//從邊界出發向右找最大子列和
int MaxRightBorderSum = 0;
int RightBorderSum = 0;
for (int i = center + 1; i <= right; i++)
{
/*中心線向右掃描*/
RightBorderSum += List[i];
if (RightBorderSum > MaxRightBorderSum)
MaxRightBorderSum = RightBorderSum;
}
//治
//返回三段最大值
return Max3(MaxLeftSum, MaxRightSum, MaxLeftBorderSum + MaxRightBorderSum);
}
int MaxSubseqSum3(int List[], int N)
{
/* 重要:保持與前2種算法相同的函數接口 */
return DivideAndConquer(List, 0, N - 1);//數組與左右值
}
複雜度分析:
- T(N)爲左右和中間三部分複雜度之和,只有一個元素時,複雜度爲1,
- 左右子列也可以再次分解爲左右子列,因此可以將1代入
即
- 一共可以展開k次,展開到(向上取整),即時(N是元素個數,當分到1時停止)
- 因爲,所以,所以3爲,兩項在一起取比較大的一項,所以
算法四:在線處理
int MaxSubseqSum4(int A[], int N)
{
int ThisSum, MaxSum;
int i;
ThisSum = MaxSum = 0;
for (i = 0; i < N; i++)
{
ThisSum += A[i];//向右累加
if (ThisSum > MaxSum)
MaxSum = ThisSum;//發現更大和則更新當前結果
else if (ThisSum < 0)//如果當前子列和爲負
ThisSum = 0;//則不可能使當前子列和增大,故棄之
}
return MaxSum;
}
“在線”的意思是指每輸入一個數劇就進行及時處理,在任何一個地方終止輸入,算法都能正確給出當前的解。
參考參考
如上圖,在線處理運行中的一個斷點的狀態。對於這個數組,最後得到的最大子列和爲7(i=5與i=6的和)。
最後歡迎大家訪問我的個人博客青蛙聽禪的個人博客