插入排序及其優(yōu)化

基本思路

正如生活中整理?yè)淇说姆椒ǎ阂粡堃粡埖膩?lái)贵白,將每一張撲克插入到其他已經(jīng)有序的撲克中的適當(dāng)位置。

Q:計(jì)算機(jī)中如何實(shí)現(xiàn)亮瓷?
A:為了給要插入的元素騰出空間欲芹,我們需要將其余所有元素在插入之前都向右移動(dòng)一位。(這里我們用交換代替移動(dòng))

Q:與選擇排序相同點(diǎn)昔瞧?
A:當(dāng)前索引左邊的所有元素都是有序的指蚁,但它們的最終位置還不確定,為了給更小的元素騰出空間自晰,它們可能會(huì)被移動(dòng)凝化。當(dāng)索引到達(dá)數(shù)組的右端時(shí),數(shù)組排序就完成了酬荞。

Q:與選擇排序不同點(diǎn)搓劫?
A:插入排序所需的時(shí)間取決于輸入中元素的初始順序。速度:元素已經(jīng)有序(或接近有序)的數(shù)組 > 隨機(jī)順序的數(shù)組

運(yùn)行軌跡

插入排序運(yùn)行軌跡

代碼實(shí)現(xiàn)

根據(jù)排序算法類的模板實(shí)現(xiàn)插入排序(提醒:點(diǎn)藍(lán)字查看詳情)

import java.util.Random;

/**
 * 插入排序
 *
 * @author TinyDolphin
 *         2017/11/1 14:20.
 */
public class Insertion {
    /**
     * 排序?qū)崿F(xiàn)
     *
     * @param arr 待排序數(shù)組
     */
    public static void sort(Comparable[] arr) {
        //排序代碼
        int length = arr.length;
        for (int indexI = 1; indexI < length; indexI++) {
            for (int indexJ = indexI; indexJ > 0 && less(arr[indexJ], arr[indexJ - 1]); indexJ--) {
                exch(arr, indexJ, indexJ - 1);
            }
        }
    }

    /**
     * 比較兩個(gè)元素的大小
     *
     * @param comparableA 待比較元素A
     * @param comparableB 待比較元素B
     * @return 若 A < B,返回 true,否則返回 false
     */
    private static boolean less(Comparable comparableA, Comparable comparableB) {
        return comparableA.compareTo(comparableB) < 0;
    }

    /**
     * 將兩個(gè)元素交換位置
     *
     * @param arr    待交換元素所在的數(shù)組
     * @param indexI 第一個(gè)元素索引
     * @param indexJ 第二個(gè)元素索引
     */
    private static void exch(Comparable[] arr, int indexI, int indexJ) {
        Comparable temp = arr[indexI];
        arr[indexI] = arr[indexJ];
        arr[indexJ] = temp;
    }

    /**
     * 打印數(shù)組的內(nèi)容
     *
     * @param arr 待打印的數(shù)組
     */
    private static void show(Comparable[] arr) {
        for (int index = 0; index < arr.length; index++) {
            System.out.print(arr[index] + " ");
        }
        System.out.println();
    }

    /**
     * 判斷數(shù)組是否有序
     *
     * @param arr 待判斷數(shù)組
     * @return 若數(shù)組有序混巧,返回 true糟把,否則返回 false
     */
    public static boolean isSort(Comparable[] arr) {
        for (int index = 1; index < arr.length; index++) {
            if (less(arr[index], arr[index - 1])) {
                return false;
            }
        }
        return true;
    }

    public static void main(String[] args) {
        Integer[] arr = new Integer[100000];
        for (int index = 0; index < 100000; index++) {
            arr[index] = new Random().nextInt(100000) + 1;
        }
        long start = System.currentTimeMillis();
        sort(arr);      //83001ms
        long end = System.currentTimeMillis();
        System.out.println("耗費(fèi)時(shí)間:" + (end - start));
        assert isSort(arr);
    }
}

性能分析

對(duì)于隨機(jī)排列的長(zhǎng)度為 N 且主鍵不重復(fù)的數(shù)組
平均情況下:~N2/4 次比較、~N2/4 次交換牲剃、O(N^2)
最壞的情況:~N2/2 次比較、~N2/2 次交換雄可、O(N^2)
最好的情況:N-1 次比較凿傅、0次交換(線性級(jí)別)(當(dāng)?shù)怪玫臄?shù)量很少時(shí)缠犀,比其他的排序算法都要快)(對(duì)于部分有序的很有效)、O(N)

Q:何為倒置聪舒?
A:數(shù)組中的兩個(gè)順序顛倒的元素辨液,如:3 2 5 1 4 中,有 5 對(duì)倒置:3-2箱残、3-1滔迈、2-1、5-1被辑、5-4燎悍。

Q:何為部分有序?
A: 數(shù)組中倒置的數(shù)量小于數(shù)組大小的某個(gè)倍數(shù)盼理。

插入排序需要的交換次數(shù) = 數(shù)組中倒置的數(shù)量
倒置數(shù)量 <= 需要的比較次數(shù) <= 倒置的數(shù)量 + 數(shù)組的大小 - 1

