排序算法的比較與java實現

冒泡排序

基本思想:

比較相鄰的元素。如果第一個比第二個大,就交換他們兩個。

對每一對相鄰元素作同樣的工作,從開始第一對到結尾的最后一對。在這一點,最后的元素應該會是最大的數。 針對所有的元素重復以上的步驟,除了最后一個。持續每次對越來越少的元素重復上面的步驟,直到沒有任何一對數字需要比較。

Java實現
   
加入標記狀態 flag 若在一次冒泡中,沒有交換 則說明可以停止 減少運行時

public static void bubbleSort(int[] numbers) {
    int temp = 0;
    int size = numbers.length;
    boolean flag = true;
    for (int i = 0; i < size - 1&&flag; i++) {
        flag = false;
        for (int j = 0; j < size - 1 - i; j++) {
            if (numbers[j] > numbers[j + 1]) // 交換兩數位置
            {
                temp = numbers[j];
                numbers[j] = numbers[j + 1];
                numbers[j + 1] = temp;
                flag = true;
            }
        }
    }
}

時間復雜度O(n*n)

選擇排序算法

基本思想:

在要排序的一組數中,選出最小的一個數與第一個位置的數交換;然后在剩下的數當中再找最小的與第二個位置的數交換,如此循環到倒數第二個數和最后一個數比較為止。

Java 實現

public static void selectSort(int[] numbers) {
    int size = numbers.length; // 數組長度
    int temp = 0; // 中間變量
    for (int i = 0; i < size-1; i++) {
        int k = i; // 待確定的位置
        // 選擇出應該在第i個位置的數
        for (int j = size - 1; j > i; j--) {
            if (numbers[j] < numbers[k]) {
                k = j;
            }
        }
        // 交換兩個數
        temp = numbers[i];
        numbers[i] = numbers[k];
        numbers[k] = temp;
    }
}

時間復雜度O(n*n) 性能上優于冒泡排序 交換次數少

插入排序算法

基本思想:

每步將一個待排序的記錄,按其順序碼大小插入到前面已經排序的字序列的合適位置(從后向前找到合適位置后),直到全部插入排序完為止。

java 實現

public static void insertSort(int[] numbers) {
    int size = numbers.length;
    int temp = 0;
    int j = 0;
    for (int i = 1; i < size; i++) {
        temp = numbers[i];
        // 假如temp比前面的值小,則將前面的值后移
        for (j = i; j > 0 && temp < numbers[j - 1]; j--) {
            numbers[j] = numbers[j - 1];
        }
        numbers[j] = temp;
    }
}

時間復雜度
O(n*n) 性能上優于冒泡排序和選擇排序

希爾排序算法

基本思想:

先將整個待排序的記錄序列分割成為若干子序列分別進行直接插入排序,待整個序列中的記錄“基本有序”時,再對全體記錄進行依次直接插入排序。

java 實現

/**
 * 希爾排序的原理:根據需求,如果你想要結果從小到大排列,它會首先將數組進行分組,然后將較小值移到前面,較大值
 * 移到后面,最后將整個數組進行插入排序,這樣比起一開始就用插入排序減少了數據交換和移動的次數,
 * 可以說希爾排序是加強 版的插入排序 拿數組5, 2,8, 9, 1, 3,4來說,數組長度為7,當increment為3時,數組分為兩個序列
 * 5,2,8和9,1,3,4,第一次排序,9和5比較,1和2比較,3和8比較,4和比其下標值小increment的數組值相比較
 * 此例子是按照從小到大排列,所以小的會排在前面,第一次排序后數組為5, 1, 3, 4, 2, 8,9
 * 第一次后increment的值變為3/2=1,此時對數組進行插入排序, 實現數組從大到小排
 */
public static void shellSort(int[] data) {
    int j = 0;
    int temp = 0;
    // 每次將步長縮短為原來的一半
    for (int increment = data.length / 2; increment > 0; increment /= 2) {
        for (int i = increment; i < data.length; i++) {
            temp = data[i];
            for (j = i; j >= increment; j -= increment) {
                if (temp < data[j - increment])// 從小到大排
                {
                    data[j] = data[j - increment];
                } else {
                    break;
                }
            }
            data[j] = temp;
        }
    }
}

