1. 程式人生 > >簡單氣泡排序的時間複雜度及其兩種優化

簡單氣泡排序的時間複雜度及其兩種優化

在我們寫一些簡單的程式中,對一組資料進行簡單的有序的排列是經常會遇到的,所以我們必須熟悉幾種基本的演算法。

而氣泡排序就是我們學習排序的基礎

氣泡排序:

形象的可以理解為:水中上升的氣泡,在上升的過程中,氣泡越來越小,不斷比較相鄰的元素,將小的往後調

我們來解決這樣一個問題:

設有一陣列,其大小為6個元素(int array[6]),陣列內的資料是(4,2,5,3,2,6),此刻資料是無序的,現要求我們程式設計將其變成一個有序的從大到小的陣列,從下標0開始。

思考:按照題目要求,毫無疑問,最終的有序陣列應該是這樣(6,5,4,3,2,2),要做到這樣,最簡單的辦法就是進行比較交換

1:我們從陣列的第一個元素array[0]向後走,如果比相鄰的小,那麼交換,如此進行下去,可以發現,我們找到了所

      有元素中最小的並且已經將它放在了最後一個位置array[5]

2:然後,我們重新從第一個元素向後走,還是相鄰的比較,並且交換,但是我們只需要比較到4,放在array[4]

3:重複第二步,直到比較到1

程式碼:

#include <stdio.h>
#include <iostream>
using namespace std;

void Bubble_sort(int *array, int length)
{
        //變數i,j用於迴圈,變數t用於交換中間值
        int i, j, t;
        for(i = 1; i < length; i++)
        {       
                //比較的次數越來越少,但是每一個都能在相應的範圍內確定一個最小值並放在合理的位置
                for(j = 0; j < length - i ; j++)
                {
                        //在滿足條件的情況下,交換兩個數
                        if(array[j] < array[j + 1])
                        {
                                t = array[j];
                                array[j] = array[j + 1];
                                array[j + 1] = t;
                        }
                }
        }
        //將6個數輸出
        for(i = 0; i< length; i++)
                cout<<array[i]<<endl;
}

int main()
{
        int array[] = {4,2,5,3,2,6};        //定義一個數組並簡單的初始化
        int length = sizeof(array)/sizeof(int);//求定義陣列的具體長度
        Bubble_sort(array, length);     //呼叫相應的排序函式
        
        return 0;       
}

關於優化:我們必須從時間複雜度和空間複雜度上面來看

好多人一直都在糾結氣泡排序的時間複雜度

在最壞的情況下是:O(N)

還是在最壞的情況下是比較比較次數是:n(n - 1)/ 2

他們兩到底是什麼關係???

其實:總而言之,氣泡排序是一種用時間在換空間的排序。

最壞的情況當然是:每一次的比較都會進行交換,也就是說要把逆序的數列變成順序或者要把順序變成逆序

5,4,3,2,1以冒泡升序排列

1,從第一個數5開始和後面的數進行比較比較到倒數第二個數2為止,過程為:5跟4比較,5比4大,兩者交換,然後

      跟3比較,5比3大,繼續進行交換,依次進行比較,比較完成為:4,3,2,1,5

2,從第一個數4開始和後面的數進行比較,過程為:4跟3比較,4比3大,兩者進行交換,然後跟2進行比較,4比2大

      ,繼續交換,依次比較,比較完成為:3,2,1,4,5

3,同理

所以總的比較次數就是:4 + 3 + 2 + 1

所以,對於n位數的比較次數就是:n + (n - 1)+(n -2)+...+1 = n(n - 1)/ 2

而O(N^2)表示的是複雜度的數量級,舉個例子來說,如果n = 10000,那麼n(n -1 )/2 = (n^2 - n)/ 2

= (100000000 - 10000)/2,相對於10^8來說,10000可以忽略不計,所以總計算次數約為0.5 * N^2.

所以,就用O(N^2)表示了數量級(忽略了前面的係數0.5)

所以,我們可以從趟數上面處理,因為有可能我們比較一定的倘數之後就已經變成了有序的了,沒有必要再去做一些無用的比較了

如:5,1,2,3,4

