冒泡排序

綜述

冒泡排序是一種簡單的排序算法.它重復(fù)地走訪過要排序的數(shù)列,一次比較兩個元素,如果它們的順序錯誤就把它們交換過來.
走訪數(shù)列的工作是重復(fù)地進(jìn)行直到?jīng)]有再需要交換,也就是說該數(shù)列已經(jīng)排序完成.
這個算法的名字由來是因為越小的元素會經(jīng)由交換慢慢“浮”到數(shù)列的頂端.

算法描述

1.比較相鄰的元素.如果前一個比后二個大,就交換它們兩個;
2.對每一對相鄰元素作同樣的工作,從開始第一對到結(jié)尾的最后一對,這樣在最后的元素應(yīng)該會是最大的數(shù);
3.針對所有的元素重復(fù)以上的步驟,除了最后一個;
4.持續(xù)每次對越來越少的元素重復(fù)上面的步驟,直到?jīng)]有任何一對數(shù)字需要比較.

示意圖

動態(tài)示意圖
靜態(tài)示意圖

性質(zhì)

排序類別:交換排序
是否是穩(wěn)定排序:穩(wěn)定
是否是原地排序:是
時間復(fù)雜度:O(N^2)
空間復(fù)雜度:O(1)


要點

冒泡排序是一種交換排序.
交換排序:兩兩比較待排序的關(guān)鍵字,并交換不滿足次序要求的那對數(shù),直到整個表都滿足次序要求為止.

算法思想

它重復(fù)地走訪過要排序的數(shù)列,一次比較兩個元素,如果他們的順序錯誤就把他們交換過來.走訪數(shù)列的工作是重復(fù)地進(jìn)行直到?jīng)]有再需要交換,也就是說該數(shù)列已經(jīng)排序完成.
這個算法的名字由來是因為越小的元素會經(jīng)由交換慢慢“浮”到數(shù)列的頂端,故名冒泡排序.

假設(shè)有一個大小為 N 的無序序列.冒泡排序就是要每趟排序過程中通過兩兩比較,找到第 i 個小(大)的元素,將其往上排.
要將以上流程轉(zhuǎn)化為代碼,我們需要像機器一樣去思考,不然編譯器可看不懂.
假設(shè)要對一個大小為 N 的無序序列進(jìn)行升序排序(即從小到大).
(1)每趟排序過程中需要通過比較找到第 i 個小的元素.
所以,我們需要一個外部循環(huán),從數(shù)組首端(下標(biāo) 0)開始,一直掃描到倒數(shù)第二個元素(即下標(biāo) N - 2),剩下最后一個元素,必然為最大.
(2)假設(shè)是第 i 趟排序,可知,前 i-1 個元素已經(jīng)有序.現(xiàn)在要找第 i 個元素,只需從數(shù)組末端開始,掃描到第 i 個元素,將它們兩兩比較即可.
所以,需要一個內(nèi)部循環(huán),從數(shù)組末端開始(下標(biāo) N - 1),掃描到(下標(biāo) i + 1).

時間復(fù)雜度分析

1.若文件的初始狀態(tài)是正序的,一趟掃描即可完成排序.所需的關(guān)鍵字比較次數(shù)C和記錄移動次數(shù)M均達(dá)到最小值:Cmin=N-1, Mmin=0.所以,冒泡排序最好時間復(fù)雜度為O(N).
2.若初始文件是反序的,需要進(jìn)行 N -1 趟排序.每趟排序要進(jìn)行(N-i)次關(guān)鍵字的比較(1 ≤ i ≤ N - 1),且每次比較都必須移動記錄三次來達(dá)到交換記錄位置.
在這種情況下,比較和移動次數(shù)均達(dá)到最大值:
Cmax = N(N-1)/2 = O(N^2)
Mmax = 3N(N-1)/2 = O(N^2)
冒泡排序的最壞時間復(fù)雜度為O(N^2).

因此,冒泡排序的平均時間復(fù)雜度為O(N^2).

總結(jié)起來,其實就是一句話:當(dāng)數(shù)據(jù)越接近正序時,冒泡排序性能越好.

穩(wěn)定性分析

冒泡排序就是把小的元素往前調(diào)或者把大的元素往后調(diào).比較是相鄰的兩個元素比較,交換也發(fā)生在這兩個元素之間.
所以相同元素的前后順序并沒有改變,所以冒泡排序是一種穩(wěn)定排序算法.