時間復雜度O(n^1.5)

堆排序算法

基本思想:

堆排序是一種樹形選擇排序,是對直接選擇排序的有效改進。

堆的定義下:具有n個元素的序列 (h1,h2,...,hn),當且僅當滿足(hi>=h2i,hi>=h2i+1)或(hi<=h2i,hi<=h2i+1) (i=1,2,...,n/2)時稱之為堆。在這里只討論滿足前者條件的堆。由堆的定義可以看出,堆頂元素(即第一個元素)必為最大項(大頂堆)。完全二叉樹可以很直觀地表示堆的結構。堆頂為根,其它為左子樹、右子樹。

思想:初始時把要排序的數的序列看作是一棵順序存儲的二叉樹,調整它們的存儲序,使之成為一個 堆,這時堆的根節點的數最大。然后將根節點與堆的最后一個節點交換。然后對前面(n-1)個數重新調整使之成為堆。依此類推,直到只有兩個節點的堆,并對 它們作交換,最后得到有n個節點的有序序列。從算法描述來看,堆排序需要兩個過程,一是建立堆,二是堆頂與堆的最后一個元素交換位置。所以堆排序有兩個函數組成。一是建堆的滲透函數,二是反復調用滲透函數實現排序的函數。

java 實現

public static void heapSort(int[] a){
    int arrayLength = a.length;
    // 循環建堆
    for (int i = 0; i < arrayLength - 1; i++) {
        // 建堆
        buildMaxHeap(a, arrayLength - 1 - i);
        // 交換堆頂和最后一個元素
        swap(a, 0, arrayLength - 1 - i);
        System.out.println(Arrays.toString(a));
    }
}
// 對data數組從0到lastIndex建大頂堆
public static void buildMaxHeap(int[] data, int lastIndex) {
    // 從lastIndex處節點(最后一個節點)的父節點開始
    for (int i = (lastIndex - 1) / 2; i >= 0; i--) {
        // k保存正在判斷的節點
        int k = i;
        // 如果當前k節點的子節點存在
        while (k * 2 + 1 <= lastIndex) {
            // k節點的左子節點的索引
            int biggerIndex = 2 * k + 1;
            // 如果biggerIndex小于lastIndex,即biggerIndex+1代表的k節點的右子節點存在
            if (biggerIndex < lastIndex) {
                // 若果右子節點的值較大
                if (data[biggerIndex] < data[biggerIndex + 1]) {
                    // biggerIndex總是記錄較大子節點的索引
                    biggerIndex++;
                }
            }
            // 如果k節點的值小于其較大的子節點的值
            if (data[k] < data[biggerIndex]) {
                // 交換他們
                swap(data, k, biggerIndex);
                // 將biggerIndex賦予k,開始while循環的下一次循環,重新保證k節點的值大于其左右子節點的值
                k = biggerIndex;
            } else {
                break;
            }
        }
    }
}
// 交換
private static void swap(int[] data, int i, int j) {
    int tmp = data[i];
    data[i] = data[j];
    data[j] = tmp;
}

時間復雜度O(nlogn)不適合待排序序列較少的情況

快速排序算法

基本思想:

通過一趟排序將待排序記錄分割成獨立的兩部分,其中一部分記錄的關鍵字均比另一部分關鍵字小,則分別對這兩部分繼續進行排序,直到整個序列有序。

java 實現

/**
 * 快速排序
 * 
 * @param numbers
 *            帶排序數組
 */
public static void quick(int[] numbers) {
    if (numbers.length > 0) // 查看數組是否為空
    {
        quickSort(numbers, 0, numbers.length - 1);
    }
}
/**
 * 
 * @param numbers
 *            帶排序數組
 * @param low
 *            開始位置
 * @param high
 *            結束位置
 */
public static void quickSort(int[] numbers, int low, int high) {
    if (low >= high) {
        return;
    }
    int middle = getMiddle(numbers, low, high); // 將numbers數組進行一分為二
    quickSort(numbers, low, middle - 1); // 對低字段表進行遞歸排序
    quickSort(numbers, middle + 1, high); // 對高字段表進行遞歸排序
}
/**
 * 查找出中軸(默認是最低位low)的在numbers數組排序后所在位置
 * 
 * @param numbers
 *            帶查找數組
 * @param low
 *            開始位置
 * @param high
 *            結束位置
 * @return 中軸所在位置
 */