冒泡一次之後就變成了:1,2,3,4,5已經有序了,我們沒有必要再去比較了

程式:

#include <stdio.h>
#include <iostream>
using namespace std;

void Bubble_sort(int *array, int length)
{
        int i, j, t;
        int flag;                    //標誌量,用於指示,數列已然是有序的
        for(i = 1; i < length; i++)
        {       
                flag = 1;
                for(j = 0; j < length - i ; j++)
                {
                        if(array[j] < array[j + 1])   //合理的交換相鄰的兩個數
                        {
                                flag = 0;
                                t = array[j];
                                array[j] = array[j + 1];
                                array[j + 1] = t;
                        }
                }
                if(flag == 1)         //標誌位
                        break;
        }
        
        for(i = 0; i< length; i++)//輸出排序後的數列
                cout<<array[i]<<endl;
}

int main()
{
        int array[] = {4,2,5,3,2,6};
        int length = sizeof(array)/sizeof(int);
        Bubble_sort(array, length);
        
        return 0;       
}

這裡我們加入了標誌,flag,如果有一趟排序中沒有產生交換的話,那麼說明此刻數列以及變成了有序的數列

從上面時間複雜度看,時間主要花費在交換上面了,如果轉匯編的話,比較可能只需要一條指令,而交換可能就需要很多指令,所以我們的目的就是減少某一倘的交換次數即可

如下:為了便於講解,我們將上面用於排序的那些數列簡單的改改

4,2,3,2,6,7,8

主要的就是:4,3,2,2(前面的排列),後面就是比較就是:

array[3]跟array[4]比較,2小於6,那麼記錄下來,繼續比較array[3]和array[5],2小於7,繼續比較array[3]和array[6],

2小於8,但是由於8是數列的最後一個所以我們直接交換:

所以,第一次排序的結果就是:4,3,2,8,6,7,2

可以簡單的理解:

就變成這樣了,,,,(理解如上)

#include <stdio.h>                                                                                                                    
#include <iostream>
using namespace std;

void Bubble_sort(int *array, int length)
{
        int i, j, t, k;
        int flag;       //flag表示減少的倘數
        for(i = 1; i < length; i++)
        {
                flag = 1;
                for(j = 0; j < length - i ; j++)
                {
                        if(array[j] < array[j + 1])   
                        {
                                flag = 0;
                                k = j+ 1;
                                while(k < length - i + 1)//確定其有效性
                                {
                                        if(array[j] >= array[k]) //這裡新增等號,是為了不影響程式的穩定性
                                        {
                                                //交換array[j]與array[k - 1]
                                                t = array[j];
                                                array[j] = array[k - 1];
                                                array[k - 1] = t;
                                                break;
                                        }
                                        if(k == length - i)  //如果走到最後了,那麼直接進行交換
                                        {
                                                t = array[j];
                                                array[j] = array[k];
                                                array[k] = t;
                                                break;
                                        }
                                        k++;
                                }
                                if(k == length - i)        //已經到了末尾了,可以直接退出本次的迴圈了
                                        break;
                                k = k - 2;
                                j = k;
                        }
                } 
                if(flag == 1)
                        break;
        }
        //輸出我們排       
        for(i = 0; i< length; i++)
                cout<<array[i]<<endl;
}   

int main()
{
        //定義一個數組,並給其一個簡單的初始化
        int array[] = {4,2,3,2,6,7,8};
        //求出陣列的大小
        int length = sizeof(array)/sizeof(int);
        //呼叫排序函式
        Bubble_sort(array, length);
        
        return 0;       
}





如上程式:上面的k值就是類似與一個簡單的標誌

關於第三步的優化方式,可能一部分人不太認同,因為對於冒泡而言,必須是跟相鄰的進行比較,然後進行交換,而我上面這個不完全是這樣的,但是,我還是認為,對於程式,重要的是:

時間複雜度,空間複雜度,如果我們能針對當前的數列選擇一個合理的排序方式,那就是極好的!!!

因為,不同的排序,塊排,插入,基數,堆排都有自己最適合的情況,我們不能要求某一個排序必須用某一個排序,是吧,,,,

鄙人的一點簡單看法,,謝謝大家指點,,,小子謝謝啦!!!