void
qsort
(int arr,
int low,
int high)
}
尾遞迴詳細介紹
尾遞迴概念:
如果乙個函式中所有遞迴形式的呼叫都出現在函式的末尾,當遞迴呼叫是整個函式體中最後執行的語句且它的返回值不屬於表示式的一部分時,這個遞迴呼叫就是尾遞迴。尾遞迴函式的特點是在回歸過程中不用做任何操作,這個特性很重要,因為大多數現代的編譯器會利用這種特點自動生成優化的**。
尾遞迴原理:
當編譯器檢測到乙個函式呼叫是尾遞迴的時候,它就覆蓋當前的活動記錄而不是在棧中去建立乙個新的。編譯器可以做到這點,因為遞迴呼叫是當前活躍期內最後一條待執行的語句,於是當這個呼叫返回時棧幀中並沒有其他事情可做,因此也就沒有儲存棧幀的必要了。通過覆蓋當前的棧幀而不是在其之上重新新增乙個,這樣所使用的棧空間就大大縮減了,這使得實際的執行效率會變得更高。
快排的遞迴函式和尾遞迴有些類似,但因為呼叫了兩次自身,所以並不屬於尾遞迴,也不會被編譯器優化。
但是,我們可以手動將第二個遞迴改為迭代,對其進行優化,節省棧空間。
【tip】兩個呼叫函式位置可以顛倒,可以對任意乙個進行優化,將其改為迭代。為了盡可能地節省遞迴棧,我們對比較兩個區間的長度,對長度更長的進行優化,將其改為迭代。
偽**:
tail-recursive-quicksort'(a, p, r)
while p < r
q =partition
(a, p, r)
if(q - p < r - q)
tail-recursive-quicksort'(a, p, q)
p = q +
1else
tail-recursive-quicksort'(a, q +
1, r)
r = q -
1
c++:
void
qsort
(int arr,
int low,
int high)
else
}}
1.減少棧深度
對於劃分不平衡的情況,傳統版本棧深度最多可以達到o(n),尾遞迴優化後,棧深度最多為o(lgn)。
例如:[1 2 3 4 5 6]最壞情況下 partion()函式 每次劃分都選取末尾元素作為基準:6、5、4、3、2、1、0
未優化時:
qsort(arr,0,6)——>qsort(arr,0,5)——>qsort(arr,0,4)——>qsort(arr,0,3)——>qsort(arr,0,2)——>qsort(arr,0,1)—>qsort(arr,0,0)
箭頭表示新開闢的棧,棧深度達到了o(n)。
優化後該情況下只需要o(1)棧空間,因為左區間的遞迴被迴圈取代了。
【tip】網上的很多尾遞迴優化,固定優化右區間,對於上面的例子,仍然需要o(n)的棧深度。
2.棧深度減少了,因此大大增加了可排序的資料量,大大降低了爆棧的可能。
快速排序詳細分析(Java實現)
排序過程 1,找乙個基準數,找陣列內的任意乙個數都行,一般都是以陣列第乙個數為基準數 2,從陣列末位向前迴圈找比基準數小的,找到了先停下,也就是記錄當前的索引 從陣列開端向後迴圈找比基準數大的,找到了先停下,也就是記錄當前的索引 3,將第二步的兩個索引的位置的數值交換,這樣比基準數大的就到了基準數的...
矩陣快速冪優化DP 例題詳細分析
矩陣快速冪性質 快速求解線性遞推式的結果,如斐波那契遞推 優化的 dp 需要滿足的條件 轉移方程為線性遞推式 轉移次數超級多 n 很大 衣食無憂的 q老師 有一天突發奇想,想要去感受一下勞動人民的艱苦生活。具體工作是這樣的,有 n 塊磚排成一排染色,每一塊磚需要塗上紅 藍 綠 黃這 4 種顏色中的其...
歸併排序詳細分析
通過定義兩個儲存空間,第乙個儲存空間為使用者輸入的陣列段。在第乙個儲存空間中每次分組比較,然後將結果排入第二個儲存空間中。週期結束後兩個儲存空間交換。從菜鳥教程學習的這個演算法,菜鳥教程中歸併排序有兩個實現方法 乙個是迭代,乙個是遞迴。這裡我主要針對迭代分析。void merge sort int ...