1. 程式人生 > >排序演算法—歸併排序的Java實現,效率比選擇排序要低?

排序演算法—歸併排序的Java實現,效率比選擇排序要低?

初級Java開發人員面試,經常會碰到技術面試官問排序演算法,幾乎每次面試一遍都要刷一遍排序演算法……,今天正在刷歸併排序,居然發現在100000級別的資料量中歸併演算法的執行時間居然比選擇排序要長,百思不得解,最後在比較多位大神blog中的程式碼實現,發現問題的根源,在此做一個記錄,也算是一個成長道路上的積累吧。

歸併排序演算法的實現步驟(網上貼的)
 * 把長度為n的輸入序列分成兩個長度為n/2的子序列;
 * 對這兩個子序列分別採用歸併排序;
 * 將兩個排序好的子序列合併成一個最終的排序序列。

選擇排序演算法實現:

    /**
     * 它的工作原理很容易理解: 1.初始時在序列中找到最小(大)元素,放到序列的起始位置作為已排序序列;
     * 2.然後,再從剩餘未排序元素中繼續尋找最小(大)元素,放到已排序序列的末尾; 3.以此類推,直到所有元素均排序完畢。
     *
     * @return
int[] * @Author Administrator * @Date 2018年3月7日 */
public static void selectSort(int arr[]) { long start = System.currentTimeMillis(); if (arr != null && arr.length > 1) { for (int i = 0; i < arr.length - 1; i++) { int min = i; for
(int j = i + 1; j < arr.length; j++) { if (arr[min] > arr[j]) { min = j; } } if (min != i) { swap(arr, i, min); } } } System.out.println("select sort 耗時 : "
+ (System.currentTimeMillis() - start) + "ms"); }

第一版歸併演算法實現:


    /**
     * 歸併排序
     * 把長度為n的輸入序列分成兩個長度為n/2的子序列;
     * 對這兩個子序列分別採用歸併排序;
     * 將兩個排序好的子序列合併成一個最終的排序序列。
     * @param arr
     */
    public static void mergeSort(int[] arr) {
        if (arr == null || arr.length < 1) {
            return;
        }
        long start = System.currentTimeMillis();
        doMergeSort(arr, 0, arr.length-1);
        System.out.println("merge sort 耗時 : " + (System.currentTimeMillis() - start) + "ms");
    }

    private static void doMergeSort(int[] arr, int start, int end) {
        if (start >=end) {
            return;
        }
        int mid = (start + end) >>1;

        doMergeSort(arr, start, mid);
        doMergeSort(arr, mid+1, end);
        merge(arr, start,mid, end);
    }

    /**
     *合併子序列
     * 
     * @param arr
     * @param start
     * @param end
     */
    private static void merge(int[] arr, int start,int mid, int end) {       *//注意此處,產生臨時資料表,儲存已經歸併了的子序列
        *int[] temp = new int[arr.length];*
        int tp=start,lp=start,rp=mid +1;
        while(lp<=mid&&rp<=end){
            if(arr[lp]<=arr[rp]){
                temp[tp++] =arr[lp++] ;
            }else{
                temp[tp++] =arr[rp++] ;
            }
        }
        while(lp<=mid){
            temp[tp++] =arr[lp++] ;
        }
        while(rp<=end){
            temp[tp++] =arr[rp++] ;
        }
        int cp =start;
        //從臨時陣列拷貝到原陣列
        while(cp<=end){
            arr[cp] = temp[cp];
            cp++;
        }
    }

執行結果比較:
資料量:10000
select sort 耗時 : 62ms
merge sort 耗時 : 112ms

歸併排序耗時近乎選擇排序耗時兩倍。

改良後的程式碼實現:


    /**
     * 歸併排序 把長度為n的輸入序列分成兩個長度為n/2的子序列;
     * 對這兩個子序列分別採用歸併排序;
     * 將兩個排序好的子序列合併成一個最終的排序序列。
     * 
     * @param arr
     */
    public static void mergeSort(int[] arr) {
        if (arr == null || arr.length < 1) {
            return;
        }
        long start = System.currentTimeMillis();
        int[] temp = new int[arr.length];
        doMergeSort(arr, 0, arr.length - 1,temp);
        System.out.println("merge sort 耗時 : " + (System.currentTimeMillis() - start) + "ms");
    }

    private static void doMergeSort(int[] arr, int start, int end,int[] temp) {
        if (start >= end) {
            return;
        }
        int mid = (start + end) >> 1;

        doMergeSort(arr, start, mid,temp);
        doMergeSort(arr, mid + 1, end,temp);
        merge(arr, start, mid, end,temp);
    }

    /**
     * 合併子序列
     * 
     * @param arr
     * @param start
     * @param end
     */
    private static void merge(int[] arr, int start, int mid, int end,int[] temp) {

        // 此處new temp降低排序效能
        int tp = start, lp = start, rp = mid + 1;
        while (lp <= mid && rp <= end) {
            if (arr[lp] <= arr[rp]) {
                temp[tp++] = arr[lp++];
            } else {
                temp[tp++] = arr[rp++];
            }
        }
        while (lp <= mid) {
            temp[tp++] = arr[lp++];
        }
        while (rp <= end) {
            temp[tp++] = arr[rp++];
        }
        int cp = start;
        // 從臨時陣列拷貝到原陣列
        while (cp <= end) {
            arr[cp] = temp[cp];
            cp++;
        }
    }