優(yōu)化方式

優(yōu)化一

假設(shè)我們現(xiàn)在排序ar[]={1,2,3,4,5,6,7,8,10,9}這組數(shù)據(jù),按照上面的排序方式,第一趟排序后將10和9交換已經(jīng)有序,接下來的8趟排序就是多余的,什么也沒做.所以我們可以在交換的地方加一個標(biāo)記,如果那一趟排序沒有交換元素,說明這組數(shù)據(jù)已經(jīng)有序,不用再繼續(xù)下去.

優(yōu)化二

優(yōu)化一僅僅適用于連片有序而整體無序的數(shù)據(jù)(例如:1, 2,3,4,7,6,5).但是對于前面大部分是無序而后邊小半部分有序的數(shù)據(jù)(1,2,5,7,4,3,6,8,9,10)排序效率也不可觀,對于種類型數(shù)據(jù),我們可以繼續(xù)優(yōu)化:即我們可以記下最后一次交換的位置,后邊沒有交換,必然是有序的,然后下一次排序從第一個比較到上次記錄的位置結(jié)束即可.

優(yōu)化三

優(yōu)化二的效率有很大的提升,還有一種優(yōu)化方法可以繼續(xù)提高效率.大致思想就是一次排序可以確定兩個值,正向掃描找到最大值交換到最后,反向掃描找到最小值交換到最前面.
例如:排序數(shù)據(jù)1,2,3,4,5,6,0

Python實現(xiàn)及優(yōu)化

def bubblesort1(dest_list=[]):
    if len(dest_list) <= 1:
        return dest_list

    for i in range(len(dest_list) - 1):
        print('第{}趟排序為:'.format(str(i + 1)))
        for j in range(len(dest_list) - 1 - i):
            print('第{}次排序為:'.format(str(j + 1)), dest_list)
            if dest_list[j] > dest_list[j + 1]:
                dest_list[j], dest_list[j + 1] = dest_list[j + 1], dest_list[j]
    return dest_list

dest = [5, 3, 4, 6, 2, 1, 7]
result = bubblesort1(dest)
print('最后的結(jié)果是:', result)

'''
第1趟排序為:
第1次排序為: [5, 3, 4, 6, 2, 1, 7]
第2次排序為: [3, 5, 4, 6, 2, 1, 7]
第3次排序為: [3, 4, 5, 6, 2, 1, 7]
第4次排序為: [3, 4, 5, 6, 2, 1, 7]
第5次排序為: [3, 4, 5, 2, 6, 1, 7]
第6次排序為: [3, 4, 5, 2, 1, 6, 7]
第2趟排序為:
第1次排序為: [3, 4, 5, 2, 1, 6, 7]
第2次排序為: [3, 4, 5, 2, 1, 6, 7]
第3次排序為: [3, 4, 5, 2, 1, 6, 7]
第4次排序為: [3, 4, 2, 5, 1, 6, 7]
第5次排序為: [3, 4, 2, 1, 5, 6, 7]
第3趟排序為:
第1次排序為: [3, 4, 2, 1, 5, 6, 7]
第2次排序為: [3, 4, 2, 1, 5, 6, 7]
第3次排序為: [3, 2, 4, 1, 5, 6, 7]
第4次排序為: [3, 2, 1, 4, 5, 6, 7]
第4趟排序為:
第1次排序為: [3, 2, 1, 4, 5, 6, 7]
第2次排序為: [2, 3, 1, 4, 5, 6, 7]
第3次排序為: [2, 1, 3, 4, 5, 6, 7]
第5趟排序為:
第1次排序為: [2, 1, 3, 4, 5, 6, 7]
第2次排序為: [1, 2, 3, 4, 5, 6, 7]
第6趟排序為:
第1次排序為: [1, 2, 3, 4, 5, 6, 7]
最后的結(jié)果是: [1, 2, 3, 4, 5, 6, 7]
'''

"""
優(yōu)化方式一
對冒泡排序常見的改進(jìn)方法是加入標(biāo)志性變量exchange,用于標(biāo)志某一趟排序過程中是否有數(shù)據(jù)交換。
如果進(jìn)行某一趟排序時并沒有進(jìn)行數(shù)據(jù)交換,則說明所有數(shù)據(jù)已經(jīng)有序,可立即結(jié)束排序,避免不必要的比較過程。
"""


