排序算法
冒泡排序/Bubble Sort
插入排序/Insertion Sort
歸併排序/Merge Sort
快速排序/Quick Sort
拓撲排序/Topological Sort
遞歸和回溯/Recursion & Backtracking
遞歸的基本性質:函數調用本身
把大規模的問題不斷地變小,再進行推導的過程
回溯:利用遞歸的性質
從問題的起始點出發,不斷嘗試
返回一步甚至多步再做選擇,直到抵達終點的過程
遞歸
遞歸算法是一種調用自身函數的算法
特點:樂意使一個看似複雜的問題變得簡潔和易於理解
經典案例:漢諾塔(又稱河內塔)
遞歸的算法思想
要懂得如何將一個問題的規模變小
再利用從小規模問題中得出的結果
結合當前的值或者情況,得出最終的結果
通俗理解(自頂向下算法)
把要實現的遞歸函數,看成已經實現好的
直接利用解決一些子問題
思考:如何根據子問題的解以及當前面對的情況得出答案
遞歸寫法結構總結
function fn(n)
{
//第一步:判斷輸入或者狀態是否非法
if (input/state is valid)
{
return;
}
//第二步:判斷遞歸是否應當結束
if (match condition)
{
return some value;
}
//第三步:縮小問題規模
result1 = fn(n1);
result2 = fn(n2);
//第四步:整合結果
return combine(result1, result2)
}
兩種遞歸算法解決時間複雜度分析
迭代法
公式法
迭代法
以漢諾塔問題爲例
void hano(char A, char B, char C, int n)
{
if (n>0)
{
hano(A, C, B, n - 1);
move(A, C);
hano(B, A, C, n - 1);
}
}
時間複雜度分析
if判斷語句:O(1)
move語句:O(1)
hano(A, C, B, n-1):T(n-1)
hano(B,A,C,n-1):T(n-1)
總的時間複雜度
T(n)= 1+2*T(n-1)+1=2T(n-1)+O(1)
當n=0時,T(n)=1。因爲當沒有盤子時,程序在if語句也要執行一次,判斷一下n是否大於0
最後得到的結果爲T(n)=2^n (2的n次方)
有時很難通過迭代法推導出比較複雜的時間複雜度,可以借用公式法。
公式法
公式法是計算遞歸函數複雜度最方便的工具。
只需要牢記3種可能會出現的情況以及處理他們的公式即可
當遞歸函數的時間執行函數滿足如下的關係式時,可以利用公式法:
對於時間複雜度的分析是算法面試中非常重要的一環,掌握好迭代法和公式法對於分析大多數面試題都有非常重要的幫助。
**
**
回溯
回溯算法是一種試探算法,與暴力搜索最大的區別:
在回溯算法中,是一步步向前試探,對每一步探測的情況評估,再決定是否繼續,可避免走彎路
回溯算法的精華
出現非法的情況時,可退到之前的情景,可返回一步或多步
再去嘗試別的路徑和辦法
想要採用回溯算法,就必須保證:每次都有多種嘗試的可能。
回溯法解決問題的套路
function fn(n)
{
//第一步:判斷輸入或者狀態是否非法
if (input/state is invalid)
{
return;
}
//第二步:判斷遞歸是否應當結束
if (match condition)
{
return some value;
}
//遍歷所有可能出現的情況
for (all possible cases)
{
//第三步: 嘗試下一步的可能性
Solutoin.push(case);
//遞歸
result = fn(m);
//第四步:回溯到上一步
Solution.pop(case)
}
}
回溯其實是用遞歸實現的
因此在分析回溯的時間複雜度時,其實就是在對遞歸函數進行分析
分析一下N皇后的事假複雜度
假設backtracking 函數的執行時間是T(n)
-
首先每次都必須遍歷所有的列,這裏一共有n列
a. 先要利用check函數檢查當前的擺放方法會不會衝突
b. 檢查的時間複雜度由當前所在的行決定
c. 但其上限是n,即需要檢查n行所以這裏的總時間複雜度就是O(n2) -
接下來,遞歸地嘗試着每種擺放
a. 當放好了第1個皇后,接下來要處理之後的n-1個皇后
b. 問題的規模減少了一個,於是執行時間變成了T(n-1) -
最終得到T(n)的表達式: T(n)=n*T(n-1) +O(n^2)