• 冒泡排序和鸡尾酒排序的代码分析


    冒泡排序

      冒泡排序(buble sort)是一个比较入门的排序算法。顾名思义,它根据将最大(或最小)的数依次冒泡从而实现排序。

      如下图所示,白色部分为待排序数组,红色部分为已找出的“较大的”数,每次迭代只需从白色部分找出其中最大的数字,直至找出n-1个“较大的”数后,数组已排序。

      注:找出n-1个“较大的”数即可,因为最后一个必定为最小的数。

    代码:

    var s = [8, 7, 6, 5, 4, 3, 2, 1];
    var bubleSort = function(array) {
        var temp;
        for(var i=0; i < array.length-1; i++) {    //外层循环7次
            for(var j=0; j < array.length - i - 1; j++) {  //已有i个较大数被冒泡,比较次数为n-i-1
                if(array[j] > array[j+1]) {
                    temp = array[j];
                    array[j] = array[j+1];
                    array[j+1] = temp;
                }
            }
        }
    };
    bubleSort(s);
    console.log(s);
    //=>[ 1, 2, 3, 4, 5, 6, 7, 8 ]

    代码分析:采用嵌套的for循环实现。其中外层循环控制找出”较大的“数的个数(n - 1);内层循环控制找出第i个”较大的“数需要比较的次数(n - 1 - i),因为已有i个”较大的“数被冒泡,因此越到后面需要比较的次数就越少。

    循环不变式:

      第 1 次循环结束时,最后一个为最大数;

      第 i 次循环结束时,s[n - i, n - 1]为按序排列的"较大"数;

      ...

      第 n 次循环结束时,s[1,n - 1]为按序排列的"较大"数,而s[0]必定为最小数,因此整个数组已按序排列。

    添加一行代码在控制台观察程序过程:

    var s = [8, 7, 6, 5, 4, 3, 2, 1];
    var bubleSort = function(array) {
        var temp;
        for(var i=0; i < array.length-1; i++) {
            for(var j=0; j < array.length - i - 1; j++) {
                if(array[j] > array[j+1]) {
                    temp = array[j];
                    array[j] = array[j+1];
                    array[j+1] = temp;
                }
            }
            console.log(array);
        }
    };
    bubleSort(s);
    console.log(s);
    //=>[ 7, 6, 5, 4, 3, 2, 1, 8 ]
        [ 6, 5, 4, 3, 2, 1, 7, 8 ]
        [ 5, 4, 3, 2, 1, 6, 7, 8 ]
        [ 4, 3, 2, 1, 5, 6, 7, 8 ]
        [ 3, 2, 1, 4, 5, 6, 7, 8 ]
        [ 2, 1, 3, 4, 5, 6, 7, 8 ]
        [ 1, 2, 3, 4, 5, 6, 7, 8 ]
        [ 1, 2, 3, 4, 5, 6, 7, 8 ]

    鸡尾酒排序

      鸡尾酒排序(cocktail sort)对冒泡排序进行了优化,使得外层循环一次能找出两个已排序的数(最大和最小),可以理解为”双向“的冒泡排序。

      注:因为鸡尾酒排序外层循环一次能找出两个排序数,故其外层循环次数折半,而内层循环则为两个并列的for循环(分别控制正向和反向)。总的来说,鸡尾酒排序大多数情况下要比冒泡排序效率高。

    代码:

    var s = [8, 7, 6, 5, 4, 3, 2, 1];
    var cocktailSort = function(array) {
        var count = 0; 
        var temp;
        for(var i = 0; i < array.length/2; i++) {  //外层循环次数折半
            for(var j = count; j < array.length - count -1; j++) {  //一次正向循环
                if(array[j] > array[j+1]) {
                    temp = array[j];
                    array[j] = array[j+1];
                    array[j+1] = temp;
                }
            }
            count++;    //记录已找出"较大"数个数
            for(var k = array.length - 1 - count; k > count - 1; k--) {  //一次反向循环
                if(array[k] < array[k-1]) {
                    temp = array[k];
                    array[k] = array[k-1];
                    array[k-1] = temp;
                }
            }
        }
    };
    cocktailSort(s);
    console.log(s);
    //=>[ 1, 2, 3, 4, 5, 6, 7, 8 ]

    代码分析:外层循环次数折半,因为外层循环一次内层已包含一次往返;内层正向循环找出"较大"数,其中起点为count(当前找出"较大"数个数);内层反向循环的起点为n - 1 - count(该轮"较大"数的前一位),终点为count - 1(当前找出的"较小"数个数)。

    循环不变式:

      第 1 次循环结束时:第n个数为最大数,第1个数为最小数;

      第 i 次循环结束时:s[n-i ,n-1]为按序排列的"较大"数,s[0, i-1]为按序排列的"较小"数;

      ...

      第 n 次循环结束时: s[n - n/2, n-1]为按序排列的"较大数",s[0, n/2-1]为按序排列的"较小"数,故整个数组已按序排列。

    在控制台观察输出:

    var s = [8, 7, 6, 5, 4, 3, 2, 1];
    var cocktailSort = function(array) {
        var count = 0; 
        var temp;
        for(var i = 0; i < array.length/2; i++) {
            for(var j = count; j < array.length - count -1; j++) {
                if(array[j] > array[j+1]) {
                    temp = array[j];
                    array[j] = array[j+1];
                    array[j+1] = temp;
                }
            }
            count++;
            for(var k = array.length - 1 - count; k > count - 1; k--) {
                if(array[k] < array[k-1]) {
                    temp = array[k];
                    array[k] = array[k-1];
                    array[k-1] = temp;
                }
            }
            console.log(array);
        }
    };
    cocktailSort(s);
    console.log(s);
    //=>[ 1, 7, 6, 5, 4, 3, 2, 8 ]
        [ 1, 2, 6, 5, 4, 3, 7, 8 ]
        [ 1, 2, 3, 5, 4, 6, 7, 8 ]
        [ 1, 2, 3, 4, 5, 6, 7, 8 ]
        [ 1, 2, 3, 4, 5, 6, 7, 8 ]

    但是,当遇到已按序排列的数组(如:[1, 2, 3, 4, 5, 6, 7, 8])时,冒泡排序和鸡尾酒排序的结果过程均会做很多"无用功"。可以在循环内部定义一个flag,当某一轮循环数组元素不再发生交换时,便可认为数组已按序排列:

    var s = [1,2,3,4,5,8,6,7];
    var cocktailSort = function(array) {
        var count = 0; 
        var temp;
        for(var i = 0; i < array.length/2; i++) {
            var flag = false;  //定义flag
            for(var j = count; j < array.length - count -1; j++) {
                if(array[j] > array[j+1]) {
                    temp = array[j];
                    array[j] = array[j+1];
                    array[j+1] = temp;
                    flag = true;  //当发生交换时,改变flag的值为true
                }
            }
            count++;
            for(var k = array.length - 1 - count; k > count - 1; k--) {
                if(array[k] < array[k-1]) {
                    temp = array[k];
                    array[k] = array[k-1];
                    array[k-1] = temp;
                    flag = true;  //当发生交换时,改变flag的值true
                }
            }
            console.log(array);
            if(!flag)
                break;
        }
    };
    cocktailSort(s);
    //=>[ 1, 2, 3, 4, 5, 6, 7, 8 ]  //输出结果显示,算法会提前结束
        [ 1, 2, 3, 4, 5, 6, 7, 8 ]

    --------------------------------------------------------------------------------------------------

    注:本文为个人学习随笔,仅供个人学习使用,如有雷同,敬请原谅!

  • 相关阅读:
    nginx限流方案的实现(三种方式)
    Pthreads并行编程之spin lock与mutex性能对比分析(转)
    C/C++中float和double的存储结构(转)
    list_entry(ptr, type, member)——知道结构体内某一成员变量地址,求结构体地址
    stderr和stdout详细解说(转)
    结构体内存分配理解
    C中的C文件与h文件辨析(转)
    访问vector元素方法的效率比较(转)
    linux c中select使用技巧——计时器(转)
    thread::id
  • 原文地址:https://www.cnblogs.com/alicell/p/9073977.html
Copyright © 2020-2023  润新知