一、什么是冒泡排序?
冒泡排序(Bubble Sort),是一种计算机科学领域的较简单的排序算法。
它重复地走访过要排序的元素列,依次比较两个相邻的元素,如果他们的顺序(如从大到小、首字母从A到Z)错误就把他们交换过来。走访元素的工作是重复地进行直到没有相邻元素需要交换,也就是说该元素列已经排序完成。
这个算法的名字由来是因为越大的元素会经由交换慢慢“浮”到数列的顶端(升序或降序排列),就如同碳酸饮料中二氧化碳的气泡最终会上浮到顶端一样,故名“冒泡排序”。
上面的大串文字是从百度复制下来的,什么是冒泡排序?个人的理解是:
冒泡,轻的往上浮重的往下沉。亦即小的元素浮到上面来,大的元素沉到下面去。也就是说,冒泡排序要做的事,将一个乱序的元素列,转换成一个有序(从小到大排列)的元素列。
二、冒泡排序的原理
- 比较相邻的元素。如果第一个比第二个大,就交换他们两个。
- 对每一对相邻元素做同样的工作,从开始第一对到结尾的最后一对。在这一点,最后的元素应该会是最大的数。
- 针对所有的元素重复以上的步骤,除了最后一个。
- 持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。
三、举例说明
要是觉得上面的原理粘贴的太官方,可以看一下下面的例子:初始数组 arr [5] = { 8, 6, 6, 9, 3 } ,希望对它进行从小到大排序,利用冒泡排序算法:
1、第一趟排序:未排序的数组为初始数组arr [5] = { 8, 6, 6, 9, 3 },索引cur从0开始,下边界ver为5(这里的ver可以简单理解为数组实际要比较的长度length,cur < ver-1)。cur<4
(1)cur = 0 , arr [cur] = 8 ,由于8>6,故两者调换位置,得到{ 6,8,6,9,3 } :
(2)cur = cur + 1 =1; arr [cur] = 8, 由于8>6,故两者调换位置,得到{ 6,6,8,9,3 } :
(3)cur = cur + 1 =2; arr [cur] = 8, 由于8<9,故两者不需要调换位置,仍是{ 6,6,8,9,3 } :
(4)cur = cur + 1 =4; arr [cur] = 9, 由于9>3,故两者调换位置,得到{ 6,6,8,3,9 } :
经过第一趟的排序,数组 arr[ ] 由 { 8, 6, 6, 9, 3 } 转变成了 { 6,6,8,3,9 } (arr2[ ])。我们将第一个最大的元素沉到了最后面,即arr[4] = 9。
2、第二趟排序:对第一趟的数组 arr2[ ] = { 6,6,8,3,9 }继续进行排序,索引cur从0开始,下边界ver = ver - 1 = 4(为啥是4?因为由上一轮的排序,我们已经得到了arr[ ]中最大的元素arr[4] = 9,而且我们已经将该元素放到了数组的最末尾了,后续的排序也就不用再管它了)。cur<3
(1)cur = 0 , arr [cur] = 6 ,由于6=6,故两者不需要调换位置,仍是{ 6,6,8,3,9 } :
(2)cur = cur + 1 =1; arr [cur] = 6, 由于6<8,故两者不需要调换位置,仍是{ 6,6,8,3,9 } :
(3)cur = cur + 1 =2; arr [cur] = 8, 由于8>3,故两者调换位置,得到{ 6,6,3,8,9 } :
此时,cur = cur + 1 = 3 = ver - 1,第二趟排序结束。或者这样说,由第一趟排序我们已经得到arr[5] = 9是最大的元素,故arr[4]=8就不需要与arr[5]相比较了(此时的arr[4]肯定比arr[5]小啊)。
那么,经过第二趟的排序,数组 arr[ ] 由 { 6,6,8,3,9 } (arr2[ ])转变成了 { 6,6,3,8,9 } (arr3[ ])。我们将第二大的元素,即arr[3] = 8。
3、第三趟排序:对第二趟的数组 arr3[ ] = { 6,6,3,8,9 }继续进行排序,索引cur从0开始,下边界ver = ver - 1 = 3。cur<2
(1)cur = 0 , arr [cur] = 6 ,由于6=6,故两者不需要调换位置,仍是{ 6,6,3,8,9 } :
(2)cur = cur + 1 =1; arr [cur] = 6, 由于6 > 3,故两者调换位置,仍是{ 6,3,6,8,9 } :
此时,cur再+1就要等于ver了,故第三趟排序结束。那么,经过第三趟的排序,数组 arr[ ] 由 { 6,6,3,8,9 } (arr3[ ])转变成了 { 6,3,6,8,9 } (arr4[ ])。我们将第三大的元素,即arr[2] = 6。
4、第四趟排序:对第三趟的数组 arr4[ ] = { 6,3,6,8,9 }继续进行排序,索引cur从0开始,下边界ver = ver - 1 = 2。cur<1,那么,这次就只需要比较cur=0这一次就可以啦!
(1)cur = 0 , arr [cur] = 6 ,由于6>3,故两者调换位置,得到{ 3,6,6,8,9 } :
经过第四趟的排序,数组 arr[ ] 由 { 6,3,6,8,9 } (arr4[ ])转变成了 { 3,6,6,8,9 } (arr5[ ])。我们将第四大的元素,即arr[1] = 6。
经过4次(arr.length - 1)的排序,未排序的只剩a[0]了(绿色的表示已经排哈皮序了的元素),其实也就是说,我们的排序工作,已经完成了。吼吼吼~ 这也是冒泡排序的一个过程了!
亦即
四、Java编码实现
根据上面的思路,实现冒泡排序,编码如下:
public class BubbleSort {
private static void sort(int arr[]){
//这里-1,加不加都没什么说法,不加就都少循环一次没必要的循环吧!
for(int i=0; i<arr.length-1; i++){
//打印只是为了方便看排序过程
System.out.println("----需要排序的数组:"+Arrays.toString(arr)+"----");
//这里-1,是为了防止溢出,不能少
for(int j=0; j<arr.length-i-1; j++){
if(arr[j] > arr[j+1]){
int temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
}
System.out.println((j+1)+"、arr["+j+"]和arr["+(j+1)+"]比较后得到:"+Arrays.toString(arr));
}
System.out.println("----第"+(i+1)+"次排序得到的数组:"+Arrays.toString(arr)+"----");
System.out.println();
}
}
public static void main(String[] args) {
int arr[] = {8,6,6,9,3};
sort(arr);
}
}
输出得到:
----需要排序的数组:[8, 6, 6, 9, 3]----
1、arr[0]和arr[1]比较后得到:[6, 8, 6, 9, 3]
2、arr[1]和arr[2]比较后得到:[6, 6, 8, 9, 3]
3、arr[2]和arr[3]比较后得到:[6, 6, 8, 9, 3]
4、arr[3]和arr[4]比较后得到:[6, 6, 8, 3, 9]
----第1次排序得到的数组:[6, 6, 8, 3, 9]--------需要排序的数组:[6, 6, 8, 3, 9]----
1、arr[0]和arr[1]比较后得到:[6, 6, 8, 3, 9]
2、arr[1]和arr[2]比较后得到:[6, 6, 8, 3, 9]
3、arr[2]和arr[3]比较后得到:[6, 6, 3, 8, 9]
----第2次排序得到的数组:[6, 6, 3, 8, 9]--------需要排序的数组:[6, 6, 3, 8, 9]----
1、arr[0]和arr[1]比较后得到:[6, 6, 3, 8, 9]
2、arr[1]和arr[2]比较后得到:[6, 3, 6, 8, 9]
----第3次排序得到的数组:[6, 3, 6, 8, 9]--------需要排序的数组:[6, 3, 6, 8, 9]----
1、arr[0]和arr[1]比较后得到:[3, 6, 6, 8, 9]
----第4次排序得到的数组:[3, 6, 6, 8, 9]----
//如果第一个for循环是i<arr.length,即不 -1的话,输出结果后面还有:
//----需要排序的数组:[3, 6, 6, 8, 9]----
//----第5次排序得到的数组:[3, 6, 6, 8, 9]----
五、冒泡排序算法的优化(1)
为什么要优化?怎么优化?看看类似这样的例子:arry[ ] = {1,2,3,5,4 },很显然,这个数组只需要将5和4调换就可以了。但如果使用上面的方法,进行的过程如下,后面那几次的排序,就显得多余了。那么,该怎么优化呢?
----需要排序的数组:[1, 2, 3, 5, 4]----
1、arr[0]和arr[1]比较后得到:[1, 2, 3, 5, 4]
2、arr[1]和arr[2]比较后得到:[1, 2, 3, 5, 4]
3、arr[2]和arr[3]比较后得到:[1, 2, 3, 5, 4]
4、arr[3]和arr[4]比较后得到:[1, 2, 3, 4, 5]
----第1次排序得到的数组:[1, 2, 3, 4, 5]---- 到这已经有结果了!----需要排序的数组:[1, 2, 3, 4, 5]----
1、arr[0]和arr[1]比较后得到:[1, 2, 3, 4, 5]
2、arr[1]和arr[2]比较后得到:[1, 2, 3, 4, 5]
3、arr[2]和arr[3]比较后得到:[1, 2, 3, 4, 5]
----第2次排序得到的数组:[1, 2, 3, 4, 5]--------需要排序的数组:[1, 2, 3, 4, 5]---- 这儿是不是显得多余了?
1、arr[0]和arr[1]比较后得到:[1, 2, 3, 4, 5]
2、arr[1]和arr[2]比较后得到:[1, 2, 3, 4, 5]
----第3次排序得到的数组:[1, 2, 3, 4, 5]--------需要排序的数组:[1, 2, 3, 4, 5]---- 这儿是不是显得多余了?
1、arr[0]和arr[1]比较后得到:[1, 2, 3, 4, 5]
----第4次排序得到的数组:[1, 2, 3, 4, 5]----
针对类似 {1,2,3,5,4 }这种提早完成排序的情况,我们的优化思路是:给排序了的数组进行是否已经有序的判断,如果数组在 array.length-1次 排序前已经提早成为了有序数组,那我们也提早将后面的循环break掉!直接上代码吧:
public class BubbleSort {
private static void sort2(int arr[]){
for(int i = 0; i < arr.length-1; i++){
//只是为了方便看过程,而做的打印
System.out.println("----需要排序的数组:"+Arrays.toString(arr)+"----");
//有序标记 默认有序
boolean isSorted = true;
for(int j = 0; j < arr.length-i-1; j++){
if(arr[j] > arr[j+1]){
//有元素交换,为无序,标记变为false
isSorted = false;
int temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
}
System.out.println((j+1)+"、arr["+j+"]和arr["+(j+1)+"]比较后得到:"+Arrays.toString(arr));
}
System.out.println("----第"+(i+1)+"次排序得到的数组:"+Arrays.toString(arr)+"----");
System.out.println();
if(isSorted) //isSorted为true时,数组已有序,结束循环
break;
}
}
public static void main(String[] args) {
int arr[] = {1,2,3,5,4};
sort2(arr);
}
}
运行结果:
----需要排序的数组:[1, 2, 3, 5, 4]----
1、arr[0]和arr[1]比较后得到:[1, 2, 3, 5, 4]
2、arr[1]和arr[2]比较后得到:[1, 2, 3, 5, 4]
3、arr[2]和arr[3]比较后得到:[1, 2, 3, 5, 4]
4、arr[3]和arr[4]比较后得到:[1, 2, 3, 4, 5]
----第1次排序得到的数组:[1, 2, 3, 4, 5]---- 到这已经有结果了!但它还不知道这是结果----需要排序的数组:[1, 2, 3, 4, 5]----
1、arr[0]和arr[1]比较后得到:[1, 2, 3, 4, 5]
2、arr[1]和arr[2]比较后得到:[1, 2, 3, 4, 5]
3、arr[2]和arr[3]比较后得到:[1, 2, 3, 4, 5]
----第2次排序得到的数组:[1, 2, 3, 4, 5]---- 所以这里还会进行一次,两次都相等了,后面的还不等嘛!
六、冒泡排序算法的优化(2)
这里再列一种情况,就是无序列的前半部分无序,后半部分有序,如 arr[ ] = {2,3,1,4,5,6,7,8}。这种情况下,其实后半部分的元素我们没必要再反反复复的去比较了。下方的运行结果是用 五、优化方法(1) 的代码运行的结果。拿此运行结果与再次优化后的运行结果对比,就可以很明显看出差别了,这里就不多说了。
----需要排序的数组:[2, 3, 1, 4, 5, 6, 7, 8]----
1、arr[0]和arr[1]比较后得到:[2, 3, 1, 4, 5, 6, 7, 8]
2、arr[1]和arr[2]比较后得到:[2, 1, 3, 4, 5, 6, 7, 8]
3、arr[2]和arr[3]比较后得到:[2, 1, 3, 4, 5, 6, 7, 8]
4、arr[3]和arr[4]比较后得到:[2, 1, 3, 4, 5, 6, 7, 8]
5、arr[4]和arr[5]比较后得到:[2, 1, 3, 4, 5, 6, 7, 8]
6、arr[5]和arr[6]比较后得到:[2, 1, 3, 4, 5, 6, 7, 8]
7、arr[6]和arr[7]比较后得到:[2, 1, 3, 4, 5, 6, 7, 8]
----第1次排序得到的数组:[2, 1, 3, 4, 5, 6, 7, 8]----
----需要排序的数组:[2, 1, 3, 4, 5, 6, 7, 8]----
1、arr[0]和arr[1]比较后得到:[1, 2, 3, 4, 5, 6, 7, 8]
2、arr[1]和arr[2]比较后得到:[1, 2, 3, 4, 5, 6, 7, 8]
3、arr[2]和arr[3]比较后得到:[1, 2, 3, 4, 5, 6, 7, 8]
4、arr[3]和arr[4]比较后得到:[1, 2, 3, 4, 5, 6, 7, 8]
5、arr[4]和arr[5]比较后得到:[1, 2, 3, 4, 5, 6, 7, 8]
6、arr[5]和arr[6]比较后得到:[1, 2, 3, 4, 5, 6, 7, 8]
----第2次排序得到的数组:[1, 2, 3, 4, 5, 6, 7, 8]----
----需要排序的数组:[1, 2, 3, 4, 5, 6, 7, 8]----
1、arr[0]和arr[1]比较后得到:[1, 2, 3, 4, 5, 6, 7, 8]
2、arr[1]和arr[2]比较后得到:[1, 2, 3, 4, 5, 6, 7, 8]
3、arr[2]和arr[3]比较后得到:[1, 2, 3, 4, 5, 6, 7, 8]
4、arr[3]和arr[4]比较后得到:[1, 2, 3, 4, 5, 6, 7, 8]
5、arr[4]和arr[5]比较后得到:[1, 2, 3, 4, 5, 6, 7, 8]
----第3次排序得到的数组:[1, 2, 3, 4, 5, 6, 7, 8]----
针对无序列的前半部分无序,后半部分有序的情况,做出如下优化:
public class BubbleSort {
private static void sort3(int arr[]){
//最后一次交换的位置
int lastSvop = 0;
//无序数列的边界,每次比较只需要比到ver为止.
//第一次比较的边界是ver=arr.length-1,即比较到最后一个元素
int ver = arr.length - 1;
for(int i = 0; i < arr.length-1; i++){
//只是为了方便看过程,而做的打印
System.out.println("----需要排序的数组:"+Arrays.toString(arr)+"----");
//有序标记
boolean isSorted = true;
for(int j = 0; j < ver; j++){
if(arr[j] > arr[j+1]){
//有元素交换,即为无序,false
isSorted = false;
int temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
//把无序数列的边界更新为最后一次交换元素的位置
lastSvop = j;
}
System.out.println((j+1)+"、arr["+j+"]和arr["+(j+1)+"]比较后得到:"+Arrays.toString(arr));
}
System.out.println("----第"+(i+1)+"次排序得到的数组:"+Arrays.toString(arr)+"...最后一次交换元素的位置:"+lastSvop);
System.out.println();
ver = lastSvop;
if(isSorted)
break;
}
}
public static void main(String[] args) {
int arr[] = {2,3,1,4,5,6,7,8};
sort3(arr);
}
}
运行结果:
----需要排序的数组:[2, 3, 1, 4, 5, 6, 7, 8]----
1、arr[0]和arr[1]比较后得到:[2, 3, 1, 4, 5, 6, 7, 8]
2、arr[1]和arr[2]比较后得到:[2, 1, 3, 4, 5, 6, 7, 8]
3、arr[2]和arr[3]比较后得到:[2, 1, 3, 4, 5, 6, 7, 8]
4、arr[3]和arr[4]比较后得到:[2, 1, 3, 4, 5, 6, 7, 8]
5、arr[4]和arr[5]比较后得到:[2, 1, 3, 4, 5, 6, 7, 8]
6、arr[5]和arr[6]比较后得到:[2, 1, 3, 4, 5, 6, 7, 8]
7、arr[6]和arr[7]比较后得到:[2, 1, 3, 4, 5, 6, 7, 8]
----第1次排序得到的数组:[2, 1, 3, 4, 5, 6, 7, 8]...最后一次交换元素的位置:1----需要排序的数组:[2, 1, 3, 4, 5, 6, 7, 8]----
1、arr[0]和arr[1]比较后得到:[1, 2, 3, 4, 5, 6, 7, 8]
----第2次排序得到的数组:[1, 2, 3, 4, 5, 6, 7, 8]...最后一次交换元素的位置:0----需要排序的数组:[1, 2, 3, 4, 5, 6, 7, 8]----
----第3次排序得到的数组:[1, 2, 3, 4, 5, 6, 7, 8]...最后一次交换元素的位置:0
此文借鉴 https://blog.youkuaiyun.com/wubingju93123/article/details/81215984