1. 程式人生 > >演算法之八大排序

演算法之八大排序

演算法之八大排序

氣泡排序
1.排序思想

從無序序列頭部開始,進行兩兩比較,根據大小交換位置,直到最後將最大的資料元素交換帶無序佇列隊尾, 下一次繼續這個過程。

2.時間複雜度

最好情況: O(n) 給出的資料有序
最壞情況: O(n^2) 無序

3.空間複雜度

O(1)

4.穩定性

穩定

5.用畫圖的方式結合文字進行總結

在這裡插入圖片描述

6.程式碼實現與分析

public static void bubbleSort(int[] array) {
    boolean swap = false; //表示是否發生交換
    for (int i = 0; i < array.length - 1; ++i) { //外層迴圈,次數
        for (int j = 0; j < array.length - 1 - i; ++j) {//內層迴圈,每一次的比較
            if (array[j] > array[j + 1]) {
                int tmp = array[j];  //將前面大數字和後面小數字進行交換
                array[j] = array[j + 1];
                array[j + 1] = tmp;
                swap = true; //如果交換 swap就換成true
            }
        }
        if (!swap) {
            break;
        }
    }
}

選擇排序

1.排序的思想

在執行第i趟操作作時候,從第i條記錄後選擇一條最小的記錄和第i條進行比較交換

2.時間複雜度

O(n^2)

3.空間複雜度

O(1)

4.穩定性:

不穩定

5.用畫圖的方式結合文字進行總結

在這裡插入圖片描述

  1. 程式碼實現與分析
public static void selectSort(int[] array){
    int tmp = 0;
    for (int i = 0; i<array.length;++i){//遍歷陣列
        for (int j = i+1;j<array.length;++j){ //每次載遍歷i後面的 數字
            if (array[i]>array[j]){//每次將i與j進行比較然後交換
                tmp = array[i];
                array[i] = array[j];
                array[j] = tmp;
            }
        }
    }
}

直接插入排序

1.排序的思想

每一次將一個待排序的元素,按其數字的大小插入到前面已經排好序的一組元素的合適位置上去,直到元

素全部插完為止。(分組排序)

2.時間複雜度

最優情況下: O(n) 給出的資料有序
最差情況下: O(n^2) 無序

3.空間複雜度

O(1)

4.穩定性

穩定
  1. 優化

希爾排序從某種意義上(分組)是對直接插入排序的優化

用畫圖的方式結合文字進行總結
在這裡插入圖片描述

7.程式碼實現與分析

public static void insertSort(int[] array){
    int tmp = 0;
    int j = 0;
    for (int i = 1;i<array.length;++i) {//因為第一個數字不需要排序 所以i=1
        tmp = array[i];
        for ( j = i-1; j >= 0; --j) { //遍歷待排數字後面的數字 然後進行比較交換
            if (array[j] > tmp) {  //將大的數字往後挪
                array[j+1] = array[j];
            }else{
                break; //找到比待排數字小的數字就跳出迴圈,前面已經有序了
            }
        }
        array[j+1] = tmp;
    }
}

Shell排序

1.排序思想

待排序列有n個元素,先去一個小於n的整數h1作為第一個增量,把待排序列以間隔h1分成若干子序列,子序列內使用插入排序;然後取第二個增量h2,(<h1),重複上述的劃分和排序,直到索取的增量h1
= 1(h1 > h2 > ……>hi)。

2.時間複雜度

Shell排序是一種不穩定的排序演算法,文獻表明其時間複雜度受增量序列的影響明顯大於其他因素,最環的情況是O(n2),好的情況在O(n1.3),與增量序列選擇有關。

3.空間複雜度

O(1)

4.穩定性

不穩定

5.用畫圖的方式結合文字進行總結

在這裡插入圖片描述

6.程式碼實現與分析

public static void shell(int[] array,int gap){
    for (int i = gap;i<array.length;++i){ //以gap為間隔 分成若干進行遍歷
        int tmp = array[i];
        int j = 0;
        for (j = i-gap;j>=0;j -= gap){  //記錄後移,查詢插入的位置
            if (array[j] >tmp){
                array[j+gap] = array[j];
            }else{
                break;
            }
        }
        array[j+gap] = tmp;  
    }

}
public static void shellSort(int[] array){
   int[] drr = {5,3,1};  //建立一個數組,為排序只作為間隔的值
   for (int i = 0;i<drr.length;++i){
       shell(array,drr[i]);
   }
}


快速排序

1.快速排序的思想

先從數列中取出一個數作為基準數;分割槽過程,將比這個數大的數全放到它的右邊,小於或等於它的數全放到它的左邊;再對左右區間重複第二步,直到各區間只有一個數。

2.時間複雜度

最好情況:O(nlog2n)

最快情況:O(n2)

3空間複雜度

O(log2n)

4.穩定性

不穩定

5.優化

