冒泡排序(Bubble Sort)是基本的交换排序算法之一
-
定义:通过相邻之间的元素比较大小,进行交换。
-
基本思想:若前一个比后一个大,则交换,第一趟会把最大的放到最后。
-
时间复杂度:O(n^2)
-
稳定性:稳定(若元素相同,稳定即表示通过排序之后获得的数组中,相同元素的位置不会发生改变)
1. 实现(下面分别用C语言,Java,和JavaScript 实现)
(1)C语言
#include<stdio.h>
int main()
{
int i,j;
int a[10] = {34,65,2,43,54,64,23,49,78,89};
for(i=0;i<10-1;i++){
for(j = 0;j<10-i-1;j++){
if(a[j]>a[j+1]){
int temp = a[j];
a[j] = a[j+1];
a[j+1] = temp;
}
}
}
for(i=0;i<10;i++)
{
printf("%5d",a[i]);
}
printf("\n");
return 0;
}
运行结果如下
(2)Java
public class Bubble_Sort {
public static void main(String[] args){
int i,j;
int [] a = {34,65,2,43,54,64,23,49,78,89};
for(i=0;i<10-1;i++){
for(j = 0;j<10-i-1;j++){
if(a[j]>a[j+1]){
int temp = a[j];
a[j] = a[j+1];
a[j+1] = temp;
}
}
}
for(i=0;i<10;i++)
{
System.out.print(a[i]+" ");
}
}
}
运行结果如下
(3)JavaScript
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<script>
var i,j;
var a = [34,65,2,43,54,64,23,49,78,89];
for(i=0;i<a.length-1;i++){
for(j = 0;j<a.length-i-1;j++){
if(a[j]>a[j+1]){
var temp = a[j];
a[j] = a[j+1];
a[j+1] = temp;
}
}
}
console.log(a);
</script>
</body>
</html>
运行结果如下:
2. 优化(3种优化,JavaScript实现)
(1)设置标志位(适用于连片有序,但整体无序数据)
在排序过程中,如果有一趟没有进行交换,则表示已完成排序,即不必继续比较后面的元素,所以我们在交换处设置flag标志位,若flag值没有发生变化,则说明排序完成,跳出循环。
上面的js代码若每一趟输出数组a,则结果如下:
进行了9次排序。
优化代码如下(js)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<script>
var i,j,flag = 0;
var a = [34,65,2,43,54,64,23,49,78,89];
for(i=0;i<a.length-1;i++){
flag = 0;
for(j = 0;j<a.length-i-1;j++){
if(a[j]>a[j+1]){
flag = 1;
var temp = a[j];
a[j] = a[j+1];
a[j+1] = temp;
}
}
if(flag == 0){//表示这一趟没有进行交换,即排序完成
break;
}
console.log(a);
}
</script>
</body>
</html>
优化之后进行了5次排序,运行结果如下:
(2)每次交换记录最后一次的位置(适用于前面无序,但是后面有序)
记录每趟交换的最后位置作为下趟比较的结束位置,到0时停止。因为没有交换,说明后面的数据已经有序,所以不做无效比较,下次直接比较到上次交换的最后位置即可。
一般的冒泡排序比较次数都是逐次少1,如图:
数字为每趟的比较次数,数组a为每次的排序结果
但是优化会减少它的比较次数(代码及结果如下)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<script>
var i,j,end,t,c = 0;
var a = [34,65,2,43,54,64,23,49,78,89];
end = a.length-1;
for(i=0;i<a.length-1;i++){
t = 0;
c = 0;//记录比较次数,看是否优化
for(j = 0;j<end;j++){
c++;
if(a[j]>a[j+1]){
t = j;//记录比较的位置
var temp = a[j];
a[j] = a[j+1];
a[j+1] = temp;
}
}
console.log(c);
end = t;//把比较的最后位置作为下次比较的结束位置
console.log(a);
if(t == 0){
break;
}
}
</script>
</body>
</html>
(3)双向冒泡排序
每次既从左往右找最大值,又从右往左找最小值,把最大值放最后,最小值放前面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<script>
var a = [34,65,2,43,54,64,23,49,78,89];
var i,j,end,t,c = 0,flag;
var left = 0;//左边界的初始值
var right = a.length-1;//右边界的初始值
var before = 0,after = 0;//记录左右边界的最后一次位置
for(i=0;i<a.length-1;i++){
flag = 0;c = 0;
//正向寻找最大值
for(j = left;j<right;j++){
if(a[j]>a[j+1]){
c++;
after = j;//记录比较的位置
var temp = a[j];
a[j] = a[j+1];
a[j+1] = temp;
flag = 1;
}
}
//反向寻找最小值
for(j = right;j>left;j--){
if(a[j]<a[j+1]){
c++;
before = j;//记录比较的位置
var temp = a[j];
a[j] = a[j+1];
a[j+1] = temp;
flag = 1;
}
}
if(flag == 0){
break;
}
left = before;
right = after;
console.log(c);
console.log(a);
}
</script>
</body>
</html>
运行结果如下:(数字表示比较次数,数组为每趟比较结果)
最后说一下稳定性。
一般来说,冒泡排序是稳定的,但是一个排序的稳定性主要还是由具体算法决定,不稳定的算法在某种条件下可以变为稳定的算法,而稳定的算法在某种条件下也可以变为不稳定的算法。比如今天所分享的冒泡排序,将元素交换的条件改成a[j]>=a[j+1],则两个相等的元素就会交换位置,即不稳定。