def bubblesort2(dest_list=[]):
    if len(dest_list) <= 1:
        return dest_list

    for i in range(len(dest_list)):
        exchange = False
        print('第{}趟排序:'.format(str(i + 1)))
        for j in range(len(dest_list) - 1 - i):
            print('第{}次排序為:'.format(str(j + 1)), dest_list)
            if dest_list[j] > dest_list[j + 1]:
                dest_list[j], dest_list[j + 1] = dest_list[j + 1], dest_list[j]
                exchange = True
        if not exchange:
            break

    return dest_list

dest = [2, 1, 4, 3, 5, 6, 7]
result = bubblesort2(dest)
print('最后的結(jié)果是:', result)

'''
第1趟排序:
第1次排序為: [2, 1, 4, 3, 5, 6, 7]
第2次排序為: [1, 2, 4, 3, 5, 6, 7]
第3次排序為: [1, 2, 4, 3, 5, 6, 7]
第4次排序為: [1, 2, 3, 4, 5, 6, 7]
第5次排序為: [1, 2, 3, 4, 5, 6, 7]
第6次排序為: [1, 2, 3, 4, 5, 6, 7]
第2趟排序:
第1次排序為: [1, 2, 3, 4, 5, 6, 7]
第2次排序為: [1, 2, 3, 4, 5, 6, 7]
第3次排序為: [1, 2, 3, 4, 5, 6, 7]
第4次排序為: [1, 2, 3, 4, 5, 6, 7]
第5次排序為: [1, 2, 3, 4, 5, 6, 7]
最后的結(jié)果是: [1, 2, 3, 4, 5, 6, 7]
'''

"""
優(yōu)化方式二
優(yōu)化一僅僅適用于連片有序而整體無序的數(shù)據(jù)(例如:1, 2,3 ,4 ,7,6,5)。
但是對于前面大部分是無序而后邊小半部分有序的數(shù)據(jù)(1,2,5,7,4,3,6,8,9,10)排序效率也不可觀,對于這種類型數(shù)據(jù),我們可以繼續(xù)優(yōu)化。
我們可以記下最后一次交換的位置,后邊沒有交換,必然是有序的,然后下一次排序從第一個比較到上次記錄的位置結(jié)束即可。
"""


def bubblesort3(dest_list=[]):
    if len(dest_list) <= 1:
        return dest_list

    k = len(dest_list) - 1  # 確定比較次數(shù)
    for i in range(len(dest_list)):
        exchange = False
        pos = 0  # 用來記錄最后一次交換的位置
        print('第{}趟排序:'.format(str(i + 1)))
        for j in range(k):
            print('第{}次排序為:'.format(str(j + 1)), dest_list)
            if dest_list[j] > dest_list[j + 1]:
                dest_list[j], dest_list[j + 1] = dest_list[j + 1], dest_list[j]
                exchange = True
                pos = j  # 交換元素,記錄最后一次交換的位置
        if not exchange:  # 如果沒有交換過元素,則已經(jīng)有序
            break
        k = pos  # 下一次比較到記錄位置即可

    return dest_list

dest = [2, 1, 4, 3, 5, 6, 7]
result = bubblesort3(dest)
print('最后的結(jié)果是:', result)

'''
第1趟排序:
第1次排序為: [2, 1, 4, 3, 5, 6, 7]
第2次排序為: [1, 2, 4, 3, 5, 6, 7]
第3次排序為: [1, 2, 4, 3, 5, 6, 7]
第4次排序為: [1, 2, 3, 4, 5, 6, 7]
第5次排序為: [1, 2, 3, 4, 5, 6, 7]
第6次排序為: [1, 2, 3, 4, 5, 6, 7]
第2趟排序:
第1次排序為: [1, 2, 3, 4, 5, 6, 7]
第2次排序為: [1, 2, 3, 4, 5, 6, 7]
最后的結(jié)果是: [1, 2, 3, 4, 5, 6, 7]
'''

"""
優(yōu)化方式三
大致思想就是一次排序可以確定兩個值,正向掃描找到最大值交換到最后,反向掃描找到最小值交換到最前面。
"""