public static int getMiddle(int[] numbers, int low, int high) {
    int temp = numbers[low]; // 數組的第一個作為中軸
    while (low < high) {
        while (low < high && numbers[high] > temp) {
            high--;
        }
        numbers[low] = numbers[high];// 比中軸小的記錄移到低端
        while (low < high && numbers[low] < temp) {
            low++;
        }
        numbers[high] = numbers[low]; // 比中軸大的記錄移到高端
    }
    numbers[low] = temp; // 中軸記錄到尾
    return low; // 返回中軸的位置
}

時間復雜度O(nlogn)
快速排序在序列中元素很少時,效率將比較低,不如插入排序,因此一般在序列中元素很少時使用插入排序,這樣可以提高整體效率。

歸并排序算法

基本思想:

歸并(Merge)排序法是將兩個(或兩個以上)有序表合并成一個新的有序表,即把待排序序列分為若干個子序列,每個子序列是有序的。然后再把有序子序列合并為整體有序序列。

java 實現

/**
 * 歸并排序
 * 簡介:將兩個(或兩個以上)有序表合并成一個新的有序表 即把待排序序列分為若干個子序列,每個子序列是有序的。然后再把有序子序列合并為整體有序序列
 * 時間復雜度為O(nlogn)
 * 穩定排序方式
 * @param nums 待排序數組
 * @return 輸出有序數組
 */
public static int[] sort(int[] nums, int low, int high) {
    int mid = (low + high) / 2;
    if (low < high) {
        // 左邊
        sort(nums, low, mid);
        // 右邊
        sort(nums, mid + 1, high);
        // 左右歸并
        merge(nums, low, mid, high);
    }
    return nums;
}
/**
 * 將數組中low到high位置的數進行排序
 * @param nums 待排序數組
 * @param low 待排的開始位置
 * @param mid 待排中間位置
 * @param high 待排結束位置
 */
public static void merge(int[] nums, int low, int mid, int high) {
    int[] temp = new int[high - low + 1];
    int i = low;// 左指針
    int j = mid + 1;// 右指針
    int k = 0;
    // 把較小的數先移到新數組中
    while (i <= mid && j <= high) {
        if (nums[i] < nums[j]) {
            temp[k++] = nums[i++];
        } else {
            temp[k++] = nums[j++];
        }
    }
    // 把左邊剩余的數移入數組
    while (i <= mid) {
        temp[k++] = nums[i++];
    }
    // 把右邊邊剩余的數移入數組
    while (j <= high) {
        temp[k++] = nums[j++];
    }
    // 把新數組中的數覆蓋nums數組
    for (int k2 = 0; k2 < temp.length; k2++) {
        nums[k2 + low] = temp[k2];
    }
}

時間復雜度O(nlogn)

各種算法的時間復雜度等性能比較

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,238評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,430評論 3 415
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,134評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,893評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,653評論 6 408
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,136評論 1 323
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,212評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,372評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,888評論 1 334
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,738評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,939評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,482評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,179評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,588評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,829評論 1 283
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,610評論 3 391
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,916評論 2 372

推薦閱讀更多精彩內容

  • 排序的基本概念 在計算機程序開發過程中,經常需要一組數據元素(或記錄)按某個關鍵字進行排序,排序完成的序列可用于快...
    Jack921閱讀 1,449評論 1 4
  • 概述排序有內部排序和外部排序,內部排序是數據記錄在內存中進行排序,而外部排序是因排序的數據很大,一次不能容納全部的...
    Luc_閱讀 2,285評論 0 35
  • 概述 排序有內部排序和外部排序,內部排序是數據記錄在內存中進行排序,而外部排序是因排序的數據很大,一次不能容納全部...
    蟻前閱讀 5,207評論 0 52
  • 概述:排序有內部排序和外部排序,內部排序是數據記錄在內存中進行排序,而外部排序是因排序的數據很大,一次不能容納全部...
    每天刷兩次牙閱讀 3,737評論 0 15
  • 要想做精致的女人,開始斷舍離吧!
    簡單生活每一天閱讀 215評論 0 0