效率比較:
select sort 耗時 : 71ms
merge sort 耗時 : 6ms

效率得到飛一般的提升,那麼兩種實現的為何會相差這麼多呢?
我的分析是在資料量大的時候,執行merge方法的次數必然增多,每呼叫merge方法,執行一次int[] temp = new int[arr.length];產生一個臨時物件,降低了效能。改良後的實現,在外部呼叫merge時把臨時資料表當做引用傳入方法中,在整個排序過程中始終始終只需要一個額外的臨時空間,因此效率得到大幅度上升。
網上有些Blog貼出的第一版的實現,再次我引以為鑑,踩過坑才能不入坑!

相關推薦

排序演算法歸併排序Java實現效率選擇排序

初級Java開發人員面試,經常會碰到技術面試官問排序演算法,幾乎每次面試一遍都要刷一遍排序演算法……,今天正在刷歸併排序,居然發現在100000級別的資料量中歸併演算法的執行時間居然比選擇排序要長,百思不得解,最後在比較多位大神blog中的程式碼實現,發現問題的

PAT乙級——1035(插入排序歸併java實現

題目: 插入與歸併 (25 分) 根據維基百科的定義: 插入排序是迭代演算法,逐一獲得輸入資料,逐步產生有序的輸出序列。每步迭代中,演算法從輸入序列中取出一元素,將之插入有序序列中正確的位置。如此迭代直到全部元素有序。 歸併排序進行如下迭代操作:首先將原始序列看成

簡單選擇排序演算法原理及java實現(超詳細)

選擇排序是一種非常簡單的排序演算法,就是在序列中依次選擇最大(或者最小)的數,並將其放到待排序的數列的起始位置。 簡單選擇排序的原理 簡單選擇排序的原理非常簡單,即在待排序的數列中尋找最大(或者最小)的一個數,與第 1 個元素進行交換,接著在剩餘的待排序的數列中繼續找最大(最小)的一個數,與第 2 個元素交

這可能是最透徹的氣泡排序演算法解析(java實現

氣泡排序是一種思想簡單,便於理解和實現的排序演算法,也許是很多人學習的第一個排序演算法,廢話不多說,我們來實現它 演算法詳解 我們以升序排列為例,演算法的思想是,遍歷整個陣列,依次對陣列中的每兩個數進行比較大小,通過兩個數字的交換,達到將最大的元素移動到陣列的最

常用排序演算法總結(Java實現

排序演算法比較: 1. 氣泡排序 /** * 氣泡排序 * 比較相鄰的元素。如果第一個比第二個大,就交換他們兩個。 * 對每一對相鄰元素作同樣的工作,從開始第一對到結尾的最後一對。在這一點,最後的元素應該會是最大的數。 * 針對所有的元素重複以上的步驟,除了最後一個

插入排序演算法思路及Java實現

1.插入排序演算法思路 我們首先來看看插入排序的過程: 2.插入排序虛擬碼實現 3.插入排序java程式碼實現 public class InsertionSort { public static void main(String[] args) {

時間複雜度為O(N*logN)的常用排序演算法總結與Java實現

時間複雜度為O(N*logN)的常用排序演算法主要有四個——快速排序、歸併排序、堆排序、希爾排序1.快速排序·基本思想    隨機的在待排序陣列arr中選取一個元素作為標記記為arr[index](有時也直接選擇起始位置),然後在arr中從後至前以下標j尋找比arr[inde

八大排序演算法總結與Java實現

概述 因為健忘,加上對各種排序演算法理解不深刻,過段時間面對排序就蒙了。所以決定對我們常見的這幾種排序演算法進行統一總結,強行學習。首先羅列一下常見的十大排序演算法: 直接插入排序 希爾排序 簡單選擇排序 堆排序 氣泡排

九種排序演算法總結與Java實現

一、九種排序演算法總結 平均時間複雜度O(n^2): 氣泡排序、選擇排序、插入排序 平均時間複雜度O(nln):  快速排序、歸併排序、堆排序 時間複雜度介於O(nlgn)和O(n^2):希爾排序 時間複雜度O(n+k):計數排序 時間複雜度O(d(n+k)):基數排序

八種排序演算法總結(Java實現

        排序演算法有很多,在特定情景中使用哪一種演算法很重要。本文對幾種常見排序演算法做了簡單的總結。 一、氣泡排序         氣泡排序(BubbleSort)是一種簡單的排序演算法。它重複地走訪要排序的數列,一次比較兩個元素,如果他們的順序錯誤就把他們交

排序演算法思路以及Java實現

這幾天忙著找工作,看到有去阿里面試的同學遇到了堆排序的問題,因此就去網上看部落格學習,但看半天實在看不懂,只好把演算法導論拿出來啃,沒想到還挺簡單,所以在這裡分享給大家。 0.堆簡介 堆(二叉堆)可以視為一棵完全的二叉樹,完全二叉樹的一個“優秀”的性質是,除了最底層之外,

(排序演算法)linux c語言實現簡化版本的插入排序演算法(二分插入)

 二分插入演算法是在已經排序好的序列裡插入一個元素,是穩定的演算法,關鍵詞是折中。 比如說我要在12345678910裡插入一個3,那麼我先看看中間的數比3大,還是比3小,要是比3大,我就去後一半,如果是比3小,我就去前一半,現在進入某個一半後,再做如此操作,最後將其他的元

常見的三種排序演算法分析及對比實現(冒泡、選擇、插入)

1. 氣泡排序      1)基本思想:           在要排序的一組數中,對當前還未排好序的範圍內的全部數,自上而下對相鄰的兩個數依次進行比較和調整,           讓較大的數往下沉,較小的往上冒。即:每當兩相鄰的數比較後發現它們的排序與排序要求相反時,就

分散式系統應用中生成全域性唯一ID的演算法(snowflake)----java 實現單例模式

概述 在分散式系統中,有很多的地方需要生成全域性id的場景,比方說,訂單模組,使用者id等。這種情況下大多數的做法是通過UUID來做處理。首先,UUID是36位的一個字串,相對來說是比較長的,一般我們採用的資料庫會是MySQL,因為大多數的情況下,我們都希望我們的資料是可以

買什麼資料結構與演算法這裡有:動態圖解十大經典排序演算法(含JAVA程式碼實現

上篇的動圖資料結構反響不錯,這次來個動圖排序演算法大全。資料結構與演算法,齊了。 幾張動態圖捋清Java常用資料結構及其設計原理 本文將採取動態圖+文字描述+正確的java程式碼實現來講解以下十大排序演算法: 氣泡排序 選擇排序 插入排序 希爾排序

[排序演算法]--歸併排序Java實現

歸併排序(2-路歸併):歸併排序是建立在歸併操作上的一種有效的排序演算法。該演算法是採用分治法(Divide and Conquer)的一個非常典型的應用,歸併排序將兩個已排序的表合併成一個表。 下面先看一個歸併排序過程的示例: 待排序列(14,12

基礎排序演算法 java 實現(冒泡、選擇、插入、快排、歸併、堆排)

package demo; import java.util.Arrays; public class SortUtil { private static void printArr(int[] arr) { System.out.println

設計一個包含一個interface三個class的Java 程式用於完成陣列排序排序任務。其中interface中包含一個sort() 方法。第一個class使用氣泡排序實現interface的

題目:設計一個包含一個interface,三個class的Java 程式,用於完成陣列排序排序任務。其中interface中包含一個sort() 方法。第一個class使用氣泡排序法實現interface的sort()方法;第二個class使用選擇排序法實現interface的sort()方法;

常見排序演算法記錄(基於java語言實現)

桶排序 public class TongPaiXu1 { public static void main(String[] args) { // 桶排序 int[] m = {1, 3, 6, 3, 4,

【資料結構與演算法】之排序全家桶(十大排序詳解及其Java實現)---第七篇

本篇文章彙總了10種場常見的排序演算法,篇幅較長,可以通過下面的索引目錄進行定位查閱: 7、桶排序 一、排序的基本概念 1、排序的定義 排序:就是使一串記錄,按照其中的某個或者某些關鍵字的大小,遞增或遞減的排列起來