2 排序基礎 - 5插入排序法

插入排序法 Insertion Sort

寫在Insertion Sort之前:

第一節我們已經學習了一種O(n^2)時間復雜度的排序算法——選擇排序法
我們來學習第二種O(n^2)時間復雜度的排序算法——插入排序。

Insertion Sort 思路:

我們大多數人玩撲克整理牌的思想大體就是插入排序的思想——就是看后面的每一張牌,然后插入到前面合適的位置,當我們對最后一張牌完成了此操作,我們手里的牌就排序完成了!

1.png

我們來看動畫演示:

我們要對這個數組排序:


數組.png

我們先看第1個元素(8):


第1個

對于8這個第1個元素,我們不動,因為我們只考慮8這個元素是,它只有一個人(已經排好序了)

下面我們來看6這個元素(即第2個數):


第2個數.png

我們要把6放到前面這個數組中合適的位置(此時我們只考慮前面2個元素,即8和6),此時6和8相比比前面的8小,所以交換位置:

交換6和8.png

交換后:


交換后.png

此時,前兩個元素就已經排好序了:


前兩個元素已排好.png

再來看第3個元素(2):


第3個元素.png

要把2插入到前面合適的位置:(此時我們只考慮前面3個元素,即6、8和2)
2分別和前面的相比:
2比8小,交換:


2和8交換.png
QQ圖片20170725190522.png

之后,2和6比,2比6小,交換:


QQ圖片20170725190821.png
QQ圖片20170725190844.png

前3個元素已經排好位置:


QQ圖片20170725190916.png

后面直接看圖吧:


QQ圖片20170725191026.png
QQ圖片20170725191050.png
QQ圖片20170725191134.png

最后3和2比,3比2大,3和2不交換;至此,前面4個已經排好序:


QQ圖片20170725191238.png

有興趣的可以已經推推后面的動作,也可以下載PPT看演示文稿。

下面來看C++代碼:

SortTestHelper.h:

#ifndef INC_04_INSERTION_SORT_SORTTESTHELPER_H
#define INC_04_INSERTION_SORT_SORTTESTHELPER_H

#include <iostream>
#include <algorithm>
#include <string>
#include <ctime>
#include <cassert>

using namespace std;


namespace SortTestHelper {

    // 生成有n個元素的隨機數組,每個元素的隨機范圍為[rangeL, rangeR]
    int *generateRandomArray(int n, int range_l, int range_r) {

        int *arr = new int[n];

        srand(time(NULL));
        for (int i = 0; i < n; i++)
            arr[i] = rand() % (range_r - range_l + 1) + range_l;
        return arr;
    }

    // 拷貝整型數組a中的所有元素到一個新的數組, 并返回新的數組
    int *copyIntArray(int a[], int n){

        int *arr = new int[n];
        //* 在VS中, copy函數被認為是不安全的, 請大家手動寫一遍for循環:)
        copy(a, a+n, arr);
        return arr;
    }

    // 打印arr數組的所有內容
    template<typename T>
    void printArray(T arr[], int n) {

        for (int i = 0; i < n; i++)
            cout << arr[i] << " ";
        cout << endl;

        return;
    }

    // 判斷arr數組是否有序
    template<typename T>
    bool isSorted(T arr[], int n) {

        for (int i = 0; i < n - 1; i++)
            if (arr[i] > arr[i + 1])
                return false;

        return true;
    }

    // 測試sort排序算法排序arr數組所得到結果的正確性和算法運行時間
    template<typename T>
    void testSort(const string &sortName, void (*sort)(T[], int), T arr[], int n) {

        clock_t startTime = clock();
        sort(arr, n);
        clock_t endTime = clock();
        cout << sortName << " : " << double(endTime - startTime) / CLOCKS_PER_SEC << " s"<<endl;

        assert(isSorted(arr, n));

        return;
    }

};

#endif //INC_04_INSERTION_SORT_SORTTESTHELPER_H

SelectionSort.h:

#ifndef INC_04_INSERTION_SORT_SELECTIONSORT_H
#define INC_04_INSERTION_SORT_SELECTIONSORT_H

#include <iostream>
#include <algorithm>

using namespace std;


