冒泡排序算法之解析及優化(一看就懂)

      前言       

面試中經常遇到寫個排序算法,相信大家都會寫冒泡排序算法,雖然很多人會寫,我敢肯定,不是所有人都深知這個算法深層次的邏輯和規律,因爲對於一個普通的開發者來說,通常工作中絕大部分時間用不到這些內容,爲了應付面試,臨時把這個算法背下來,一般面試官一看寫對了,也不會再問什麼,如果想要研究下算法問題,我們就有必要徹底弄清楚其中的奧祕,這些是基礎,

 

一、概要

        它重複地走訪過要排序的元素列,依次比較兩個相鄰的元素,如果順序(如從大到小、首字母從Z到A)錯誤就把他們交換過來。走訪元素的工作是重複地進行直到沒有相鄰元素需要交換,也就是說該元素列已經排序完成。

這個算法的名字由來是因爲越小的元素會經由交換慢慢“浮”到數列的頂端(升序或降序排列),就如同碳酸飲料中二氧化碳的氣泡最終會上浮到頂端一樣,故名“冒泡排序”。

         簡單講就是:將一組大小的雜亂的元素,逐個比較兩個相鄰的元素,較大或較小的放在右邊,最後達到升序或降序的排序

二、基本原理

      

  1. 比較相鄰的元素。如果第一個比第二個大(或小),就交換他們兩個。 

  2. 對每一對相鄰元素做同樣的工作,從開始第一對到結尾的最後一對。在這一點,最後的元素應該會是最大的數。 

  3. 針對所有的元素重複以上的步驟,除了最後一個。 

  4. 持續每次對越來越少的元素重複上面的步驟,直到沒有任何一對數字需要比較

三、算法分析

      算法複雜度:N個數字要排序完成,總共進行N-1趟排序,每i趟的排序次數爲(N-i)次

    最好時間複雜度爲O(N)

    Cmax = N(N-1)/2 = O(N2)
    Mmax = 3N(N-1)/2 = O(N2)
    冒泡排序的最壞時間複雜度爲O(N2)
    冒泡排序的平均時間複雜度爲O(N2)

        算法穩定性 : 冒泡排序就是把小的元素往前調或者把大的元素往後調。比較是相鄰的兩個元素比較,交換也發生在這兩個元素之間。所以,    如果兩個元素相等,是不會再交換的;如果兩個相等的元素沒有相鄰,那麼即使通過前面的兩兩交換把兩個相鄰起來,這時候      也  不會交換,所以相同元素的前後順序並沒有改變,所以冒泡排序是一種穩定排序算法

四、代碼實現

       以java代碼爲例

    public static void bubbleSort2(int[] list) {
        for(int i = 0 ; i< list.length-1 ; i++) {
            for(int j= 0 ; j < list.length-1-i ; j++) {
                System.out.println("==========外循環 i====" + i + "==========內循環 j====" + j);
                System.out.println(" 換位前: "  + Arrays.toString(list));
                System.out.println(" 比較的元素爲:第" + j + " 和 " +(j+1) );
                if(list[j]>list[j+1]) {
                    int temp = list[j];
                    list[j]=list[j+1];
                    list[j+1]=temp;
                }
                System.out.println(" 換位後: "  + Arrays.toString(list) );
            }
        }
    }

打印日誌爲:

==========外循環 i====0==========內循環 j====0
 換位前: [9, 2, 5, 1]
 比較的元素爲:第0 和 1
 換位後: [2, 9, 5, 1]
==========外循環 i====0==========內循環 j====1
 換位前: [2, 9, 5, 1]
 比較的元素爲:第1 和 2
 換位後: [2, 5, 9, 1]
==========外循環 i====0==========內循環 j====2
 換位前: [2, 5, 9, 1]
 比較的元素爲:第2 和 3
 換位後: [2, 5, 1, 9]
==========外循環 i====1==========內循環 j====0
 換位前: [2, 5, 1, 9]
 比較的元素爲:第0 和 1
 換位後: [2, 5, 1, 9]
==========外循環 i====1==========內循環 j====1
 換位前: [2, 5, 1, 9]
 比較的元素爲:第1 和 2
 換位後: [2, 1, 5, 9]
==========外循環 i====2==========內循環 j====0
 換位前: [2, 1, 5, 9]
 比較的元素爲:第0 和 1
 換位後: [1, 2, 5, 9]

    

 分析上述代碼可以看出,

       總共兩層循環,內層循環次數隨着外層次數的增加而減少,爲什麼要這樣設計呢?

       原因分析:

        根據算法規律,每次排序都能把數組內的最大或最小元素放在數組的最頂端,以上述代碼爲例,上述是以從小到大排序,第一次外循環結束就能把最大的放在最右邊,所以在內循環中可以看到,互相比較元素的下標隨着外循環的增加而增加,比較的次數也就越來越小,最後直接完排序,

  分析日誌可以看出

     第一次外循環結束,已經把最大的元素推到最右邊了,後續的比較,已經不再不需要這個元素參與了

總結:如果希望元素從小到大排序,第一次外循環結束就能把最大元素放至最右邊,第二次外循環線束就能把第二大的元素放至右二的位置,以此內推,直至排序結束

 

五、算法優化

     具體代碼如下

        

    public static void bubbleSort2(int[] list) {
        boolean bChange = false; // 交換標誌
        for(int i = 0 ; i< list.length-1 ; i++) {
            bChange = false;
            for(int j= 0 ; j < list.length-1-i ; j++) {
                System.out.println("==========外循環 i====" + i + "==========內循環 j====" + j);
                System.out.println(" 換位前: "  + Arrays.toString(list));
                System.out.println(" 比較的元素爲:第" + j + " 和 " +(j+1) );
                if(list[j]>list[j+1]) {
                    int temp = list[j];
                    list[j]=list[j+1];
                    list[j+1]=temp;
                    bChange = true;
                }
                System.out.println(" 換位後: "  + Arrays.toString(list) );
            }
            // 如果標誌爲false,說明本輪遍歷沒有交換,已經是有序數列,可以結束排序
            if (false == bChange)
                break;
        }
    }

 

 增加交換標誌,可以結束循環, 內循環如果沒有交換,說明已經是有序數列,直接跳出大循環,提高了效率。

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章