1. 程式人生 > >Java排序演算法分析與實現:快排、氣泡排序、選擇排序、插入排序、歸併排序(一)

Java排序演算法分析與實現:快排、氣泡排序、選擇排序、插入排序、歸併排序(一)

轉載  https://www.cnblogs.com/bjh1117/p/8335628.html

 

一、概述:

  本文給出常見的幾種排序演算法的原理以及java實現,包括常見的簡單排序和高階排序演算法,以及其他常用的演算法知識。

  簡單排序:氣泡排序、選擇排序、插入排序(本篇部落格)

  高階排序:快速排序、歸併排序、希爾排序(下篇部落格)

  相關演算法知識:劃分、遞迴、二分查詢(下篇部落格)

二、氣泡排序:

(1)原理:

  1、從第一個資料開始,與第二個資料相比較,如果第二個資料小於第一個資料,則交換兩個資料的位置。

  2、指標由第一個資料移向第二個資料,第二個資料與第三個資料相比較,如果第三個資料小於第二個資料,則交換兩個資料的位置。

  3、依此類推,完成第一輪排序。第一輪排序結束後,最大的元素被移到了最右面。

  4、依照上面的過程進行第二輪排序,將第二大的排在倒數第二的位置。

  5、重複上述過程,沒排完一輪,比較次數就減少一次。

(2)例子:

待排序資料:7, 6, 9, 8, 5,1

第一輪排序過程:指標先指向7,7和6比較,6<7,交換6和7的位置,結果為:6,7,9,8,5,1

        指標指向第二個元素7,7和9比較,9>7,不用交換位置,結果仍為:6,7,9,8,5,1

        指標指向第三個元素9,比較9和8,8<9,交換8和9的位置,結果為:6,7,8,9,5,1

        指標指向第四個元素9,比較9和5,5<9,交換5和9,結果為:6,7,8,5,9,1

        指標指向第五個元素9,比較9和1,1<9,交換1和9的位置,結果為6,7,8,5,1,9

第一輪排序結束後,最大的數字9被移到了最右邊。

進行第二輪排序,過程同上,只是由於最大的9已經放在最右邊了,因此不用在比較9了,少了一次比較,第二輪結束的結果為:6,7,5,1,8,9

第三輪結果:6,5,1,7,8,9

第四輪比較結果:5,1,6,7,8,9

第五輪比較結果:1,5,6,7,8,9

最終排序結果為:1,5,6,7,8,9,由上可知N個數據排序,需要進行N-1輪排序;第i輪排序需要的比較次數為N-i次。

(3)編碼思路:

  需要兩層迴圈,第一層迴圈i表示排序的輪數,第二層迴圈j表示比較的次數。

(4)程式碼實現:

複製程式碼
package com.test.insertsort;
/**
 * 氣泡排序
 * @author bjh
 *
 */
public class BobSort {
    
    private int[] array;
    private int length;
    
    /**
     * 建構函式
     * @param array
     */
    public BobSort(int[] array){
        this.array = array;
        this.length = array.length;
    }
    
    /**
     * 列印陣列中資料
     */
    public void display(){
        for(int i : array){
            System.out.print(i+" ");
        }
        System.out.println();
    }
    
    /**
     * 氣泡排序
     */
    public void bobSort(){
        for(int i=0;i<length-1;i++){//排序輪數
            for(int j=0;j<length-1-i;j++){//比較次數
                if(array[j]>array[j+1]){
                    int temp = array[j+1];
                    array[j+1] = array[j];
                    array[j] = temp;
                }
            }
        }
    }
    
    /**
     * 測試方法
     * @param args
     */
    public static void main(String[] args){
        int[] array = {77,29,28,36,33,25,10};
        BobSort bobSort = new BobSort(array);
        System.out.println("排序前的資料為:");
        bobSort.display();
        bobSort.bobSort();
        System.out.println("排序後的資料為:");
        bobSort.display();
    }
}
複製程式碼

(5)氣泡排序演算法總結:

  N個元素需要排序N-1輪;

  第i輪需要比較N-i次;

  N個元素排序,需要比較n(n-1)/2次;

  氣泡排序的演算法複雜度較高,為O(n*n)

三、選擇排序

  選擇排序是對氣泡排序的改進,它的比較次數與氣泡排序相同,但交換次數要小於氣泡排序。當資料量較大時,效率會有很大的提升,但時間複雜度仍為O(n*n)

(1)原理:

  1、從第一個元素開始,分別與後面的元素向比較,找到最小的元素與第一個元素交換位置;

  2、從第二個元素開始,分別與後面的元素相比較,找到剩餘元素中最小的元素,與第二個元素交換;

  3、重複上述步驟,直到所有的元素都排成由小到大為止。

