冒泡排序及优化
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次后会把第二大的数字排在倒数第二的位置。以此类推,直至没有元素交换结束。
以[10, 9, 8, 7, 6, 5, 4, 3, 2, 1]为例:
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);
排序中还有一个稳定性:通过排序过程中数值相同的元素经过排序后会不会交换位置来区分。因为冒泡排序过程中,只有前者大于后者才会交换位置,两个相同的元素不会交换位置,所以是稳定的。
以上是关于冒泡排序及优化的主要内容,如果未能解决你的问题,请参考以下文章