双向冒泡排序

Posted wasi-991017

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了双向冒泡排序相关的知识,希望对你有一定的参考价值。
















算法例题

用随机函数生成16个2位正整数(10~99),利用双向冒泡排序法将其排序。


算法思路

个人概括

在我看来双向冒泡排序就是冒泡排序的一个小改变,并不是改进优化。
每一趟排序分成两个部分,同时在得到最后的有序序列前,把有序序列分成两部分,一部分在原无序序列后(和普通冒泡排序一样),一部分在原无序序列前。最后将有序序列①和有序序列②合并。
排序①(和普通冒泡排序一样),从左到右通过无序序列中元素的比较和交换位置,将无序序列中最大的元素移到最后,将其更新为有序序列②中的最小元素。
排序②,从右到左通过无序序列中元素的比较和交换位置,将无序序列中最小的元素移到最前,将其更新为有序序列①中的最小元素。

1.引入一个无序序列

技术图片

2.第一趟

  • 第一部分
  • 比较第一个元素和第二个元素
  • 第一个元素小于第二个元素,无需交换

技术图片


  • 比较第二个元素和第三个元素
  • 第二个元素大于第三个元素,进行交换

技术图片
技术图片
技术图片

  • (省略中间比较交换n步)比较第十二个元素和第十三个元素
  • 第十二个元素大于第十三个元素,进行交换
  • 将排在无序序列中最后一个元素,即第十三个元素,更新为有序序列②

技术图片
技术图片
技术图片
技术图片

  • 第二部分
  • 从无序序列中最后一个元素开始比较,即比较第十二个元素和第十一个元素
  • 第十二个元素大于第十一个元素,无需交换

技术图片

  • 比较第十一个元素和第十个元素
  • 第十一个元素小于第十个元素,进行交换

技术图片
技术图片
技术图片

  • (省略中间比较交换n步)比较第二个元素和第一个元素
  • 第二个元素大于第一个元素,进行交换
  • 将排在无序序列中最前面的一个元素,即第一个元素,更新为有序序列①

技术图片
技术图片
技术图片
技术图片

3.第二趟

  • (省略中间比较交换n步)将排在无序序列中最后面的一个元素,即第十二个元素,更新为有序序列②
  • (省略中间比较交换n步)将排在无序序列中最前面的一个元素,即第二个元素,更新为有序序列①

技术图片



4.第三趟

  • (省略中间比较交换n步)将排在无序序列中最后面的一个元素,即第十一个元素,更新为有序序列②
  • (省略中间比较交换n步)将排在无序序列中最前面的一个元素,即第三个元素,更新为有序序列①

技术图片



5.(省略n步)最后一趟

技术图片

考虑情况

  1. 某一趟排序(不只是第一趟)没有移动任何一个元素,则说明该无序序列实际上是有序序列,代码中使用两个监视哨分别对两部分排序进行监视;
  2. 每一趟排序都会确定无序序列中一个最大元素为有序序列中的最小元素,即每一趟排序后都会少两次比较次数。

算法效率

  • 最好情况:待排序序列为顺序序列
    • 比较次数KCN:2n-3;
    • 移动次数RMN:0;
  • 最坏情况:待排序序列为逆序序列
    • 比较次数KCN:(n - 1) + (n - 2) + ... + 1 ≈ n2/2;
    • 移动次数RMN:3((n - 1) + (n - 2) + ... + 1) ≈ 3n2/2;

所以时间复杂度为O(n2)
下面的代码是我按照自己的思路进行编写的,算法效率能达到应有的程度。

空间复杂度为O(1),移动时需要辅助空间。

算法特点

  • 稳定排序;
  • 链式存储结构也适合;
  • 移动次数较多;
  • 更适合于初始记录基本有序(正序)的情况,当序列完全无序,尤其是逆序,且元素过多,时间复杂度会大大提高。


算法代码

#include<iostream>
#include<ctime>
using namespace std;

void DoubleBubbleSort(int* array, int n)
{
    //记录比较次数和移动次数
    int recordMove = 0;
    int recordCompare = 0;

    //标记排序是否发生交换
    //若不发生交换
    //则某一部分的排序完成
    int flag_1 = 1;
    int flag_2 = 1;

    while ((flag_1 == 1 || flag_2 == 1) && n - 1 > 0)
    {
        //若没有发生交换
        //flag为0,则不会发生下一趟排序
        //将最大的数移到最后
        if (flag_1 == 1)
        {
            flag_1 = 0;
            for (int i = 16 - n + 1; i < n; i++)
            {
                recordCompare++;
                if (array[i] > array[i + 1])
                {
                    flag_1 = 1;
                    array[0] = array[i];
                    recordMove++;
                    array[i] = array[i + 1];
                    recordMove++;
                    array[i + 1] = array[0];
                    recordMove++;
                }
            }
        }

        //若没有发生交换
        //flag为0,则不会发生下一趟排序
        //将最小的数移到最前
        if (flag_2 == 1)
        {
            flag_2 = 0;
            for (int i = n - 1; i > 16 - n + 1; i--)
            {
                recordCompare++;
                if (array[i] < array[i - 1])
                {
                    flag_2 = 1;
                    array[0] = array[i];
                    recordMove++;
                    array[i] = array[i - 1];
                    recordMove++;
                    array[i - 1] = array[0];
                    recordMove++;
                }
            }
        }
        
        n--;

        cout << "第" << 16 - n << "趟排序:" << endl;
        for (int j = 1; j <= 16; j++)
        {
            if (j == 1) cout << "[ ";
            if (j == n + 1) cout << "[ ";
            cout << array[j] << " ";
            if (j == 16) cout << "]";
            if (j == 16 - n) cout << "] ";
        }
        cout << endl << endl;
    }
    cout << "比较次数:" << recordCompare << endl;
    cout << "移动次数:" << recordMove << endl << endl;
}


int main()
{
    //生成随机16个正整数
    int positiveInteger[17];
    time_t t;
    srand((unsigned)time(&t));
    cout << "生成16个2位正整数:" << endl;
    for (int i = 1; i <= 16; i++)
    {
        positiveInteger[i] = (rand() % (100 - 10)) + 10;
        cout << positiveInteger[i] << " ";
    }
    cout << endl << endl;

    //双向冒泡排序
    DoubleBubbleSort(positiveInteger, 16);

    cout << "排序后数组:" << endl;
    for (int i = 1; i <= 16; i++)
    {
        cout << positiveInteger[i] << " ";
    }
    cout << endl << endl;


    system("pause");
    return 0;
}


运行结果

技术图片


以上是关于双向冒泡排序的主要内容,如果未能解决你的问题,请参考以下文章

经典算法学习——非循环双向链表实现冒泡排序(不带头结点)

鸡尾酒排序Cocktail Sort (双向冒泡排序)

双向冒泡排序

排序算法系列:冒泡排序与双向冒泡排序

C语言之双向冒泡排序

鸡尾酒排序/双向冒泡排序