(2)例子:

  待比較資料:7, 6, 9, 8, 5,1

  第一輪:此時指標指向第一個元素7,找出所有資料中最小的元素,即1,交換7和1的位置,排序後的資料為:1,6,9,8,5,7

  第二輪:第一個元素已經為最小的元素,此時指標指向第二個元素6,找到6,9,8,5,7中最小的元素,即5,交換5和6的位置,排序後的結果為:1,5,9,8,6,7

  第三輪:前兩個元素為排好序的元素,此時指標指向第三個元素9,找到9,8,6,7中最小的元素,即6,交換6和9的位置,排序後的結果為:1,5,6,8,9,7

  第四輪:前三個元素為排好序的元素,此時指標指向第四個元素8,找到8,9,7中最小的元素,即7,交換8和7的位置,排序後的結果為:1,5,6,7,9,8

  第五輪:前四個元素為排好序的元素,此時指標指向第五個元素9,找到9,8中最小的元素,即8,交換9和8的位置,排序後的結果為:1,5,6,7,8,9

  到此,全部排序完成。

  分析:從上面的原理可以看出,與氣泡排序不同,選擇排序每排完一輪都把最小的元素移到了最左面,然後下一輪排序的比較次數比上一輪減少一次,即第i輪排序需要比較N-i次。因此,和氣泡排序一樣,N個數據比較大小,需要排序N-1輪,第i輪排序需要比較N-i次。

(3)編碼思路:

  需要兩次迴圈,第一層迴圈i表示每輪指標指向的位置,將最小值min初始化為第i個元素,第二層迴圈從j=i+1開始,分別與min比較,如果小於min,則更新min的值,內層迴圈結束後;交換min元素和第i個元素的位置。以此類推進行下一輪迴圈,直到i=length時停止迴圈。當i=length時,說明小的元素已經全部移到了左面,因此無需進行內層迴圈了。

(4)編碼實現:

複製程式碼
package com.test.insertsort;
/**
 * 選擇排序
 * @author Administrator
 *
 */
public class ChooseSort {
    private int[] array;
    private int length;
    
    public ChooseSort(int[] array){
        this.array = array;
        this.length = array.length;
    }
    
    /**
     * 列印陣列中的所有元素
     */
    public void display(){
        for(int i: array){
            System.out.print(i+" ");
        }
        System.out.println(); 
    }
    
    /**
     * 選擇排序演算法
     */
    public void chooseSort(){
        for(int i=0; i<length-1; i++){
            int minIndex = i;
            for(int j=minIndex+1;j<length;j++){
                if(array[j]<array[minIndex]){
                    minIndex = j;
                }
            }
            int temp = array[i];
            array[i] = array[minIndex];
            array[minIndex] = temp; 
        }
    }
    
    public static void main(String[] args){
        int[] array={100,45,36,21,17,13,7};
        ChooseSort cs = new ChooseSort(array);
        System.out.println("排序前的資料為:");
        cs.display();
        cs.chooseSort();
        System.out.println("排序後的資料為:");
        cs.display();
    }
}
複製程式碼

 (5)選擇排序總結:

  N個元素需要排序N-1輪;

  第i輪需要比較N-i次;

  N個元素排序,需要比較n(n-1)/2次;

  選擇排序的演算法複雜度仍為O(n*n);

  相比於氣泡排序,選擇排序的交換次數大大減少,因此速度要快於氣泡排序

 四、插入排序

  插入排序是簡單排序中最快的排序演算法,雖然時間複雜度仍然為O(n*n),但是卻比氣泡排序和選擇排序快很多。

(1)原理:

  1、將指標指向某個元素,假設該元素左側的元素全部有序,將該元素抽取出來,然後按照從右往左的順序分別與其左邊的元素比較,遇到比其大的元素便將元素右移,直到找到比該元素小的元素或者找到最左面發現其左側的元素都比它大,停止;

       2、此時會出現一個空位,將該元素放入到空位中,此時該元素左側的元素都比它小,右側的元素都比它大;

  3、指標向後移動一位,重複上述過程。每操作一輪,左側有序元素都增加一個,右側無序元素都減少一個。

