目录
1.冒泡排序算法:
原理:以N个元素数组的升序为例。相邻元素两两比较,大的往后放,第一轮完毕,最大值出现在了最大索引处,即数组最右侧。每一轮都从头开始执行,总共执行 N-1 轮。
代码书写分析:需要两层循环。第一层表示轮次,从第一个元素开始,相邻元素两两比较,大的往后放。每遍历一轮,进入第二层循环,在此从头开始,确定最大值后放在最大索引处,每遍历一轮最大索引值 -1。直到排序完成。
public class BubbleSort {
public static void swap(int[] arr,int i,int j) {
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
/**
* 冒泡排序:
* isAsc:true 升序 ;false 降序
* @param arr
* @param isAsc
*/
public static void bubbleSort(int[] arr,boolean isAsc) {
for(int i=0;i<arr.length-1;i++) { //轮次:数组长度减一
for(int j=0;j<arr.length-i-1;j++) { //遍历数组索引(第一次遍历到倒数第二个元素,之后每轮减一)
if(isAsc == true) {
if(arr[j]<arr[j+1]) { //相邻元素对比,符合规则交换位置
swap(arr,j,j+1);
}
}else {
if(arr[j]>arr[j+1]) {
swap(arr,j,j+1);
}
}
}
}
}
}
2.算法优化分析:
2.1优化外层循环
正常情况下,冒泡排序算法的外层循环需要进行N-1轮,但如果在某一轮结束后数组排序完成,这个时候就可以提前退出外层循环,这就是冒泡排序外层循环优化的基本思路,如下所示:
原始数组 2 1 3 5 6 4
一轮循环 1 2 3 5 4 6
二轮循环 1 2 3 4 5 6
正常情况下,数组需要进行5轮外层循环,但两轮就已经排序完成。因此可以在第三轮未发生交换的情况下,设置一个标志。作为结束外层循环的标志。
public class BubbleSort_Opt {
public static void swap(int[] arr,int i,int j) {
int temp = 0;
temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
public static void bubbleSort(int[] arr) {
for(int i=0 ; i<arr.length-1 ; i++) {
int flag = 0; //设置循环结束标志
for(int j=arr.length-1 ; j>0 ; j--) {
//以升序为例
if(arr[j]<arr[j-1]) {
swap(arr,j,j-1);
flag = 1; //发生交换则对标志变量重新赋值
}
}
//内层循环完成后,判断上一轮是否发生元素交换。未交换元素,则表示排序完成,提前退出。
if(flag == 0) {
return;
}
}
}
}
2.2优化内层循环
冒泡排序算法是相邻元素两两比较,经过一轮就可以将最大的元素防止数组尾部。但是如果相邻元素有序的话则不发生交换。因此在最后交换位置前面为无序区,需要继续循环比较;而最后交换位置后面则为有序区,下一轮可以只遍历无序区即可。因此可以将最后交换位置的索引值赋给一个变量,作为内层循环结束的标志,这样可以提前结束内层循环。具体原理如下所示:
原始数组 3 2 1 4 6 7
一轮交换 2 1 3 4 6 7
二轮交换 1 2 3 4 6 7
public class BubbleSort_Opt {
public static void swap(int[] arr,int i,int j) {
int temp = 0;
temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
public static void bubbleSort(int[] arr) {
int pos = 0;
int temp = arr.length-1;
for(int i=0 ; i<arr.length-1 ; i++) {
for(int j=0 ; j<temp ; j++) {
//以升序为例
if(arr[j]>arr[j+1]) {
swap(arr,j,j+1);
pos = j ; //记录最后的交换位置,换分有序区与无序区
}
}
temp = pos; //将最后的交换位置作为内层循环结束标志
}
}
}
2.3同时优化内层和外层循环
public class BubbleSort_Opt {
public static void swap(int[] arr,int i,int j) {
int temp = 0;
temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
public static void bubbleSort(int[] arr) {
int pos = 0;
int temp = arr.length-1;
for(int i=0 ; i<arr.length-1 ; i++) {
int flag = 0; //设置外层循环结束标志
for(int j=0 ; j<temp ; j++) {
//以升序为例
if(arr[j]>arr[j+1]) {
swap(arr,j,j+1);
flag = 1; //发生交换则对标志变量重新赋值
pos = j ;
}
}
//将最后交换位置作为内层循环结束的标志
temp = pos;
//内层循环完成后,判断上一轮是否发生元素交换。未交换元素,则表示排序完成,提前退出外层循环。
if(flag == 0) {
return;
}
}
}
}
3.时间复杂度分析:
3.1优化前
对于元素个数为N的数组:
1.第一轮从第一个元素开始,相邻元素进行对比,需要比较 (N-1) 次,考虑均交换的情况是 3(N-1),之后最值放在最大索引处,同时维护最大索引值(最大索引值-1);
2.第二轮从也第一个元素开始,相邻元素进行对比,需要比较 (N-2) 次,考虑均交换的情况是 3(N-2),之后最值放在最大索引处,同时维护最大索引值(最大索引值-1);
......
3.总共循环 (N-1)轮
注:复杂度与数组是否有序无关,每轮都需要遍历才能找到最大/最小值,最好,最坏或平均时间复杂度均为O(N^2)
3.2优化后
对于近乎有序的数组,优化后的冒泡排序会提前结束循环。因此它的时间复杂度会降低很多。
1.对于有序数组,它在进行一轮遍历后就会提前结束,最佳的时间复杂度为O(N);
2.对于逆序数组,它需要进行N-1轮的遍历,因此最差的时间复杂度为O(N^2);
3.平均的时间复杂度为O(N^2);
4.空间复杂度分析:
冒泡排序属于原位排序,无论是否进行优化,其需要的临时变量都在个位数以内。因此冒泡排序算法的空间复杂度为 O(1)