def bubblesort4(dest_list=[]):
    if len(dest_list) <= 1:
        return dest_list

    k = len(dest_list) - 1  # 確定比較次數(shù)
    n = 0  # 同時找最大值的最小需要兩個下標(biāo)遍歷
    for i in range(len(dest_list)):
        exchange = False
        pos = 0  # 用來記錄最后一次交換的位置
        print('第{}趟排序:'.format(str(i + 1)))
        for j in range(n, k):
            if dest_list[j] > dest_list[j + 1]:
                dest_list[j], dest_list[j + 1] = dest_list[j + 1], dest_list[j]
                exchange = True
                pos = j  # 交換元素,記錄最后一次交換的位置
            print('第{}次排序為:'.format(str(j + 1)), dest_list)
        if not exchange:  # 如果沒有交換過元素,則已經(jīng)有序
            break
        k = pos  # 下一次比較到記錄位置即可

        # print('進(jìn)行倒序?qū)ふ?..')

        for m in range(k, n, -1):
            if dest_list[m] < dest_list[m - 1]:
                dest_list[m], dest_list[m - 1] = dest_list[m - 1], dest_list[m]
                exchange = True
            # print('第{}次排序為:'.format(str(k - n - m + 1)), dest_list)
        n += 1
        if not exchange:  # 如果沒有交換過元素,則已經(jīng)有序
            break

    return dest_list

dest = [1, 2, 3, 4, 5, 6, 0]
result = bubblesort4(dest)
print('最后的結(jié)果是:', result)


'''
第1趟排序:
第1次排序為: [1, 2, 3, 4, 5, 6, 0]
第2次排序為: [1, 2, 3, 4, 5, 6, 0]
第3次排序為: [1, 2, 3, 4, 5, 6, 0]
第4次排序為: [1, 2, 3, 4, 5, 6, 0]
第5次排序為: [1, 2, 3, 4, 5, 6, 0]
第6次排序為: [1, 2, 3, 4, 5, 0, 6]
第2趟排序:
第2次排序為: [0, 1, 2, 3, 4, 5, 6]
第3次排序為: [0, 1, 2, 3, 4, 5, 6]
第4次排序為: [0, 1, 2, 3, 4, 5, 6]
第5次排序為: [0, 1, 2, 3, 4, 5, 6]
最后的結(jié)果是: [0, 1, 2, 3, 4, 5, 6]
'''

C語言實現(xiàn)及優(yōu)化

#include <stdio.h>


/* 1.從當(dāng)前元素起,向后依次比較每一對相鄰元素,若逆序則交換 */
/* 2.對所有元素均重復(fù)以上步驟,直至最后一個元素 */
void bubbleSort(int arr[],int len)
{
    int i = 0;
    int count = 0;
    int tmp = 0;
    for(i = 0; i < len - 1; i++)//確定排序趟數(shù)
    {
        int j = 0;
        for(j = 0; j < len - 1 - i; j++)//確定比較次數(shù)
        {
            if(arr[j]>arr[j + 1])
            {
                //交換
                tmp = arr[j];
                arr[j] = arr[j + 1];
                arr[j + 1] = tmp;
                count ++;
            }
        }
    }
    printf("最后交換的次數(shù)是:%d\n", count);
}

/*
優(yōu)化一
假設(shè)我們現(xiàn)在排序ar[]={1,2,3,4,5,6,7,8,10,9}這組數(shù)據(jù),按照上面的排序方式,第一趟排序后將10和9交換已經(jīng)有序,接下來的8趟排序就是多余的,什么也沒做.
所以我們可以在交換的地方加一個標(biāo)記,如果那一趟排序沒有交換元素,說明這組數(shù)據(jù)已經(jīng)有序,不用再繼續(xù)下去.
*/

void bubbleSort2(int arr[], int len)
{
    int i=0, j=0, count=0;
    for(i=0;i<len;i++)             /* 外循環(huán)為排序趟數(shù),len個數(shù)進(jìn)行l(wèi)en-1趟 */
    {
        int flag = 0;
        for(j=0;j<len-i-1;j++)     /* 內(nèi)循環(huán)為每趟比較的次數(shù),第i趟比較len-i次 */
        {
            if(arr[j]>arr[j+1])    /* 相鄰元素比較,若逆序則交換(升序為左大于右,降序反之)*/
            {
                int temp = arr[j];
                arr[j] = arr[j+1];
                arr[j+1] = temp;
                flag = 1;
                count ++;
            }
        }

        if(flag)
            printf("最后交換的次數(shù)是:%d\n", count);
            return;
    }
    printf("最后交換的次數(shù)是:%d\n", count);
}

