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

冒泡排序

  冒泡排序(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 ]

 

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

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

 

posted @ 2018-05-22 20:41  黎志文  阅读(510)  评论(1编辑  收藏  举报