排序算法(直接插入排序、希爾排序、選擇排序、冒泡排序、快速排序)

1. 直接插入排序

假設有一組待排序數據  {4, 8, 2, 1, 45, 13, 2}。

算法思路:直接插入排序就是像整理撲克牌一樣,把待排序的元素插入到已經排好序的元素中。(每次拿出無序區間中的第一個數,插入到有序區間的合適的位置)

具體實現:

當插入第 i ( i>=1 ) 個元素時,前面的 array[0],arr[1],...  arr[i-1] 已經排好序,此時 arr[i] 與 arr[i-1],arr[i-2],... arr[0]順序進行比較,找到插入位置就將 arr[i] 插入,原來位置上的元素後移。

代碼:

public static void insertSort(int[] arr){
        int len = arr.length;
        for(int i=1; i<len; i++){
            int tmp = arr[i];//臨時變量存放arr[i]的值
            int j;
            for(j=i-1; j>=0; j--){
                if(tmp>=arr[j]){
                    //當前有序
                    break;
                }else {
                    //開始移動賦值
                    arr[j+1] = arr[j];
                }
            }
            arr[j+1] = tmp;
        }
}

分析: 時間複雜度 O(n^2),空間複雜度 O(1),穩定。

2. 希爾排序(是對插入排序的優化)

算法思路:希爾排序又叫縮小增量排序。是對插入排序的優化,是將待排序序列先分成 drr 組,再分別對每組進行直接插入排序。一輪之後,減小 drr ,繼續對每組進行直接插入排序,...  一直到 drr=1,進行最後一輪直接插入排序。這樣做的目的是爲了讓序列趨近於有序,加快排序。

至於這裏的 drr 的取法有多種,不同的取法算法的性能也會有所差異,這裏主要選取一種:

第1輪:drr = (len/3向下取整)+1;

第2輪:drr = (drr/3向下取整)+1;

. . .

一直到 drr = 1

具體實現:

代碼:

import java.util.Arrays;

public class ShellSort {
    public static void main(String[] args) {
        int[] arr = new int[]{5,4,9,2,1,7,1,3,2,10};
        shellSort(arr);
        System.out.println(Arrays.toString(arr));
    }
    public static void shellSort(int[] arr){
        int[] drr = new int[]{4,2,1};
        for(int i=0; i<drr.length; i++){
            shell(arr,drr[i]);
        }
    }
    public static void shell(int[] arr, int gap){
        for(int i=gap; i<arr.length; i++){
            int tmp = arr[i];
            int j = 0;
            for(j=i-gap; j>=0; j-=gap){
                if(tmp >= arr[j]){
                    break;
                }else {
                    arr[j+gap] = arr[j];
                }
            }
            arr[j+gap] = tmp;
        }
    }
}

分析: 最壞時間複雜度 O(n^2),最好時間複雜度 O(n^1.3),空間複雜度O(1),不穩定。

3. 選擇排序

算法思路:選擇排序是每一次從待排序元素中選出最小(或最大)的一個元素,存放在序列的起始位置。直到要排序的元素全部排完。

具體實現:

假設有一組待排序數據  {4, 8, 2, 1, 45, 13, 2}

定義變量 i 來遍歷序列,j = i+1;

如果 arr[j] < arr[i] 就交換位置,否則繼續 j++。

代碼:

public static void selectSort(int[] arr){
        for(int i=0; i<arr.length; i++){
            for(int j=i+1; j<arr.length; j++){
                if(arr[j] < arr[i]){
                    int tmp = arr[i];
                    arr[i] = arr[j];
                    arr[j] = tmp;
                }
            }
        }
}

分析:時間複雜度O(n^2),空間複雜度O(1),不穩定。

4. 冒泡排序

算法思路:對待排序元素相鄰的元素兩兩比較,如果順序錯誤,就進行交換。(把最大的數擠到了最後)

代碼:

public static void bubbleSort(int[] arr){
        for(int i=0; i<arr.length-1; i++){ //趟數
            boolean flag = false;
            for(int j=0; j<arr.length-1-i; j++){
                if(arr[j+1] < arr[j]){
                    int tmp = arr[j+1];
                    arr[j+1] = arr[j];
                    arr[j] = tmp;
                    flag = true;
                }
            }
            if(flag == false){
                break;
            }
        }
}

分析:時間複雜度O(n^2),優化後(加了flag標記)時間複雜度爲O(n),空間複雜度O(1),穩定。

5. 快速排序

算法思路:從待排序序列中任取一個元素作爲基準值,按基準值將待排序序列分割成兩個子序列。比基準值小的元素放到基準值左邊,比基準值大的元素放到基準值右邊。然後左右序列重複該過程,直到所有元素都排列在相應位置爲止。

具體實現:下面方法的基準值總是取序列的第一個元素。實際上,基準值的取法有多種,取法不同,算法的性能也會有差異。

代碼:

import java.util.Arrays;
import java.util.Random;
public class QuickSort {
    public static void main(String[] args) {
        int[] arr = new int[100];
        Random random = new Random();
        for(int i=0; i<arr.length; i++){
            arr[i] = random.nextInt()/10000000;
        }
        quickSort(arr,0,arr.length-1);
        System.out.println(Arrays.toString(arr));
    }
    public static void quickSort(int[] arr, int start, int end){
        int low = start;
        int high = end;
        int key = arr[low];
        while(low<high){
            while(low < high && arr[high] >= key){
                high--;
            }
            if(low >= high){
                break;
            }else {
                arr[low] = arr[high];
            }
            while(low < high && arr[low] <= key){
                low++;
            }
            if(low >= high){
                break;
            }else {
                arr[high] = arr[low];
            }
        }
        arr[low] = key;
        if(low>start+1) quickSort2(arr,start,low-1);//對左邊繼續排序
        if(high<end-1) quickSort2(arr,high+1,end);//對右邊繼續排序
    }
}

分析:時間複雜度O(N*logN),空間複雜度O(logN),不穩定。另外,採用快排,如果序列已經有序,此時的分割是一個非常不好的分割。因爲每次劃分只能使待排序序列減一,並沒有將序列分成兩部分。此時爲最壞情況,快排淪爲冒泡排序,時間複雜度爲O(n^2)。爲了避免這種情況,就要對快排進行優化,使得每次選擇的基準值能較爲均勻地把序列分成兩部分。下面有兩種方法:

方法1:隨機選取基準(取待排序序列中任意一個元素作爲基準)

這是一種相對安全的策略。由於基準值是隨機從序列中選取的,那麼產生的分割也不會總是出現劣質的分割。但當整個序列全相等時,仍然是最壞情況,時間複雜度是O(n^2)。但實際上,隨機化快速排序得到理論最壞情況的可能性很低,僅爲1/(2^n),所以隨機化快速排序可以對於絕大多數數據達到O(N*logN)的期望時間複雜度。

方法2:三數取中法(也就是取左端、中間、右端三個數,進行排序,將中間數作爲基準值)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章