日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

排序算法杂谈(三) —— 归并排序的非递归实现

發布時間:2023/12/10 编程问答 31 豆豆
生活随笔 收集整理的這篇文章主要介紹了 排序算法杂谈(三) —— 归并排序的非递归实现 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

1. 遞歸

?

在眾多排序算法中,歸并排序(Merge Sort)和快速排序(Quick Sort)都是時間復雜度為 O(nlog2n) 的高效排序。

這兩種排序有一種共性,就是運用到了遞歸的思想。

?

在程序設計中,遞歸是一個很有用的性質,用遞歸寫出來的算法,一般來說都很簡潔明了,易于理解。

例如歸并排序:

public final class MergeSort extends BasicMergeSort {@Overridepublic void sort(int[] array) {sort(array, 0, array.length - 1);}private void sort(int[] array, int left, int right) {if (left < right) {int mid = ArrayHelper.getMiddle(left, right);sort(array, left, mid);sort(array, mid + 1, right);mergeSolution.merge(array, left, right);}} }

?

其中,mergeSolution.merge() 是將兩個有序數組合并成一個有序數組的接口,詳細內容可以參見:

https://www.cnblogs.com/jing-an-feng-shao/p/9038915.html

?

然而,漂亮的代碼與高效的代碼往往是相沖的。遞歸存在一個很致命的缺點。

眾所周知,Java 程序中的數據,存放在堆棧(Stack)空間中,堆棧具有訪問效率高,速度快的優點,但同時其分配的空間往往是有限的。

在遞歸過程中,會把上層的數據不斷地壓入堆棧空間中。如果遞歸地深度過大,在達到遞歸的結束條件之前,堆棧中的數據已經溢出,那么就會導致程序崩潰。

StackOverFlow 這個異常,相信讀者見到的次數不在少數。

?

?

2. 遞歸轉循環

?

所以,在這種情況下,往往就需要將遞歸結構轉化為循環結構去解決問題。

那么,怎么進行轉化呢?這時候就要從遞歸的本質出發。

?

遞歸是什么?它是?數據的傳遞 + 數據的壓棧

所以解決了怎么傳遞數據的問題和怎么數據壓棧的問題,就能將遞歸轉化為循環,那么自然而然地想到了:

  • 記錄傳遞的數據 -> 利用自定義對象 Record 記錄傳遞的數據。
  • 利用堆棧的結構 -> 利用程序手動將 Stack<Record> 進行壓棧的操作。

最后,通過迭代自定義的堆棧內數據,達到遞歸地效果。

?

?

3. 歸并排序的非遞歸實現

?

首先需要明確的一點,是歸并排序在遞歸地過程中傳遞什么數據?

顯然是兩個子數組的左、中、右節點,其中中節點時刻已通過計算得出的,可有可無。

那么我們現在就可以設計對象 Record(為方便使用,這里記錄了中節點):

private static class Record {int left;int mid;int right;private Record(int left, int mid, int right) {this.left = left;this.mid = mid;this.right = right;}}

?

其次,如何將 Record 顯示地入棧?

假設現在我們正在進行長度為 8 的數組排序,那么顯然,我們最終希望堆棧中的數據是這樣的(方便起見,這里的 Record 不計中節點):

?

?

這樣的堆棧結構,自上而下進行合并,就是我們期望的歸并排序的合并順序。

這樣的結構是不是很眼熟?

沒錯,看到這個圖,我們很自然地會想到 漢諾塔(Tower of Hanoi)。

那么又是很自然地,除了目標堆棧,還需要申請另外 2 個輔助空間去完成壓棧工作。

方便起見,這里的輔助空間也使用堆棧結構。

所以現在就有了:初始堆棧 stack1,目標堆棧 stack2,中轉堆棧 stack3。

?

  • 將原始數組壓入 stack1。
  • 將 stack1 中的數據全部取出,放入 stack3。
  • 依次取出 stack3 中的 Record,壓入 stack2;同時判斷 left < mid,若是,將 Record(left, mid) 壓入 stack1;判斷 mid+1 < right,若是,將 Record(mid+1, right) 壓入stack1。
  • 若 stack1 中仍有數據,循環步驟 2、3。
  • 若 stack1 已經清空,表明 stack2 已經滿足上圖結構,即全部數據已經壓入 stack2 中,循環歸并 stack2 中的記錄,直至 stack2 清空。
  • ?

    于是,有如下代碼:

    public class MergeSortLoop extends BasicMergeSort {private static final Stack<Record> stack1 = new Stack();private static final Stack<Record> stack2 = new Stack();private static final Stack<Record> stack3 = new Stack();@Overridepublic void sort(int[] array) {int left = 0;int right = array.length - 1;pushStack(stack1, left, right);while (!stack1.isEmpty()) {popAll(stack1, stack3);while (!stack3.isEmpty()) {Record record = stack3.pop();stack2.push(record);if (record.left < record.mid) {pushStack(stack1, record.left, record.mid);}if (record.mid + 1 < record.right) {pushStack(stack1, record.mid + 1, record.right);}}}while (!stack2.isEmpty()) {Record toBeMerged = stack2.pop();mergeSolution.merge(array, toBeMerged.left, toBeMerged.right);}}private void popAll(Stack<Record> source, Stack<Record> target) {while (!source.isEmpty()) {target.push(source.pop());}}private void pushStack(Stack<Record> stack, int left, int right) {Record record = new Record(left, ArrayHelper.getMiddle(left, right), right);stack.push(record);}private static class Record {int left;int mid;int right;private Record(int left, int mid, int right) {this.left = left;this.mid = mid;this.right = right;}} }

    ?

    ?

    4. 另一種堆棧模型

    將遞歸結構轉為循環結構,核心思想就是構建堆棧模型。

    之前寫到的這種 類-漢諾塔 堆棧模型,其結構非常漂亮,但是構造效率卻相對較低(其中需要用到 2 個額外的輔助空間,更多的入棧-出棧操作)。

    其實,仔細回想歸并排序的遞歸過程,并不是依從這種 類-漢諾塔 堆棧模型的順序去遞歸的,其遞歸過程類似以下堆棧模型:

    ?

    ?

    ?

    構造這種堆棧結構,只需要初始堆棧 stack1,目標堆棧 stack2。

  • 將原始數組壓入 stack1。
  • 取出 stack1 棧頂數據,放入 stack2。
  • 根據取出的數據 record,判斷 left < mid,若是,將 Record(left, mid) 壓入 stack1;判斷 mid+1 < right,若是,將 Record(mid+1, right) 壓入stack1。
  • 若 stack1 中仍有數據,循環步驟 2、3。
  • 若 stack1 已經清空,表明 stack2 已經滿足上圖結構,即全部數據已經壓入 stack2 中,循環歸并 stack2 中的記錄,直至 stack2 清空。
  • ?

    與之前的過程相比,唯一的變化就在于第 2 步,只需要取出棧頂數據,而不是堆棧 stack1 中的所有數據。

    ?

    轉載于:https://www.cnblogs.com/jing-an-feng-shao/p/9084928.html

    總結

    以上是生活随笔為你收集整理的排序算法杂谈(三) —— 归并排序的非递归实现的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。