一、學習算法必要性why:
應用:機器學習、數據挖掘、自然語言處理、密碼學、計算機圖形學等
找工作:貪心、分治、動態規劃、樹、圖等.
二、怎麼做how?
- 窮舉法(萬能算法)
求N個數的全排列
8皇后問題
- 分而治之(減而治之)
二分查找——減而治之
歸併排序——分而治之
- 貪心
最小生成樹 Prim, Kruskal
單源最短路 Dijkstra
- 動態規劃
揹包
士兵路徑
三、時間、空間複雜度
常見時間複雜度分析方法
- 數循環次數
- 均攤分析
- 遞歸式——主定理
• O(1) 基本運算,+,-,*,/,%,尋址
• O(logn) 二分查找
• O(n1/2) 枚舉約數
• O(n) 線性查找
• O() 樸素最近點對
• O(n3) Floyd最短路、普通矩陣乘法
• O(nlogn) 歸併排序、快速排序的期望複雜度、基於比較排序的算法下界
• O(2n) 枚舉全部的子集
• O(n!) 枚舉全排列
• 總結:
優秀 O(1) < O(logn) < O(n1/2) < O(n) < O(nlogn)
可能可以優化 O() < O() < O() < O(n!)
證明:O(nlogn)是基於比較的算法時間複雜度的下限
因爲排序就是排列組合中的一種,總的組合次數是n!種,每一次比較如i<j即可排除一半的組合,
所以當比較了k次,即>n! 所以k>ln(n!) ln(n!) < ln() 所以k> nln(n) 所以O(nlogn)是下限
四、時間複雜度的均攤分析
- 多個操作,一起算時間複雜度,MULTIPOP的隊列,可以一次性出隊k個元素,每個元素只出入隊列一次
- 動態數組尾部插入操作(vector)一旦元素超過容量限制,則擴大一倍,再複製原來的數組,再釋放原數組空間
所以vector當插入n個元素時,可能發生複製的次數是1+2+4+8+.....+ 次 = 2*-1次=O(2n) 所以插入n個數時間複雜度是 O(n);所以插入一個元素的時間複雜度是O(1),所以vector特別快,值得學習。
五、例題1:
• 給定數組a[1…n],求最大子數組和,即找出1<=i<=j<=n,使a[i]+a[i+1]+…+a[ j]最大,LeetCode第53題,最大子序和
• 介紹三個算法
- 暴力枚舉 O(n3)
- 優化枚舉 O(n2)
- 貪心法 O(n)
要求:手寫暴力枚舉算法必須會;暴力枚舉:三重循環,時間複雜度 O(n3) 附加空間複雜度 O(1)
for i = 1 to n
for j = i to n
sum = a[i]+..+a[j]
ans =max(ans, sum)
class Solution {
public int maxSubArray(int[] nums) {
int n = nums.length;
int ans = nums[0];
for(int i = 0; i < n; ++i){
for(int j = i; j < n; ++j){
int sum = 0;
for(int k = i; k <= j; ++k){
sum += nums[k];
if(sum > ans){
ans = sum;
}
}
}
}
return ans;
}
}
重點:優化過程就是解決重複計算的過程上述存在i...j的求和與i....j,j+1的求和,存在i...j求和的重複,所以i....j,j+1 = sum(i....j) + nums[j]即可,提前記錄sum(i....j)和,優化如下
優化枚舉:兩重循環
for i = 1 to n
sum = 0
for j = i to n
sum = sum + a[i]
ans = max(ans, sum)
時間複雜度 O(n2) 附加空間複雜度 O(1)
class Solution {
public int maxSubArray(int[] nums) {
int n = nums.length;
int ans = nums[0];
for(int i = 0; i < n; ++i){
int sum = 0;
for(int j = i; j < n; ++j){
sum += nums[j];
if(sum >= ans){
ans = sum;
}
}
}
return ans;
}
}
貪心法:一重循環
sum = 0 ans = 0
for i = 1 to n
sum = sum + a[i]
ans = max(sum, ans)
if (sum < 0) sum = 0
時間複雜度 O(n) 附加空間複雜度 O(1)
class Solution {
public int maxSubArray(int[] nums) {
int ans = nums[0];
int sum = 0;
int n=nums.length;
for(int i=0; i< n; i++){
sum += nums[i];
if(sum > ans){
ans = sum;
}
if(sum < 0){
sum = 0;
}
}
return ans;
}
}
例題2:設計一個隊列;支持:出隊,入隊,求最大元素;要求O(1);均攤分析
例題3:給定一個正整數組a,是否能以3個數爲邊長構成三角形?即是否存在不同的i,j,k,
• 滿足 a[i] < a[ j] + a[k]
• 並且 a[ j] < a[i] + a[k]
• 並且 a[k] < a[i] + a[ j]