優(yōu)化方案

NO.1

原有方案:在內(nèi)循環(huán)中谈山,總是交換兩個(gè)元素。
優(yōu)化方案:進(jìn)行了預(yù)處理操作宏怔,并在內(nèi)循環(huán)中奏路,總是將較大的元素向右移動(dòng)。(這樣訪問(wèn)數(shù)組的次數(shù)就能減半)

優(yōu)化之后的運(yùn)行軌跡
優(yōu)化之后的運(yùn)行軌跡
優(yōu)化之后的插入排序
優(yōu)化之后的代碼
public static void sortPlus(Comparable[] arr) {
        int length = arr.length;
        int exchanges = 0; //交換次數(shù)
        // 預(yù)處理:若 arr[index] < arr[index - 1]臊诊,則交換兩數(shù)
        for (int index = length - 1; index > 0; index--) {
            if (less(arr[index], arr[index - 1])) {
                exch(arr, index, index - 1);
                exchanges++;
            }
        }
        // 若交換次數(shù)為0(即數(shù)組有序)鸽粉,則無(wú)需進(jìn)行下一步排序。
        if (exchanges == 0) return;
        // 若有交換次數(shù)抓艳,表明目前的數(shù)組無(wú)序触机。
        for (int indexI = 2; indexI < length; indexI++) {
            Comparable temp = arr[indexI];  //記錄一下 arr[indexI] 的值
            int indexJ = indexI;            // indexI 的代替品
            // 若 indexJ 的前一位元素小于 temp,則將小于 temp 的元素向右移動(dòng)一位
            while (less(temp, arr[indexJ - 1])) {
                arr[indexJ] = arr[indexJ - 1];
                indexJ--;
            }
            arr[indexJ] = temp; // 將記錄的值放在 indexJ 的位置上
        }
    }

NO.2

優(yōu)化方案:進(jìn)行了預(yù)處理操作壶硅,并查找插入位置時(shí)使用二分查找的方式

優(yōu)化之后的代碼
    public static void sortPlus2(Comparable[] arr) {
        int length = arr.length;
        int exchanges = 0; //交換次數(shù)
        //若 arr[index] < arr[index - 1]威兜,則交換兩數(shù)
        for (int index = length - 1; index > 0; index--) {
            if (less(arr[index], arr[index - 1])) {
                exch(arr, index, index - 1);
                exchanges++;
            }
        }
        //若交換次數(shù)為0(即數(shù)組有序),則無(wú)需進(jìn)行下一步排序庐椒。
        if (exchanges == 0) return;
        //若有交換次數(shù)椒舵,表明目前的數(shù)組無(wú)序。
        for (int indexI = 1; indexI < length; indexI++) {
            Comparable key = arr[indexI];  //記錄一下arr[indexI]的值
            int left = 0;
            int right = indexI - 1;
            // 二分查找尋找插入點(diǎn)
            while (left <= right) {
                // ①:a >> n 相當(dāng)于 a/2^n ②:此處有坑约谈,不要圖快用加法笔宿,會(huì)溢出。③棱诱、注意 >> 的優(yōu)先級(jí)別低于 + ,也就是說(shuō)先執(zhí)行 + 泼橘,在執(zhí)行 >>
                int middle = left + ((right - left) >> 1);
                if (less(key, arr[middle])) {
                    right = middle - 1;
                } else {
                    left = middle + 1;
                }
            }
            // 將插入點(diǎn)以后的所有元素,后移一位
            for (int indexJ = indexI - 1; indexJ >= left; indexJ--) {
                arr[indexJ + 1] = arr[indexJ];
            }
            // 插入元素到插入點(diǎn)
            arr[left] = key;
        }
    }
測(cè)試代碼

高效復(fù)制數(shù)組的方法】迈勋,提示:點(diǎn)擊藍(lán)色字體查看方法詳情炬灭。

    public static void main(String[] args) {
        int length = 100000;// 十萬(wàn)數(shù)量級(jí)別
        Integer[] arr = new Integer[length];
        Integer[] arr2 = new Integer[length];
        Integer[] arr3 = new Integer[length];
        for (int index = 0; index < length; index++) {
            arr[index] = new Random().nextInt(length) + 1;
        }
        //高效復(fù)制數(shù)組的方法
        System.arraycopy(arr, 0, arr2, 0, arr.length);
        System.arraycopy(arr, 0, arr3, 0, arr.length);

        long start = System.currentTimeMillis();
        sort(arr);
        long end = System.currentTimeMillis();
        System.out.println("耗費(fèi)時(shí)間:" + (end - start) + "ms");
        assert isSort(arr);

        start = System.currentTimeMillis();
        sortPlus(arr2);
        end = System.currentTimeMillis();
        System.out.println("耗費(fèi)時(shí)間:" + (end - start) + "ms");
        assert isSort(arr2);

        start = System.currentTimeMillis();
        sortPlus2(arr3);
        end = System.currentTimeMillis();
        System.out.println("耗費(fèi)時(shí)間:" + (end - start) + "ms");
        assert isSort(arr3);
    }
