java冒泡排序和快速排序

来源:https://www.cnblogs.com/javagh/archive/2018/04/08/bubble.html
-Advertisement-
Play Games

幾乎每個人做java開發的都能寫出冒泡排序的演算法,那麼java中的排序是怎麼做的呢?冒泡排序並不是很好的排序方法,一起來瞭解一下吧。 ...


一、冒泡排序

1.演算法介紹

 

 

設排序表長為n,從後向前或者從前向後兩兩比較相鄰元素的值,如果兩者的相對次序不對(A[i-1] > A[i]),則交換它們,其結果是將最小的元素交換到待排序序列的第一個位置,我們稱它為一趟冒泡。下一趟冒泡時,前一趟確定的最小元素不再參與比較,待排序序列減少一個元素,每趟冒泡的結果把序列中最小的元素放到了序列的”最前面”。

 

 

2.演算法實現

 

冒泡排序封裝函數的代碼如下

public void bubbleSort(int[] arr) {
    int temp;//定義一個臨時變數
    for(int i=0;i<arr.length-1;i++){//冒泡趟數
        for(int j=0;j<arr.length-i-1;j++){
            //如果順序不對,則交換兩個元素
            if(arr[j+1]<arr[j]){
                temp = arr[j];
                arr[j] = arr[j+1];
                arr[j+1] = temp;
            }
        }
    }
}

測試代碼如下

public static void main(String[] args) {
    Test t = new Test();
    int arr[] = new int[]{13,26,22,22,35,18};
    t.bubbleSort(arr);
    System.out.println(Arrays.toString(arr));
}

 

運行結果如下

[13, 18, 22, 22, 26, 35]

 

3.演算法分析

 

冒泡排序的時間複雜度為O(n^2),空間複雜度為O(1),它是一種穩定的排序演算法。當然了,這也是非常基礎的一種演算法,一般找工作有些公司喜歡出筆試題。

 

下麵我們來看看java中的Arrays.sort(int []a)方法是怎麼實現的。

 


 

二、快速排序


java中Arrays.sort使用了兩種排序方法,快速排序和優化的合併排序。

快速排序主要是對哪些基本類型數據(int,short,long等)排序, 而合併排序用於對對象類型進行排序。

 

使用不同類型的排序演算法主要是由於快速排序是不穩定的,而合併排序是穩定的。這裡的穩定是指比較相等的數據在排序之後仍然按照排序之前的前後順序排列。對於基本數據類型,穩定性沒有意義,而對於對象類型,穩定性是比較重要的,因為對象相等的判斷可能只是判斷關鍵屬性,最好保持相等對象的非關鍵屬性的順序與排序前一直;另外一個原因是由於合併排序相對而言比較次數比快速排序少,移動(對象引用的移動)次數比快速排序多,而對於對象來說,比較一般比移動耗時。

 

1.實現原理

 

java1.7之後的版本,開始用雙軸快排取代了以前的排序演算法,現在只實現了8種基本數據類型性的雙軸快排,對象的排序在1.7中還在用老式的,不過都標了過時,估計以後版本中就會被新的雙軸快排取代了。

 

他的DualPivotQuicksort()方法,裡邊一共寫了三種演算法(不算改進版的插入排序話),對於大數組而且部分高度有序的用歸併排序,其餘的用雙軸快排進行分割, 分割到足夠小的時候用插入排序(主要是改進版的pair insertion sort)。

 

雙軸快排的基本原理是取兩個pivot,所有比pivot1小的放到最左邊,比pivot2大的放到最右邊,然後遞歸下去,就可以把兩端的元素完成排序,之後處理中間部分,中間部分如果過大就繼續遞歸用這種方式繼續分割,如果不大,就用單軸分割對兩部分遞歸調用下去。

 

2.實現代碼

 

代碼截取自jdk1.7中的Arrays類

/**
 * Sorts the specified range of the array.
 *
 * @param a the array to be sorted
 * @param left the index of the first element, inclusive, to be sorted
 * @param right the index of the last element, inclusive, to be sorted
 */
