冒泡排序及优化

Posted 趣味学习吧

tags:

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

排序算法中,普遍最先学的都是冒泡排序。

基本原理:

序列:A1,A2,A3.....A(n-1),An

1,比较A1和A2,如果A1>A2,交换位置。此时,A1在第二个位置上,然后比较A1和A3,

如果A1>A3,交换位置,依次类推,直至最后一共比较了n-1次,最大的数就会被排到最后。

2,然后,对1到n-1位置上的数字再进行步骤1中的两两比较排序,则比较n-2次后会把第二大的数字排在倒数第二的位置。以此类推,直至没有元素交换结束。

[10987654321]为例:

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

到此为止,不管原始数组是不是已经排序完成,冒泡排序进行完都需要比较[ n(n-1) ] / 2次。

此时他的时间复杂度则是O(n^2)

但是我们可以对此算法进行优化。

优化一:如果在比较过程中排序已经完成,则可以立即终止不必要的循环。那怎么判断排序完成了呢。很简单,只需要判断一下上一次内循环中有没有交换过元素就可以了,因为如果没有交换,就说明后面的数字总是比前一个数字大。所以,代码中加一个是否交换的标志即可。

代码如下:

let arr1 = [1, 2, 3, 4, 5, 6, 7, 8, 9];function sort1(array) { var length = array.length; for (var i = 0; i < length - 1; i++) { //内循环开始前,交换标志改成false        var isSwitch = false; for (var j = 0; j < length - 1 - i; j++) { if (array[j] > array[j + 1]) { var temp = array[j + 1]; array[j + 1] = array[j]; array[j] = temp; //如果发生了交换,交换标志改成true isSwitch = true; } } //没有发生交换,则终止下一次循环 if(!isSwitch){ break; } }}

此时,如上arr1原始数组就是升序,则内循环只需要一趟,也就是说只需要比较n-1次,就结束了。

优化二: 思考一个问题,例如:[3, 2, 1, 4, 5, 6] 这样一个数组,进行冒泡排序,第一个步骤时,会交换几次,是不是就是3和2交换,然后,3和1交换,因为后面的数字都比3大,而且后面4,5,6已经是有序的了,如果按部就班的往下走,代码中的第二次内循环的结果时把5排到倒数第二的位置,第三次内循环的结果是把4放到倒数第三的位置。可是,4,5本来就是有序的,所以可以想办法省下这两次循环,直接对前面的数字排序。那怎么判断后面的的数字是不是已经有序了呢,只需要记录上一次循环,最后交换的位置即可。

function sort2(array) { var length = array.length; //第一次内循环最后比较的位置设为n-1,因为第一次循环还是要比较完的。 var lastSwitchIndex = length - 1; for (var i = 0; i < length - 1; i++) { //内循环开始前,交换标志改成false isSwitch = false; var tempLastIndex = 0; for (var j = 0; j < lastSwitchIndex; j++) { if (array[j] > array[j + 1]) { var temp = array[j + 1]; array[j + 1] = array[j]; array[j] = temp; //如果发生了交换,交换标志改成true isSwitch = true; //记录最后一次比较的位置,下次循环就只需要比较到此位置即可,因为后面的元素已经排序完成。 tempLastIndex = j; } } //没有发生交换,则终止下一次循环 if (!isSwitch) { break; } lastSwitchIndex = tempLastIndex; }}

优化三:一般情况下有上面两次优化已经可以了,还有一种是双向冒泡,相当于外层一次循环,内层通过两次循环,确定一个最大值,一个最小值。然后再对中间剩余的元素继续上述操作。在上述优化的基础上进行。代码如下

function sort3(array) { var length = array.length; //第一次内循环最后比较的位置设为n-1,因为第一次循环还是要比较完的。 var lastSwitchIndex = length - 1; //内循环开始的位置 var startIndex = 0; for (var i = 0; i < length - 1; i++) {
//内循环开始前,交换标志改成false isSwitch = false; var tempLastIndex = 0; for (var j = startIndex; j < lastSwitchIndex; j++) { if (array[j] > array[j + 1]) { var temp = array[j + 1]; array[j + 1] = array[j]; array[j] = temp; //如果发生了交换,交换标志改成true isSwitch = true; //记录最后一次比较的位置,下次循环就只需要比较到此位置即可,因为后面的元素已经排序完成。 tempLastIndex = j; } } //没有发生交换,则终止下一次循环 if (!isSwitch) { break; }
//倒序比较,把最小值排到第一位去 for (var j = tempLastIndex; j > startIndex; j--) { if (array[j - 1] > array[j]) { var temp = array[j - 1]; array[j - 1] = array[j]; array[j] = temp; isSwitch = true; } } //没有发生交换,则终止下一次循环 if (!isSwitch) { break; } //如果还有交换发生,游标开始位置+1,开启下一次循环 startIndex++; //记录最后一次交换的位置 lastSwitchIndex = tempLastIndex; }}

也可以用while循环代替外层for循环,如下:

function sort3(array) { var length = array.length; //第一次内循环最后比较的位置设为n-1,因为第一次循环还是要比较完的。 var lastSwitchIndex = length - 1; //内循环开始的位置 var startIndex = 0; while (startIndex < lastSwitchIndex) { //内循环开始前,交换标志改成false isSwitch = false; var tempLastIndex = 0; for (var j = startIndex; j < lastSwitchIndex; j++) { if (array[j] > array[j + 1]) { var temp = array[j + 1]; array[j + 1] = array[j]; array[j] = temp; //如果发生了交换,交换标志改成true isSwitch = true; //记录最后一次比较的位置,下次循环就只需要比较到此位置即可,因为后面的元素已经排序完成。 tempLastIndex = j; } } //没有发生交换,则终止下一次循环 if (!isSwitch) { break; }
//倒序比较,把最小值排到第一位去 for (var j = tempLastIndex; j > startIndex; j--) { if (array[j - 1] > array[j]) { var temp = array[j - 1]; array[j - 1] = array[j]; array[j] = temp; isSwitch = true; } } //没有发生交换,则终止下一次循环 if (!isSwitch) { break; } //如果还有交换发生,游标开始位置+1,开启下一次循环 startIndex++; //记录最后一次交换的位置 lastSwitchIndex = tempLastIndex; }}

上述所有优化,都是为了减少比较次数或者循环次数,但是如果数组本完全是逆序,如:[10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0],则比较次数并不会减少。(可以通过代码中比较的位置,打印证明)

冒泡排序过程用用到的辅助变量不会随着比较的规模而改变,则它的空间复杂度是O(1);

排序中还有一个稳定性:通过排序过程中数值相同的元素经过排序后会不会交换位置来区分。因为冒泡排序过程中,只有前者大于后者才会交换位置,两个相同的元素不会交换位置,所以是稳定的。

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

C#冒泡排序法及优化

Java_冒泡排序_原理及优化

排序算法冒泡选择排序的Python实现及算法优化详解

java冒泡排序及优化

冒泡排序及优化详解

挖掘算法中的数据结构:O(n^2)排序算法之 选择插入冒泡希尔排序 及 优化