第一種:當資料量少的時候有插入排序 ;第二種:聚焦相同的基準元素法

6.用畫圖的方式結合文字進行總結

在這裡插入圖片描述

7.程式碼實現與分析

import java.util.Arrays;
import java.util.Random;

/**
 * Created with IntelliJ IDEA.
 * Description:快排的三種排序方式以及兩種優化方式
 * 排序方式:1.固定位置選取基準法 2.隨機選取基準法  3.三分取中法
 * 優化方式:1、當資料量少的時候用插入排序   2.聚集相同基準元素法
 * User: GAOBO
 * Date: 2018-09-16
 * Time: 17:27
 */
public class src2 {
    public static int partion(int[] array,int low,int high) {
        int tmp = array[low];

        while (low < high) {

            while(low < high && array[high] >= tmp) {
                high--;
            }
            if(low >= high) {
                break;//array[low] = tmp;
            } else {
                array[low] = array[high];
            }

            while(low < high && array[low] <= tmp) {
                low++;
            }
            if(low >= high) {
                break;
            } else {
                array[high] = array[low];
            }
        }
        array[low] = tmp;//par
        return low;
    }

    public static void swap(int[] array,int low,int high) {
        int tmp = array[low];
        array[low] = array[high];
        array[high] = tmp;
    }
    // 排序方法:3.三分取中法
    public static void medianOfThree(int[] array,int low,int high) {

        int mid = (high+low)>>1;
        //array[mid] <= array[low] <= array[high]
        if(array[low] > array[high]) {//array[low] <= array[high]
            swap(array,low,high);
        }
        if(array[low] < array[mid]) {//array[mid] <= array[low]
            swap(array,low,mid);
        }
        if(array[mid] > array[high]) {//array[mid] <= array[high]
            swap(array,mid,high);
        }
    }

    //優化:1、當資料量少的時候用插入排序
    public static void insertSort(int[] array,int low,int high) {
        int tmp = 0;
        for(int i = low+1;i < high;i++) {
            tmp = array[i];
            int j = 0;
            for(j = i-1;j >= low;j--) {
                if(tmp < array[j]) {
                    array[j+1] = array[j];
                } else {
                    break;
                }
            }
            array[j+1] = tmp;
        }
    }

    //優化:2.聚集相同基準元素法
    public static int[] focusParNum(int[] array,int low,int high,int par,
                                    int left,int right) {

        int[] brr = new int[2];
        int parR = par+1;
        int parL = par-1;

        for(int i = par-1;i >= low;i--) {
            if(array[i] == array[par]) {
                if(i != parL) {
                    swap(array,i,parL);
                    parL--;
                } else {
                    parL--;
                }
            }
        }
        left = parL;
        for(int i = par+1;i <= high;i++) {
            if(array[i] == array[par]) {
                if(i != parR) {
                    swap(array,i,parR);
                    parR++;
                } else {
                    parR++;
                }
            }
        }
        right = parR;
        brr[0] = left;
        brr[1] = right;
        return brr;
    }


    public static void quick(int[] array,int low,int high) {

        //優化方式1:資料量少的時候 用直接插入排序
        /*if(high - low < 100) {
            //直接插入排序
            System.out.println("insert comeing");
            insertSort(array,low,high);
        }*/

        /*
        1、無限趨近於一個終止條件
        2、迴圈呼叫自己本身
         */
      /*  //方式2、隨機選取基準法
         Random random = new Random();
        //low --- high  2     7  7-2 = 5 === [0,5)
        //6   8   2===>[0,2)===0,1+6
        int rand = random.nextInt((high-low)+1+low);
        //low   rand
        swap(array,low,rand);
        //方式3、三分取中法*/
        medianOfThree(array,low,high);

        int par = partion(array,low,high);//一次劃分函式,第一個par//O(n)

        //優化方式2:聚集相同基準元素法
        int left = par-1;
        int right = par+1;
        int[] brr = focusParNum(array,low,high,par,left,right);
        left = brr[0];
        right = brr[1];
        //保證一個前提:必須有兩個資料以上
        if(par > low+1) {//log2n
            quick(array,low,left);
        }

        if(par < high-1) {
            quick(array,right,high);
        }

    }

    public static void quickSort(int[] array) {
        quick(array,0,array.length-1);
    }