template<typename T>
void selectionSort(T arr[], int n){

    for(int i = 0 ; i < n ; i ++){

        int minIndex = i;
        for( int j = i + 1 ; j < n ; j ++ )
            if( arr[j] < arr[minIndex] )
                minIndex = j;

        swap( arr[i] , arr[minIndex] );
    }
}

#endif //INC_04_INSERTION_SORT_SELECTIONSORT_H

main.cpp:

#include <iostream>
#include <algorithm>
#include "SortTestHelper.h"
#include "SelectionSort.h"

using namespace std;

template<typename T>
void insertionSort(T arr[], int n){

    for( int i = 1 ; i < n ; i ++ ) {

        // 尋找元素arr[i]合適的插入位置
        // 寫法1
//        for( int j = i ; j > 0 ; j-- )
//            if( arr[j] < arr[j-1] )
//                swap( arr[j] , arr[j-1] );
//            else
//                break;

        // 寫法2
        for( int j = i ; j > 0 && arr[j] < arr[j-1] ; j -- )
            swap( arr[j] , arr[j-1] );

    }

    return;
}

// 比較SelectionSort和InsertionSort兩種排序算法的性能效率
// 此時, 插入排序比選擇排序性能略低
int main() {

    int n = 20000;

    cout<<"Test for random array, size = "<<n<<", random range [0, "<<n<<"]"<<endl;
    int *arr1 = SortTestHelper::generateRandomArray(n,0,n);
    int *arr2 = SortTestHelper::copyIntArray(arr1, n);

    SortTestHelper::testSort("Insertion Sort", insertionSort,arr1,n);
    SortTestHelper::testSort("Selection Sort", selectionSort,arr2,n);

    delete[] arr1;
    delete[] arr2;

    cout<<endl;

    return 0;
}

對插入排序,第1個元素根本不用考慮,1個就已經算排好序了!

對于循環邊界控制,告誡大家不要偷懶,頭腦中要是考慮不清楚就馬上去用筆在紙上模擬模擬

注意:
插入排序和選擇排序最大的一個區別,就是:對于第二層循環,插入排序是可以提前結束的!

第二層循環提前結束.png

而對于選擇排序 不管整個數組是什么樣子的,為了找到沒一輪中最小的那個元素,必須從頭到尾把剩下的整個數組全掃面一遍,而沒有提前終止條件。

因為以上原因,插入排序理論上要比選擇排序效率好,但是:
C++代碼運行結果:

插入排序 選擇排序.png

結果顯示插入排序用時9秒多 選擇排序用時0.5秒
插入排序比選擇排序用時多!
具體原因即算法改進請關注:
2 排序基礎 - 5插入排序法的改進


Java代碼:

** InsertionSort.java: **

import java.util.*;

public class InsertionSort{

    // 我們的算法類不允許產生任何實例
    private InsertionSort(){}

    public static void sort(Comparable[] arr){

        int n = arr.length;
        for (int i = 0; i < n; i++) {

            // 尋找元素arr[i]合適的插入位置

            // 寫法1
//            for( int j = i ; j > 0 ; j -- )
//                if( arr[j].compareTo( arr[j-1] ) < 0 )
//                    swap( arr, j , j-1 );
//                else
//                    break;

            // 寫法2
            for( int j = i; j > 0 && arr[j].compareTo(arr[j-1]) < 0 ; j--)
                swap(arr, j, j-1);

        }
    }

    private static void swap(Object[] arr, int i, int j) {
        Object t = arr[i];
        arr[i] = arr[j];
        arr[j] = t;
    }

    // 測試InsertionSort
    public static void main(String[] args) {

        int N = 20000;
        Integer[] arr = SortTestHelper.generateRandomArray(N, 0, 100000);
        SortTestHelper.testSort("bobo.algo.InsertionSort", arr);

        return;
    }
}

** SelectionSort.java: **

import java.util.*;

public class SelectionSort{

    // 我們的算法類不允許產生任何實例
    private SelectionSort(){}

    public static void sort(Comparable[] arr){

        int n = arr.length;
        for( int i = 0 ; i < n ; i ++ ){
            // 尋找[i, n)區間里的最小值的索引
            int minIndex = i;
            for( int j = i + 1 ; j < n ; j ++ )
                // 使用compareTo方法比較兩個Comparable對象的大小
                if( arr[j].compareTo( arr[minIndex] ) < 0 )
                    minIndex = j;

            swap( arr , i , minIndex);
        }
    }