(2)例子:
  待比較資料:7, 6, 9, 8, 5,1

  第一輪:指標指向第二個元素6,假設6左面的元素為有序的,將6抽離出來,形成7,_,9,8,5,1,從7開始,6和7比較,發現7>6。將7右移,形成_,7,9,8,5,1,6插入到7前面的空位,結果:6,7,9,8,5,1

  第二輪:指標指向第三個元素9,此時其左面的元素6,7為有序的,將9抽離出來,形成6,7,_,8,5,1,從7開始,依次與9比較,發現9左側的元素都比9小,於是無需移動,把9放到空位中,結果仍為:6,7,9,8,5,1

  第三輪:指標指向第四個元素8,此時其左面的元素6,7,9為有序的,將8抽離出來,形成6,7,9,_,5,1,從9開始,依次與8比較,發現8<9,將9向後移,形成6,7,_,9,5,1,8插入到空位中,結果為:6,7,8,9,5,1

  第四輪:指標指向第五個元素5,此時其左面的元素6,7,8,9為有序的,將5抽離出來,形成6,7,8,9,_,1,從9開始依次與5比較,發現5比其左側所有元素都小,5左側元素全部向右移動,形成_,6,7,8,9,1,將5放入空位,結果5,6,7,8,9,1。

  第五輪:同上,1被移到最左面,最後結果:1,5,6,7,8,9。

(3)編碼分析:

  需要兩層迴圈,第一層迴圈index表示上述例子中的指標,即遍歷從座標為1開始的每一個元素;第二層迴圈從leftindex=index-1開始,leftindex--向左遍歷,將每一個元素與i處的元素比較,直到j處的元素小於i出的元素或者leftindex<0;遍歷從i到j的每一個元素使其右移,最後將index處的元素放到leftindex處的空位處。

(4)程式碼實現:

複製程式碼
package com.test.insertsort;
/**
 * 插入排序演算法:
 * 1、以陣列的某一位作為分隔位,比如index=1,假設左面的都是有序的.
 * 
 * 2、將index位的資料拿出來,放到臨時變數裡,這時index位置就空出來了.
 * 
 * 3、從leftindex=index-1開始將左面的資料與當前index位的資料(即temp)進行比較,如果array[leftindex]>temp,
 * 則將array[leftindex]後移一位,即array[leftindex+1]=array[leftindex],此時leftindex就空出來了.
 * 
 * 4、再用index-2(即leftindex=leftindex-1)位的資料和temp比,重複步驟3,
 * 直到找到<=temp的資料或者比到了最左面(說明temp最小),停止比較,將temp放在當前空的位置上.
 * 
 * 5、index向後挪1,即index=index+1,temp=array[index],重複步驟2-4,直到index=array.length,排序結束,
 * 此時陣列中的資料即為從小到大的順序.
 * 
 * @author bjh
 *
 */
public class InsertSort {
    private int[] array;
    private int length;
    
    public InsertSort(int[] array){
        this.array = array;
        this.length = array.length;
    }
    
    public void display(){        
        for(int a: array){
            System.out.print(a+" ");
        }
        System.out.println();
    }
    
    /**
     * 插入排序方法
     */
    public void doInsertSort(){
        for(int index = 1; index<length; index++){//外層向右的index,即作為比較物件的資料的index
            int temp = array[index];//用作比較的資料
            int leftindex = index-1;
            while(leftindex>=0 && array[leftindex]>temp){//當比到最左邊或者遇到比temp小的資料時,結束迴圈
                array[leftindex+1] = array[leftindex];
                leftindex--;
            }
            array[leftindex+1] = temp;//把temp放到空位上
        }
    }
    
    public static void main(String[] args){
        int[] array = {38,65,97,76,13,27,49};
        InsertSort is = new InsertSort(array);
        System.out.println("排序前的資料為:");
        is.display();
        is.doInsertSort();
        System.out.println("排序後的資料為:");
        is.display();
    }
}
複製程式碼

(5)插入排序分析:

       時間複雜度,由於仍然需要兩層迴圈,插入排序的時間複雜度仍然為O(n*n)。
  比較次數:在第一輪排序中,插入排序最多比較一次;在第二輪排序中插入排序最多比較二次;以此類推,最後一輪排序時,最多比較N-1次,因此插入排序的最多比較次數為1+2+...+N-1=N*(N-1)/2。儘管如此,實際上插入排序很少會真的比較這麼多次,因為一旦發現左側有比目標元素小的元素,比較就停止了,因此,插入排序平均比較次數為N*(N-1)/4。

  移動次數:插入排序的移動次數與比較次數幾乎一致,但移動的速度要比交換的速度快得多。

  綜上,插入排序的速度約比氣泡排序快一倍(比較次數少一倍),比選擇排序還要快一些,對於基本有序的資料,插入排序的速度會很快,是簡單排序中效率最高的排序演算法。