    public static String getColumnLable(int n) {

        String tmp = "";
        // 按位找
        int count = 1;
        while (((int) (n / Math.pow(26, count-1)) >  0)) {
            int w3 = ((int) (n % Math.pow(26, count)) / (int) Math.pow(26, count - 1));
            char c = (char) (w3 + 64);
            tmp = c + tmp;
            count++;
        }
        return tmp;
    }

堆排序

1.堆排序的基本思想

堆排序的原理就是這樣,先構造出來大根堆(假設從小到大排序),然後取出堆頂元素(也就是最大的元素),放到陣列的最後面,然後再將剩餘的元素構造大根堆,再取出堆頂元素放到陣列倒數第二個位置,依次類推,知道所有的元素都放到陣列中,排序就完成了

2.時間複雜度

O(nlog2n)

3.空間複雜度

O(1)

4.穩定性

不穩定

5.用畫圖的方式結合文字進行總結

在這裡插入圖片描述

6.程式碼實現與分析

public static void adjust(int[] array,int start,int end) {
    int tmp = array[start];
    for(int i = 2*start+1; i <= end;i = i*2+1) {
        //1、是否有右孩子,如果有i表示的就是大的數字的下標
        if((i < end-1) && array[i] < array[i+1]) {
            i++;
        }
        if(array[i] > tmp) {
            array[start] = array[i];
            start = i;
        }
        if(array[i] <= tmp) {
            break;
        }
    }
    array[start] = tmp;
}

public static void heapSort(int[] array) {
    //1、調整大根堆
    for(int i = (array.length-1-1)/2;i >= 0;i--) {
        adjust(array,i,array.length);
    }
    //1.交換  2.調整
    for(int j = 0;j < array.length-1;j++) {
        int tmp = array[0];
        array[0] = array[array.length-1-j];
        array[array.length-1-j] = tmp;
        //-1:上面交換過後的節點不計算
        adjust(array,0,array.length-1-j-1);
    }
}


歸併排序

1.歸併排序的基本思想:

歸併排序就是利用歸併思想對數列進行排序。根據具體的實現,歸併排序包括"從上往下"和"從下往上"2種方式。

第一是從下往上的歸併排序:將待排序的數列分成若干個長度為1的子數列,然後將這些數列兩兩合併;得到若干個長度為2的有序數列,再將這些數列兩兩合併;得到若干個長度為4的有序數列,再將它們兩兩合併;直接合併成一個數列為止。這樣就得到了我們想要的排序結果。第二是從上往下的歸併排序:它與"從下往上"在排序上是反方向的。它基本包括3步:
① 分解 – 將當前區間一分為二,即求分裂點 mid = (low + high)/2; ② 求解 –
遞迴地對兩個子區間a[low…mid] 和 a[mid+1…high]進行歸併排序。遞迴的終結條件是子區間長度為1。 ③ 合併 –
將已排序的兩個子區間a[low…mid]和 a[mid+1…high]歸併為一個有序的區間a[low…high]。

2.時間複雜度

O(nolg2n)

3.空間複雜度

O(n)

4.穩定性

穩定

5.用畫圖的方式結合文字進行總結
在這裡插入圖片描述

6.程式碼實現與分析

public static void mergeSort(int[] array){
    for (int i = 1;i<array.length;i *= 2){
        merge(array,i);
    }
}

private static void merge(int[] array, int gap) {
    int[] tmp = new int[array.length];
    int i = 0;//表示陣列tmp的下標

    //確定s1,s2,e1,e2
    int start1 = 0;
    int end1 = start1 + gap-1;
    int start2 = end1+1;
    int end2 = start2 + gap-1 < array.length-1 ? start2 + gap-1 : array.length-1;

    //判斷是否有兩個歸併段
    while (start2 < array.length){

        while (start1 <= end1 && start2 <= end2){
            if (array[start1] < array[start2]){
                tmp[i++] = array[start1++];
            }else{
                tmp[i++] = array[start2++];
            }
        }

        //1.退出上面迴圈兩種條件
        while (start2 <= end2) {
            tmp[i++] = array[start2++];
        }

        //2.start2 > end2
        while (start1 <= end1) {
            tmp[i++] = array[start1++];
        }
        //重新對start1  end1 start2  end2 賦值
            start1 = end2+1;
            end1 = start1 + gap-1;
            start2 = end1+1;
            end2 = start2 + gap-1 < array.length-1 ? start2 + gap-1 : array.length-1;
    }
    while (start1 < array.length){
        tmp[i++] = array[start1++];
    }
    System.arraycopy(tmp,0,array,0,array.length);
}



基數排序
1.基數排序基本思想:

基數排序是這樣一種排序演算法,我們可以從低位(個位)開始,根據個位數排序一次,然後根據十位數排序,再根據百位數進行排序……最終完成整個陣列的排序。
對於十進位制數字而言,每一位只會是 0~9 這十個數字,我們通常使用桶排序(計數排序)來完成每一位數的排序。桶排序是一種穩定的排序演算法,基數排序的正確性依賴一種穩定的排序演算法。
基數排序其實是分 LSD(從低位向高位排序) 和 MSD(從高位向低位排序) 兩種。

2.時間複雜度:

基數排序的時間複雜度為 O(n)。
基數排序使用桶排序對其每一位進行排序,即每一位的排序時間複雜度為 O(n),假設最大的數有 digit 位,則共需要進行 digit * O(n) 次排序。時間複雜度依舊為 O(n)。

3.畫圖示意:
在這裡插入圖片描述