/*
優(yōu)化二
優(yōu)化一僅僅適用于連片有序而整體無序的數(shù)據(jù)(例如:1, 2,3,4,7,6,5).
但是對于前面大部分是無序而后邊小半部分有序的數(shù)據(jù)(1,2,5,7,4,3,6,8,9,10)排序效率也不可觀,對于種類型數(shù)據(jù),我們可以繼續(xù)優(yōu)化.
既我們可以記下最后一次交換的位置,后邊沒有交換,必然是有序的,然后下一次排序從第一個比較到上次記錄的位置結(jié)束即可.
*/
void bubbleSort3(int arr[], int len)
{
    int i = 0;
    int count = 0;
    int tmp = 0;
    int flag = 0;
    int pos = 0;//用來記錄最后一次交換的位置
    int k = len - 1;
    for(i = 0; i < len - 1; i++)//確定排序趟數(shù)
    {
        pos = 0;
        int j = 0;
        flag = 0;
        for(j = 0; j < k; j++)//確定比較次數(shù)
        {
            if(arr[j]>arr[j + 1])
            {
                //交換
                tmp = arr[j];
                arr[j] = arr[j + 1];
                arr[j + 1] = tmp;
                flag = 1;//加入標(biāo)記
                pos = j;//交換元素,記錄最后一次交換的位置
                count ++;
            }
        }

        if(flag == 0)//如果沒有交換過元素,則已經(jīng)有序
        {
            printf("最后交換的次數(shù)是:%d\n", count);
            return;
        }
        k = pos;//下一次比較到記錄位置即可
    }
    printf("最后交換的次數(shù)是:%d\n", count);
    return;
}


/*
優(yōu)化三
優(yōu)化二的效率有很大的提升,還有一種優(yōu)化方法可以繼續(xù)提高效率.
大致思想就是一次排序可以確定兩個值,正向掃描找到最大值交換到最后,反向掃描找到最小值交換到最前面.例如:排序數(shù)據(jù)1,2,3,4,5,6,0
*/

void bubbleSort4(int arr[], int len)
{
    int i = 0;
    int j = 0;
    int n = 0;//同時找最大值的最小需要兩個下標(biāo)遍歷
    int flag = 0;
    int pos = 0;//用來記錄最后一次交換的位置
    int k = len - 1;
    for(i = 0; i < len - 1; i++)//確定排序趟數(shù)
    {
        pos = 0;
        flag = 0;
        //正向?qū)ふ易畲笾?        for(j = n; j < k; j++)//確定比較次數(shù)
        {
            if(arr[j]>arr[j + 1])
            {
                //交換
                int tmp = arr[j];
                arr[j] = arr[j + 1];
                arr[j + 1] = tmp;
                flag = 1;//加入標(biāo)記
                pos = j;//交換元素,記錄最后一次交換的位置
            }
        }
        if(flag == 0)//如果沒有交換過元素,則已經(jīng)有序,直接結(jié)束
        {
            return;
        }
        k = pos;//下一次比較到記錄位置即可
        //反向?qū)ふ易钚≈?        for(j = k; j > n; j--)
        {
            int tmp = arr[j];
            arr[j] = arr[j - 1];
            arr[j - 1] = tmp;
            flag = 1;
        }
        n++;
        if(flag == 0)//如果沒有交換過元素,則已經(jīng)有序,直接結(jié)束
        {
            return;
        }
    }
}




int main()
{
    int i=0;

    int dest[] = { 1, 2, 5, 7, 4, 3, 6, 8, 9, 10 };
    bubbleSort(dest, 7);
    for(i=0;i<7;i++)
        printf("%d ", dest[i]);
    printf("\n");

    int dest1[] = { 1, 2, 5, 7, 4, 3, 6, 8, 9, 10 };
    bubbleSort2(dest1, 7);
    for(i=0;i<7;i++)
        printf("%d ", dest1[i]);
    printf("\n");

    int dest2[] = { 1, 2, 5, 7, 4, 3, 6, 8, 9, 10 };
    bubbleSort3(dest2, 10);
    for(i=0;i<10;i++)
        printf("%d ", dest2[i]);
    printf("\n");

    int dest3[] = { 1, 2, 5, 7, 4, 3, 6, 8, 9, 10 };
    bubbleSort4(dest2, 10);
    for(i=0;i<10;i++)
        printf("%d ", dest3[i]);
    printf("\n");

    return 0;
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容