自頂向下歸并排序運用的原理:分治法和遞歸 ?
特點:犧牲空間效率來提升時間效率?
分治法的精髓:
分--將問題分解為規模更小的子問題;
治--將這些規模更小的子問題逐個擊破;
合--將已解決的子問題合并,最終得出“母”問題的解;
時間復雜度為O(nlog?n) 這是該算法中最好、最壞和平均的時間性能。
比較操作的次數介于(nlogn) / 2和nlogn - n + 1。
賦值操作的次數是(2nlogn)。歸并算法的空間復雜度為:o (n)
歸并排序比較占用內存,但卻是一種效率高且穩定的算法。
相比簡單排序(冒泡排序 插入排序) ?減治法 ? ?
1)減去一個常量
2)減去一個常量因子
3)減去的規模是可變的
冒泡排序 時間復雜度O(n2) ?空間復雜度O(1)
遞歸就是程序調用自身不斷深入嵌套,直到滿足條件退出的一種算法
遞歸過程一般通過函數或子過程來實現。遞歸方法:在函數或子過程的內部,直接或者間接地調用自己的算法。
圖解
遞歸執行圖:
歸并排序是建立在歸并操作上的一種有效的排序算法,該算法是采用分治法(Divide and Conquer)的一個非常典型的應用。將已有序的子序列合并,得到完全有序的序列;即先使每個子序列有序,再使子序列段間有序。若將兩個有序表合并成一個有序表,稱為二路歸并。
基本思路:
先遞歸的把數組劃分為兩個子數組,一直遞歸到數組中只有一個元素,然后再調用函數把兩個子數組排好序,因為該函數在遞歸劃分數組時會被壓入棧,所以這個函數真正的作用是對兩個有序的子數組進行排序
java實現歸并排序:
```
```
在main函數調用func_A的時候,首先在自己的棧幀中壓入函數返回地址,然后為func_A創建新棧幀并壓入系統棧
在func_A調用func_B的時候,同樣先在自己的棧幀中壓入函數返回地址,然后為func_B創建新棧幀并壓入系統棧
在func_B返回時,func_B的棧幀被彈出系統棧,func_A棧幀中的返回地址被“露”在棧頂,此時處理器按照這個返回地址重新跳到func_A代碼區中執行
在func_A返回時,func_A的棧幀被彈出系統棧,main函數棧幀中的返回地址被“露”在棧頂,此時處理器按照這個返回地址跳到main函數代碼區中執行
在實際運行中,main函數并不是第一個被調用的函數,程序被裝入內存前還有一些其他操作,上圖只是棧在函數調用過程中所起作用的示意圖
ESP:棧指針寄存器(extended stack pointer),其內存放著一個指針,該指針永遠指向系統棧最上面一個棧幀的棧頂
EBP:基址指針寄存器(extended base pointer),其內存放著一個指針,該指針永遠指向系統棧最上面一個棧幀的底部
函數棧幀:ESP和EBP之間的內存空間為當前棧幀,EBP標識了當前棧幀的底部,ESP標識了當前棧幀的頂部。
函數調用大致包括以下幾個步驟:
參數入棧:將參數從右向左依次壓入系統棧中
返回地址入棧:將當前代碼區調用指令的下一條指令地址壓入棧中,供函數返回時繼續執行
代碼區跳轉:處理器從當前代碼區跳轉到被調用函數的入口處
棧幀調整:具體包括
保存當前棧幀狀態值,已備后面恢復本棧幀時使用(EBP入棧)
將當前棧幀切換到新棧幀。(將ESP值裝入EBP,更新棧幀底部)
給新棧幀分配空間。(把ESP減去所需空間的大小,抬高棧頂)
參考文獻:1.圖解排序算法(四)之歸并排序
? ? ? ? ? ? ? ? ? 2.函數調用過程中棧到底是怎么壓入和彈出的 ?