簡體   English   中英

遞歸和非遞歸合並排序算法之間的時間/空間復雜度是否存在差異?

[英]is there a difference in time/space complexity between recursive and non-recursive merge sort algorithm?

我已經使用分而治之的方法實現了一個合並排序算法,其中一個數組被分成兩個子 arrays。

在我的代碼中,我重新使用插入排序算法對合並排序中的子 arrays 進行排序。 這是正確的方法還是我必須使用不同的排序方法在合並排序中對子 arrays 進行排序?

就歸並排序算法的理解而言,一切都清楚了,但是當談到歸並排序的實現時,如何在不使用遞歸策略的情況下將一個數組划分為n-sub arrays。

是遞歸還是非遞歸有效的方式來實現歸並排序?

下面是我在 github 中的代碼片段: https://github.com/vamsikankipati/algorithms-in-java/blob/master/src/com/algorithms/sort/MergeSort.Z33321F725FZ48B28

我從實現的角度理解我的代碼是錯誤的,因為我將數組分成兩個子 arrays 而不是 n-sub arrays。

從算法實現的角度清楚地理解歸並排序所需的任何幫助。

這是代碼:

package com.algorithms.sort;

public class MergeSort {

    public static int[] increasing(int[] arr) {
        int[] result = new int[arr.length];
        int q = arr.length / 2;
        System.out.println("q: " + q);
        int[] left = new int[q];
        int[] right = new int[q];
        for (int i = 0; i < q; i++) {
            left[i] = arr[i];
        }
        int k = 0;
        for (int j = q; j < arr.length; j++) {
            right[k] = arr[j];
            k += 1;
        }
        left = InsertionSort.increasing(left);
        right = InsertionSort.increasing(right);

        // Printing
        for (int e : left) {
            System.out.print(e);
        }
        System.out.println("\n");
        for (int e : right) {
            System.out.print(e);
        }
        System.out.println("\n");

        int i = 0;
        int j = 0;
        int s = 0;
        while ((i < left.length) && (j < right.length)) {
            if (left[i] <= right[j]) {
                result[s] = left[i];
                i++;
            } else {
                result[s] = right[j];
                j++;
            }
            s++;
        }
        while (i < left.length) {
            result[s] = left[i];
            i++;
            s++;
        }
        while (j < right.length) {
            result[s] = right[j];
            j++;
            s++;
        }
        return result;
    }

    /**
     * Main method to test an example integer array
     */
    public static void main(String[] args) {
        int[] ar = { 18, 12, 11, 6, 55, 100 };
        int[] res = increasing(ar);
        for (int a : res) {
            System.out.print(a + " ");
        }
    }
}

比優化更重要的是,你必須首先實現正確性。 increasing static方法有一個bug:如果數組參數的大小不偶數, right子數組分配的大小不正確: int[] right = new int[q]; 應該

int[] right = new int[arr.length - q];

此外,如果數組太小,則不應嘗試拆分數組。

關於優化,您應該只在子數組大小低於閾值(介於 16 到 128 個元素之間)時回退到InsertionSort() 使用不同的閾值和各種分布進行仔細的基准測試將有助於為您的系統確定一個好的閾值。

正如當前實施的那樣,您的 function 的時間復雜度為O(N 2 ) ,因為它在除最后一個合並階段之外的所有階段都遵循InsertionSort 要將復雜性降低到O(N.log(N)) ,您必須對子數組進行遞歸,直到它們的大小低於固定閾值。

這是修改后的版本:

package com.algorithms.sort;

public class MergeSort {

    public static int threshold = 32;

    public static int[] increasing(int[] arr) {
        if (arr.length <= threshold)
            return InsertionSort.increasing(arr);

        int len1 = arr.length / 2;
        int[] left = new int[len1];
        for (int i = 0; i < len1; i++) {
            left[i] = arr[i];
        }
        int len2 = arr.length - len1;
        int[] right = new int[len2];
        for (int i = 0; i < len2; i++) {
            right[i] = arr[i + len1];
        }
        left = increasing(left);
        right = increasing(right);

        int[] result = new int[len1 + len2];
        int i = 0;
        int j = 0;
        int s = 0;
        while (i < len1 && j < len2) {
            if (left[i] <= right[j]) {
                result[s] = left[i];
                i++;
            } else {
                result[s] = right[j];
                j++;
            }
            s++;
        }
        while (i < len1) {
            result[s] = left[i];
            i++;
            s++;
        }
        while (j < len2) {
            result[s] = right[j];
            j++;
            s++;
        }
        return result;
    }

    /**
     * Main method to test an example integer array
     */
    public static void main(String[] args) {
        int[] ar = { 18, 12, 11, 6, 55, 100 };
        int[] res = increasing(ar);
        for (int a : res) {
            System.out.print(a + " ");
        }
    }
}

兩者的時間復雜度都是 O(n log n)。

關於空間復雜度,實現可能因您的數據結構選擇而異。 在遞歸中,如果您選擇數組:空間復雜度:N log N 如果您選擇鏈表:空間復雜度為 O(1) 在迭代中:如果您選擇數組:空間復雜度:N ( 根據您的實現,它是 O ( N log N)因為您在每個划分 state 中創建一個新的子數組,要將其減少到 O(n),您應該使用一個額外的數組作為原始數組的大小和索引)如果您選擇鏈表:空間復雜度為 O (1)

如您所見,鏈表最適合排序。 除此之外,由於 function 框架創建,遞歸可能會比基於編程語言的預期消耗更多的 memory。

資源

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM