排序算法

穩(wěn)定的排序

算法 時間復雜度 空間復雜度
冒泡排序 最差、平均都是O(n^2),最好是O(n) 1
插入排序 最差、平均都是O(n^2)值桩,最好是O(n) 1
歸并排序 最差豪椿、平均和最好都是O(nlog n) O(n)

不穩(wěn)定的排序

算法 時間復雜度 空間復雜度
選擇排序 最差、平均都是O(n^2) 1
希爾排序 O(nlog n) 1
快速排序 平均是O(nlog n)搭盾,最壞情況下是O(n^2) O(log n)

冒泡排序

/**
 * 冒泡排序原理:從頭部開始,兩兩對比澜建,并交換調整更大(小)值在數(shù)組中的位置炕舵,
* 把未有序部分中最大(小)值移動到有序部分的頭部咽筋。 * 就像“冒泡”一樣徊件,把無序部
分中的最大(小)值庇忌,移動到有序部分舞箍。
 *
 * @author Affy
 * @date 2018-09-20
 */
public class BubbleSort {

    public static void bubbleSort(int[] source) {
        int unSortIndex = 0;
        int sortHeadIndex = source.length - 1;
        System.out.print("before sort: ");
        for (int i = 0; i < source.length; i++) {
            System.out.printf("%d ", source[i]);
        }

        boolean exchange = false;
        int times = 0;
        for (int high = sortHeadIndex; high > unSortIndex; high--) {
            exchange = false;
            times++;
            //兩兩,把更大的值往后“冒”皆疹,直到有序序列的頭部位置
            for (int low = 0; low < sortHeadIndex; low++) {
                if (source[low] > source[low + 1]) {
                    int temp = source[low];
                    source[low] = source[low + 1];
                    source[low + 1] = temp;
                    exchange = true;
                }
            }
            //如果存在某一次排序沒有發(fā)生交換疏橄,證明無序區(qū)中所有元素都有序,可以提前終止算法了
            if (!exchange) {
                break;
            }
        }

        System.out.print("\n第" + times + "趟完成排序" + "\nafter sort: ");
        for (int i = 0; i < source.length; i++) {
            System.out.printf("%d ", source[i]);
        }
    }

    public static void main(String[] args) {
        int[] source = {44, 5, 9, 7, 3, 10, 4, 6, 1, 7, 12, 51};
        bubbleSort(source);
    }
}

  1. 算法的最好時間復雜度
    若文件的初始狀態(tài)是有序的,則一次掃描就可完成排序捎迫,則比較次數(shù)C=n-1晃酒,移動次數(shù)M=0;也就是時間復雜度為O(n)窄绒。
  2. 算法的最壞時間復雜度
    若文件的初始狀態(tài)是逆序贝次,則需要進行n-1次排序。每次排序要進行n-i次關鍵字的比較(1<= i <= n-1),每次比較都必須移動記錄三次來達到交換記錄位置彰导。此時比較次數(shù)C=n(n-1)/2 = O(n^2 )蛔翅,交換次數(shù)M=3n(n-1)/2 = O( n^2 )。也就是時間復雜度為O(n^2)位谋。

選擇排序

/**
 * 每趟排序山析,從未排序序列中選擇最小(大)值掏父,放到有序序列的末尾
 *
 * @author Affy
 * @date 2018-09-21
 */
public class SelectSort {

    public static void selectSort(int[] source) {
        System.out.print("before sort: ");
        for (int i = 0; i < source.length; i++) {
            System.out.printf("%d ", source[i]);
        }

        for (int low = 0; low < source.length - 1; low++) {
            //每次遍歷笋轨,找到未排序隊列中的最小值,并標記其下標
            int minimumIndex = low;
            for (int high = low + 1; high < source.length; high++) {
                if (source[minimumIndex] > source[high]) {
                    minimumIndex = high;
                }
            }
            if (minimumIndex != low) {
                int temp = source[low];
                source[low] = source[minimumIndex];
                source[minimumIndex] = temp;
            }
        }

        System.out.print("\nafter sort: ");
        for (int i = 0; i < source.length; i++) {
            System.out.printf("%d ", source[i]);
        }
    }

    public static void main(String[] args) {
        int[] source = {44, 5, 9, 7, 3, 10, 4, 6, 1, 7, 12, 51};
        selectSort(source);
    }
}

選擇排序的交換操作介于0和(n-1)次之間赊淑;比較操作為n(n-1)/2次之間爵政;賦值操作介于0和3(n-1)次之間;平均復雜度為O(n^2)

插入排序

/**
 * <li>從第一個元素開始陶缺,該元素可以認為已經被排序</li>
 * <li>取出下一個元素钾挟,在有序序列中從后往前掃描</li>
 * <li>如果取出的新元素比當前元素小,則將當前元素移動到下一位置</li>
 * <li>重復上一步饱岸,直到新元素小于或等于前一個元素等龙,記錄該位置,并將新元素插入到該位置</li>
 *
 * @author Affy
 * @date 2018-09-21
 */
public class InsertSort {
    public static void insertSourt(int[] source) {
        System.out.print("before sort: ");
        for (int i = 0; i < source.length; i++) {
            System.out.printf("%d ", source[i]);
        }

        for (int low = 0; low < source.length - 1; low++) {
            //取出有序隊列末尾的后一個元素伶贰,從后往前遍歷。如果比當前元素小罐栈,則將當前元素移動一個位置(其實就是交換兩元素)
            for (int high = low + 1; high > 0 && source[high] < source[high - 1]; high--) {
                int temp = source[high];
                source[high] = source[high-1];
                source[high-1] = temp;
            }
        }

        System.out.print("\nafter sort: ");
        for (int i = 0; i < source.length; i++) {
            System.out.printf("%d ", source[i]);
        }
    }

    public static void main(String[] args) {
        int[] source = {44, 5, 9, 7, 3, 10, 4, 6, 1, 7, 12, 51};
        insertSourt(source);
    }
}

如果目標是升序排列,那么在最好和最壞的情況下:

  1. 最好:目標序列已經是升序荠诬。在這種情況下柑贞,只需要n-1次比較钧嘶。因此復雜度是O(n)。
  2. 最壞:目標序列是降序空盼。此時新荤,共需要進行n(n-1)/2次比較苛骨,賦值操作時比較次數(shù)加上n-1次痒芝。因此復雜度是O(n ^ 2)
    平均來說,插入排序算法復雜度為O(n^2)校哎。因此不適合對于數(shù)據(jù)量比較大的排序應用闷哆。但是抱怔,如果量級小于千時屈留,那么插入排序還是一個不錯的選擇测蘑。

希爾排序

shell排序分組

可參考:圖解排序之希爾排序

package com.affy.testapp;

/**
 * 希爾排序是把記錄按下標的一定增量分組勇蝙,對每組使用直接插入排序算法排序味混;隨著增量逐漸減少翁锡,
 * 每組包含的關鍵詞越來越多夕土,當增量減至1時,整個文件恰被分成一組荒适,算法便終止
 *
 * @author Affy
 * @date 2018-09-25
 */
public class ShellSort {
    public static void shellSort(int[] source, int gap) {
        System.out.print("\nbefore sort: ");
        for (int i = 0; i < source.length; i++) {
            System.out.printf("%d ", source[i]);
        }
        int high, low;
        //從第一個間隔開始刀诬,依次對source[gap]...source[n]進行處理陕壹,每次比較都取source[high-gap],這樣就能確保覆蓋到每個元素
        for (low = gap; low < source.length; low++) {
            for (high = low; high - gap >= 0 && source[high - gap] > source[high]; high -= gap) {
                int temp = source[high];
                source[high] = source[high - gap];
                source[high - gap] = temp;
            }   
        }

        System.out.print("\nafter sort: ");
        for (int i = 0; i < source.length; i++) {
            System.out.printf("%d ", source[i]);
        }
    }

    public static void main(String[] args) {
        int[] source = {44, 5, 9, 7, 3, 10, 4, 6, 1, 7, 12, 51};
        int increment = source.length;
        do {
            increment /= 2;
            shellSort(source, increment);
        } while (increment > 0);

    }
}

快速排序

1
2
3
4
5
6
7
8
總時序.jpg

以上圖片來源于網絡

/**
 * 快排采用了分治法的策略進行排序
 * <p>分治法:將原問題分解為若干規(guī)模更小但結構與原問題相似的子問題,
遞歸地解這些子問題毕匀,然后將這些子問題的解組合為原問題的解</p>
 * <p>
 * 快排基本思想:
 * <ol>
 * <li>分解癌别。選取基準展姐,使得基準左邊為小于或等于基準值,右邊大于或等于基準值</li>
 * <li>求解教馆。遞歸調用快排對左右子區(qū)間R[low,pivotPos-1]和R[pivotPos+1,high]進行排序</li>
 * <li>組合活玲。上一步做完之后,其實左右子區(qū)間已經有序镀钓,故無需做什么</li>
 * </ol>
 * </p>
 *
 * @author Affy
 * @date 2018-09-26
 */
public class QuickSort {
    private static void quickSort(int[] source, int low, int high) {
        int pivot;//基準位置的記錄

        if (low < high) {
            int i = low;
            int j = high;
            pivot = source[i];
            while (i < j) {
                //從高處往左遍歷丁溅,直到找到第一個關鍵字小于pivot的記錄source[j]
                while (j > i && source[j] >= pivot) {
                    j--;
                }

                //接著從i開始,從低處往右遍歷箱季,直到找到第一個關鍵字大于pivot的記錄source[i]藏雏。
                while (i < j && source[i] <= pivot) {
                    i++;
                }
                //此時可交換source[i]和source[j]
                if (i < j) {
                    source[i] = source[i] ^ source[j];
                    source[j] = source[i] ^ source[j];
                    source[i] = source[i] ^ source[j];
                }
            }
            //當i == j時掘殴,i就是需要的基準位置粟誓,它的左邊小于它鹰服,右邊大于它,交換source[low]和source[i]
            source[low] = source[i];
            source[i] = pivot;

            quickSort(source, low, i - 1);
            quickSort(source, i + 1, high);
        }
    }

    public static void main(String[] args) {
        int[] source = {44, 5, 9, 7, 3, 10, 4, 6, 1, 7, 12, 51};
        System.out.print("before sort: ");
        for (int i = 0; i < source.length; i++) {
            System.out.printf("%d ", source[i]);
        }
        quickSort(source, 0, source.length - 1);
        System.out.print("\nafter sort: ");
        for (int i = 0; i < source.length; i++) {
            System.out.printf("%d ", source[i]);
        }
    }
}

這里交換操作用了亦或操作的小技巧悲酷,可參考:https://blog.csdn.net/heathyhuhu/article/details/12744407

歸并排序

分治

合并

圖片出處

/**
 * 歸并排序笼踩。使用分割的方法將無序的序列分成一個一個已經排好序的子序列嚎于,
* 然后利用歸并的方法將一個個子序列合并成排好序的序列挟冠。 每次分割的時候知染,
* 首先分成兩份,然后再把2份分成4份嫌吠,一次分割直到分割成一個一個數(shù)據(jù)辫诅。
 *
 * @author Affy
 * @date 2018-09-26
 */
public class MergeSort {

    static void mergeSort(int[] source, int start, int end) {
        if (start < end) {
            int mid = (start + end) / 2;
            //分組炕矮,不斷遞歸直到子序列只包含一個元素
            mergeSort(source, start, mid);
            mergeSort(source, mid + 1, end);
            //合并
            merge(source, start, mid, mid + 1, end);
        }
    }

    static void merge(int[] source, int start1, int end1, int start2, int end2) {
        //新建和兩個需要合并的數(shù)組大小一樣的臨時數(shù)組存放合并結果
        int[] temp = new int[end2 - start1 + 1];
        int i = start1, j = start2, k = 0;
        //合并兩個數(shù)組,按照從小到大的順序排列
        while (i <= end1 && j <= end2) {
            if (source[i] < source[j]) {
                temp[k++] = source[i++];
            } else {
                temp[k++] = source[j++];
            }
        }

        //剩余元素存放
        while (i <= end1) {
            temp[k++] = source[i++];
        }
        while (j <= end2) {
            temp[k++] = source[j++];
        }

        //將臨時數(shù)組替換到原數(shù)組
        k = 0;
        for (i = start1; i <= end2; i++) {
            source[i] = temp[k++];
        }

    }

    public static void main(String[] args) {
        int[] source = {44, 5, 9, 7, 3, 10, 4, 6, 1, 7, 12, 51};
        System.out.print("before sort: ");
        for (int i = 0; i < source.length; i++) {
            System.out.printf("%d ", source[i]);
        }
        mergeSort(source, 0, source.length - 1);
        System.out.print("\nafter sort: ");
        for (int i = 0; i < source.length; i++) {
            System.out.printf("%d ", source[i]);
        }
    }
}

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市邢滑,隨后出現(xiàn)的幾起案子殊鞭,更是在濱河造成了極大的恐慌,老刑警劉巖锯仪,帶你破解...
    沈念sama閱讀 223,002評論 6 519
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件庶喜,死亡現(xiàn)場離奇詭異久窟,居然都是意外死亡斥扛,警方通過查閱死者的電腦和手機丹锹,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,357評論 3 400
  • 文/潘曉璐 我一進店門楣黍,熙熙樓的掌柜王于貴愁眉苦臉地迎上來租漂,“玉大人,你說我怎么就攤上這事秃踩°狙睿” “怎么了驾孔?”我有些...
    開封第一講書人閱讀 169,787評論 0 365
  • 文/不壞的土叔 我叫張陵翠勉,是天一觀的道長对碌。 經常有香客問我,道長怀读,這世上最難降的妖魔是什么菜枷? 我笑而不...
    開封第一講書人閱讀 60,237評論 1 300
  • 正文 為了忘掉前任啤誊,我火速辦了婚禮拥娄,結果婚禮上,老公的妹妹穿的比我還像新娘牡昆。我一直安慰自己丢烘,他們只是感情好凄硼,可當我...
    茶點故事閱讀 69,237評論 6 398
  • 文/花漫 我一把揭開白布狐史。 她就那樣靜靜地躺著说墨,像睡著了一般。 火紅的嫁衣襯著肌膚如雪姜贡。 梳的紋絲不亂的頭發(fā)上楼咳,一...
    開封第一講書人閱讀 52,821評論 1 314
  • 那天,我揣著相機與錄音余耽,去河邊找鬼苹熏。 笑死轨域,一個胖子當著我的面吹牛,可吹牛的內容都是我干的朱巨。 我是一名探鬼主播铐然,決...
    沈念sama閱讀 41,236評論 3 424
  • 文/蒼蘭香墨 我猛地睜開眼沥阳,長吁一口氣:“原來是場噩夢啊……” “哼自点!你這毒婦竟也來了?” 一聲冷哼從身側響起功炮,我...
    開封第一講書人閱讀 40,196評論 0 277
  • 序言:老撾萬榮一對情侶失蹤薪伏,失蹤者是張志新(化名)和其女友劉穎嫁怀,沒想到半個月后借浊,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經...
    沈念sama閱讀 46,716評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡存捺,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 38,794評論 3 343
  • 正文 我和宋清朗相戀三年岗钩,在試婚紗的時候發(fā)現(xiàn)自己被綠了肖油。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,928評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖疲恢,靈堂內的尸體忽然破棺而出瓷胧,到底是詐尸還是另有隱情,我是刑警寧澤杂数,帶...
    沈念sama閱讀 36,583評論 5 351
  • 正文 年R本政府宣布揍移,位于F島的核電站反肋,受9級特大地震影響,放射性物質發(fā)生泄漏罕邀。R本人自食惡果不足惜养距,卻給世界環(huán)境...
    茶點故事閱讀 42,264評論 3 336
  • 文/蒙蒙 一肾胯、第九天 我趴在偏房一處隱蔽的房頂上張望定铜。 院中可真熱鬧,春花似錦帘皿、人聲如沸畸陡。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,755評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至恶守,卻和暖如春兔港,著一層夾襖步出監(jiān)牢的瞬間仔拟,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,869評論 1 274
  • 我被黑心中介騙來泰國打工科侈, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留臀栈,地道東北人挠乳。 一個月前我還...
    沈念sama閱讀 49,378評論 3 379
  • 正文 我出身青樓欲侮,卻偏偏與公主長得像,于是被迫代替她去往敵國和親刁俭。 傳聞我的和親對象是個殘疾皇子韧涨,可洞房花燭夜當晚...
    茶點故事閱讀 45,937評論 2 361

推薦閱讀更多精彩內容