    private static void swap(Object[] arr, int i, int j) {
        Object t = arr[i];
        arr[i] = arr[j];
        arr[j] = t;
    }

    // 測試SelectionSort
    public static void main(String[] args) {

        int N = 20000;
        Integer[] arr = SortTestHelper.generateRandomArray(N, 0, 100000);
        SortTestHelper.testSort("bobo.algo.SelectionSort", arr);

        return;
    }
}

** SortTestHelper.java: **

import java.lang.reflect.Method;
import java.lang.Class;

public class SortTestHelper {

    // SortTestHelper不允許產生任何實例
    private SortTestHelper(){}

    // 生成有n個元素的隨機數組,每個元素的隨機范圍為[rangeL, rangeR]
    public static Integer[] generateRandomArray(int n, int rangeL, int rangeR) {

        assert rangeL <= rangeR;

        Integer[] arr = new Integer[n];

        for (int i = 0; i < n; i++)
            arr[i] = new Integer((int)(Math.random() * (rangeR - rangeL + 1) + rangeL));
        return arr;
    }

    // 打印arr數組的所有內容
    public static void printArray(Object[] arr) {

        for (int i = 0; i < arr.length; i++){
            System.out.print( arr[i] );
            System.out.print( ' ' );
        }
        System.out.println();

        return;
    }

    // 判斷arr數組是否有序
    public static boolean isSorted(Comparable[] arr){

        for( int i = 0 ; i < arr.length - 1 ; i ++ )
            if( arr[i].compareTo(arr[i+1]) > 0 )
                return false;
        return true;
    }

    // 測試sortClassName所對應的排序算法排序arr數組所得到結果的正確性和算法運行時間
    public static void testSort(String sortClassName, Comparable[] arr){

        // 通過Java的反射機制,通過排序的類名,運行排序函數
        try{
            // 通過sortClassName獲得排序函數的Class對象
            Class sortClass = Class.forName(sortClassName);
            // 通過排序函數的Class對象獲得排序方法
            Method sortMethod = sortClass.getMethod("sort",new Class[]{Comparable[].class});
            // 排序參數只有一個,是可比較數組arr
            Object[] params = new Object[]{arr};

            long startTime = System.currentTimeMillis();
            // 調用排序函數
            sortMethod.invoke(null,params);
            long endTime = System.currentTimeMillis();

            assert isSorted( arr );

            System.out.println( sortClass.getSimpleName()+ " : " + (endTime-startTime) + "ms" );
        }
        catch(Exception e){
            e.printStackTrace();
        }
    }
}

** Main.java: **

import java.util.Arrays;

public class Main {

    // 比較SelectionSort和InsertionSort兩種排序算法的性能效率
    // 此時,插入排序比選擇排序性能略低
    public static void main(String[] args) {

        int N = 20000;
        System.out.println("Test for random array, size = " + N + " , random range [0, " + N + "]");

        Integer[] arr1 = SortTestHelper.generateRandomArray(N, 0, N);
        Integer[] arr2 = Arrays.copyOf(arr1, arr1.length);

        SortTestHelper.testSort("bobo.algo.SelectionSort", arr1);
        SortTestHelper.testSort("bobo.algo.InsertionSort", arr2);

        return;
    }
}
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • We believe that writing is about content, about what you ...
    hongXkeX閱讀 352評論 0 0
  • 概述排序有內部排序和外部排序,內部排序是數據記錄在內存中進行排序,而外部排序是因排序的數據很大,一次不能容納全部的...
    Luc_閱讀 2,300評論 0 35
  • 概述:排序有內部排序和外部排序,內部排序是數據記錄在內存中進行排序,而外部排序是因排序的數據很大,一次不能容納全部...
    每天刷兩次牙閱讀 3,746評論 0 15
  • 概述 排序有內部排序和外部排序,內部排序是數據記錄在內存中進行排序,而外部排序是因排序的數據很大,一次不能容納全部...
    蟻前閱讀 5,222評論 0 52
  • 小區名稱:金科王府 建筑面積:145平米 戶型:三室兩廳一廚兩衛 工程預算:10萬元 主...
    裝修設計熱點閱讀 352評論 0 1