public static void sort(int[] a, int left, int right) {
    // Use Quicksort on small arrays
    if (right - left < QUICKSORT_THRESHOLD) {
        sort(a, left, right, true);
        return;
    }

    /*
     * Index run[i] is the start of i-th run
     * (ascending or descending sequence).
     */
    int[] run = new int[MAX_RUN_COUNT + 1];
    int count = 0; run[0] = left;

    // Check if the array is nearly sorted
    for (int k = left; k < right; run[count] = k) {
        if (a[k] < a[k + 1]) { // ascending
            while (++k <= right && a[k - 1] <= a[k]);
        } else if (a[k] > a[k + 1]) { // descending
            while (++k <= right && a[k - 1] >= a[k]);
            for (int lo = run[count] - 1, hi = k; ++lo < --hi; ) {
                int t = a[lo]; a[lo] = a[hi]; a[hi] = t;
            }
        } else { // equal
            for (int m = MAX_RUN_LENGTH; ++k <= right && a[k - 1] == a[k]; ) {
                if (--m == 0) {
                    sort(a, left, right, true);
                    return;
                }
            }
        }

        /*
         * The array is not highly structured,
         * use Quicksort instead of merge sort.
         */
        if (++count == MAX_RUN_COUNT) {
            sort(a, left, right, true);
            return;
        }
    }

    // Check special cases
    if (run[count] == right++) { // The last run contains one element
        run[++count] = right;
    } else if (count == 1) { // The array is already sorted
        return;
    }

    /*
     * Create temporary array, which is used for merging.
     * Implementation note: variable "right" is increased by 1.
     */
    int[] b; byte odd = 0;
    for (int n = 1; (n <<= 1) < count; odd ^= 1);

    if (odd == 0) {
        b = a; a = new int[b.length];
        for (int i = left - 1; ++i < right; a[i] = b[i]);
    } else {
        b = new int[a.length];
    }

    // Merging
    for (int last; count > 1; count = last) {
        for (int k = (last = 0) + 2; k <= count; k += 2) {
            int hi = run[k], mi = run[k - 1];
            for (int i = run[k - 2], p = i, q = mi; i < hi; ++i) {
                if (q >= hi || p < mi && a[p] <= a[q]) {
                    b[i] = a[p++];
                } else {
                    b[i] = a[q++];
                }
            }
            run[++last] = hi;
        }
        if ((count & 1) != 0) {
            for (int i = right, lo = run[count - 1]; --i >= lo;
                b[i] = a[i]
            );
            run[++last] = right;
        }
        int[] t = a; a = b; b = t;
    }
}

 

3.源碼分析

 

源碼中的快速排序,主要做了以下幾個方面的優化:

 

  1)當待排序的數組中的元素個數較少時,源碼中的閥值為7,採用的是插入排序。儘管插入排序的時間複雜度為0(n^2),但是當數組元素較少時,插入排序優於快速排序,因為這時快速排序的遞歸操作影響性能。

 

  2)較好的選擇了劃分元(基準元素)。能夠將數組分成大致兩個相等的部分,避免出現最壞的情況。例如當數組有序的的情況下,選擇第一個元素作為劃分元,將使得演算法的時間複雜度達到O(n^2).

 

源碼中選擇劃分元的方法:

    當數組大小為 size=7 時 ,取數組中間元素作為劃分元。int n=m>>1;(此方法值得借鑒)

    當數組大小 7<size<=40時,取首、中、末三個元素中間大小的元素作為劃分元。

    當數組大小 size>40 時 ,從待排數組中較均勻的選擇9個元素,選出一個偽中數做為劃分元。

 

  3)根據劃分元 v ,形成不變式 v* (<v)* (>v)* v*

  普通的快速排序演算法,經過一次劃分後,將劃分元排到素組較中間的位置,左邊的元素小於劃分元,右邊的元素大於劃分元,而沒有將與劃分元相等的元素放在其附近,這一點,在Arrays.sort()中得到了較大的優化。

搜索微信公眾號“java工會”,或者掃碼關註我們,與君共勉!

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 在對比醫院業務數據中的各類藥品價格的時候,面對著成千上百種的藥品。因而想到使用爬蟲來自動獲取網上的藥品價格,保存下來導入資料庫中就可以方便地比較院方的藥品採購價格了。 通過百度搜索“藥品價格查詢”,在眾多的網站中,這裡選擇了藥價查詢網(http://www.china-yao.com/),主要是因為 ...
  • 採用ID3演算法 (信息熵:H(X)=−∑i=0np(xi)log2p(xi)) 下載一個決策樹可視化軟體:Graphviz (註意環境變數Path加:C:\Program Files (x86)\Graphviz2.38\bin) 代碼: 導入需要用到的庫: 讀取表格: 這裡一些數據(屬性),決定一 ...
  • 1.修改tp5/application/index/controller/Index.php內容。 2.修改tp5/application/index/view/index/index.html頁面內容。 ...
  • Maven的多模塊可以讓項目結構更明確,提高功能的內聚,降低項目的耦合度,真正的體現出分層這一概念。 我們在操作中,要明白為什麼這樣做,要瞭解到更深的層次,這樣,我們就不限於個別軟體了。 話不多說,直入主題: 如果對Maven還不夠熟悉,請看該博客:Maven基礎 整個項目做完之後的結構是這樣的: ...
  • 從官網上下載php後(我下的是php7.2.3版本),本想做個mysql的連接,但是無論怎麼配置mysqli擴展,發現mysqli都沒法用。 從百度上搜的那些方法都沒法用,發現都是一些在php.ini中配置extension=php_mysqli.dll,事實上這句話沒用了。 於是我仔細看了一下ph ...
  • 涉及到介面的方法和幾種特殊的介面,lambda表達式的作用、目的,以及使用過程中需要註意的一些地方 ...
  • go語言聖經-聲明1.四種類型的聲明語句:var、const、type和func,分別對應變數、常量、類型和函數實體對象的聲明2.包一級聲明語句聲明的名字可在整個包對應的每個源文件中訪問,局部聲明的名字就只能在函數內部很小的範圍被訪問 go語言聖經-變數1.var 變數名字 類型 = 表達式2.零值 ...
  • 本文將詳細的介紹C語言單鏈表的創建、刪除、查找、插入以及輸出功能 一、創建 二、插入 三、刪除 四、查找 五、輸出 六、主函數部分 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...