測(cè)試結(jié)果
十萬(wàn)數(shù)量級(jí)別測(cè)試結(jié)果
總結(jié)

NO.2 采用的優(yōu)化方案,在查找插入點(diǎn)的時(shí)候靡菇,加快了速度重归,相比于 NO.1 的方案米愿,減少了比較的次數(shù)。所以 NO.2 優(yōu)化是成功的鼻吮。

注意:編譯器默認(rèn)不適用 assert 檢測(cè)(但是junit測(cè)試中適用)育苟,所以要使用時(shí)要添加參數(shù)虛擬機(jī)啟動(dòng)參數(shù)-ea 具體添加過(guò)程,請(qǐng)參照eclipse 和 IDEA 設(shè)置虛擬機(jī)啟動(dòng)參數(shù)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末椎木,一起剝皮案震驚了整個(gè)濱河市违柏,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌香椎,老刑警劉巖漱竖,帶你破解...
    沈念sama閱讀 212,686評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異士鸥,居然都是意外死亡闲孤,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,668評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門(mén)烤礁,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)讼积,“玉大人,你說(shuō)我怎么就攤上這事脚仔∏谥冢” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 158,160評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵鲤脏,是天一觀的道長(zhǎng)们颜。 經(jīng)常有香客問(wèn)我,道長(zhǎng)猎醇,這世上最難降的妖魔是什么窥突? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,736評(píng)論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮硫嘶,結(jié)果婚禮上阻问,老公的妹妹穿的比我還像新娘。我一直安慰自己沦疾,他們只是感情好称近,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,847評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著哮塞,像睡著了一般刨秆。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上忆畅,一...
    開(kāi)封第一講書(shū)人閱讀 50,043評(píng)論 1 291
  • 那天衡未,我揣著相機(jī)與錄音,去河邊找鬼。 笑死缓醋,一個(gè)胖子當(dāng)著我的面吹牛剔交,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播改衩,決...
    沈念sama閱讀 39,129評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼驯镊!你這毒婦竟也來(lái)了葫督?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 37,872評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤板惑,失蹤者是張志新(化名)和其女友劉穎橄镜,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體冯乘,經(jīng)...
    沈念sama閱讀 44,318評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡洽胶,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,645評(píng)論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了裆馒。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片姊氓。...
    茶點(diǎn)故事閱讀 38,777評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖喷好,靈堂內(nèi)的尸體忽然破棺而出翔横,到底是詐尸還是另有隱情,我是刑警寧澤梗搅,帶...
    沈念sama閱讀 34,470評(píng)論 4 333
  • 正文 年R本政府宣布禾唁,位于F島的核電站,受9級(jí)特大地震影響无切,放射性物質(zhì)發(fā)生泄漏荡短。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,126評(píng)論 3 317
  • 文/蒙蒙 一哆键、第九天 我趴在偏房一處隱蔽的房頂上張望掘托。 院中可真熱鬧,春花似錦洼哎、人聲如沸烫映。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,861評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)锭沟。三九已至,卻和暖如春识补,著一層夾襖步出監(jiān)牢的瞬間族淮,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,095評(píng)論 1 267
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留祝辣,地道東北人贴妻。 一個(gè)月前我還...
    沈念sama閱讀 46,589評(píng)論 2 362
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像蝙斜,于是被迫代替她去往敵國(guó)和親名惩。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,687評(píng)論 2 351

推薦閱讀更多精彩內(nèi)容

  • 概述:排序有內(nèi)部排序和外部排序孕荠,內(nèi)部排序是數(shù)據(jù)記錄在內(nèi)存中進(jìn)行排序娩鹉,而外部排序是因排序的數(shù)據(jù)很大,一次不能容納全部...
    每天刷兩次牙閱讀 3,729評(píng)論 0 15
  • 概述 排序有內(nèi)部排序和外部排序稚伍,內(nèi)部排序是數(shù)據(jù)記錄在內(nèi)存中進(jìn)行排序弯予,而外部排序是因排序的數(shù)據(jù)很大,一次不能容納全部...
    蟻前閱讀 5,170評(píng)論 0 52
  • 算法簡(jiǎn)介 是一種分治的排序算法个曙,特點(diǎn)就是快锈嫩,而且效率高。 基本思路 通過(guò)一趟排序?qū)⒋旁胤指舫瑟?dú)立的兩部分垦搬,其中...
    TinyDolphin閱讀 3,431評(píng)論 0 3
  • 1.插入排序—直接插入排序(Straight Insertion Sort) 基本思想: 將一個(gè)記錄插入到已排序好...
    依依玖玥閱讀 1,245評(píng)論 0 2
  • 我想給他打個(gè)電話 我總覺(jué)得那天來(lái)的太快呼寸,去的也快。驟然忙碌的兩天悼沿,帶給我緩慢難以消散的痛苦記憶等舔。 可是,即使...
    七寸清歡閱讀 105評(píng)論 0 0