数据结构与算法——Java排序算法之冒泡排序及优化(配详细注释)
排序算法是我们在数据结构与算法中必学掌握的重点,下面着重以代码的方式复习一下冒泡排序。
- 普通的冒泡排序
//普通的冒泡排序:比较相邻元素之间的大小,如果发现当前顺序与所期望的顺序不一致则进行交换
public static void bubbleSort(int[] arrs) {
//接收数组长度
int length = arrs.length;
//定义一个临时变量用于交换
int temp;
//for循环遍历数组元素,外层为交换的轮数,内循环为每轮比较的次数
for (int i = 0; i < length - 1; i++) {
for (int j = 0; j < length - 1 - i; j++) {
//判断当前数如果大于其后面的数时进行交换(因为我们的顺序是从小到大,只有当前数小于等于后一个数时不用交换)
if (arrs[j] > arrs[j + 1]) {
//通过临时变量来进行交换,如果不使用临时变量直接赋值会造成覆盖
temp = arrs[j];
arrs[j] = arrs[j + 1];
arrs[j + 1] = temp;
}
}
}
}
但是此时,该算法的平均时间复杂度为O(n^2),当数据量较大的时候需要大量的时间,为此,出现了对上面算法的改进。
- 改进一
通过增加了判断数组在某一轮下是否已经有序的标识来提高冒泡排序的运算效率。
public static void improved1BubbleSort(int[] arrs) {
//接收数组长度
int length = arrs.length;
//定义一个临时变量用于交换
int temp;
//判断是否进行了数据交换的标识,0为发生了数据交换,1表示没有发生交换(即数据当前顺序符合期望顺序)
int flag = 0;
//for循环遍历数组元素,外层为交换的轮数,内循环为每轮比较的次数
for (int i = 0; i < length - 1; i++) {
for (int j = 0; j < length - i - 1; j++) {
//判断当前数如果大于其后面的数时进行交换(因为我们的顺序是从小到大,只有当前数小于等于后一个数时不用交换)
if (arrs[j] > arrs[j + 1]) {
//通过临时变量来进行交换,如果不使用临时变量直接赋值会造成覆盖
temp = arrs[j];
arrs[j] = arrs[j + 1];
arrs[j + 1] = temp;
//上面表示发生了数据交换,则把flag置为1
flag = 1;
}
}
//当前轮完成之后,立即判断该轮是否进行过交换,如果flag为0则说明没有交换,数组已经有序直接跳出for循环,反之则继续进行冒泡排序
if (flag == 0) {
break;
}
}
}
- 改进二
记录上一轮最后比较位置作为下一轮比较的末位置缩短比较次数,因为我选定的顺序是从小到大,所以后面的顺序先确定。如果选定的顺序是从大到小,则该记录位置应该作为下一次的起始位置。
//在每轮比较中增加一个判断标识,如果当前一轮中一次数据交换都没有发生,则说明数据已经有序,后面的轮数也不用再进行,直接break跳出循环
//当产生了数据交换则记录当前交换的位置作为下一轮起始位置
public static void improved2BubbleSort(int[] arrs) {
//接收数组长度
int length = arrs.length;
//定义一个临时变量用于交换
int temp;
//判断是否进行了数据交换的标识,0为发生了数据交换,1表示没有发生交换(即数据当前顺序符合期望顺序)
int flag = 0;
//pos用于记录最后一次交换的位置
int pos = 0;
//innerlength用于记录内层比较的位置
int innerlength = length - 1;
//for循环遍历数组元素,外层为交换的轮数,内循环为每轮比较的次数
for (int i = 0; i < length - 1; i++) {
for (int j = 0; j < innerlength; j++) {
//判断当前数如果大于其后面的数时进行交换(因为我们的顺序是从小到大,只有当前数小于等于后一个数时不用交换)
if (arrs[j] > arrs[j + 1]) {
//通过临时变量来进行交换,如果不使用临时变量直接赋值会造成覆盖
temp = arrs[j];
arrs[j] = arrs[j + 1];
arrs[j + 1] = temp;
//上面表示发生了数据交换,则把flag置为1
flag = 1;
//记录当前轮最后交换的位置
// 因为这里我选择的顺序是从小到大,所以是后面的顺序是最先确定,下一轮判断到该位置即可
// 如果选择的顺序是从大到小,则前面的顺序是最先确定,下一轮要从该位置开始
pos = j;
}
}
//下一轮循环至该位置即可
innerlength = pos;
//当前轮完成之后,立即判断该轮是否进行过交换,如果flag为0则说明没有交换,数组已经有序直接跳出for循环,反之则继续进行冒泡排序
if (flag == 0) {
break;
}
}
}
- 改进三
在上一算法的基础上,新增了在当前轮数中增加同时寻找最小值。
//在每轮比较中增加一个判断标识,如果当前一轮中一次数据交换都没有发生,则说明数据已经有序,后面的轮数也不用再进行,直接break跳出循环
//当产生了数据交换则记录当前交换的位置作为下一轮起始位置,同时在每轮中增加寻找最小数的过程
public static void improved3BubbleSort(int[] arrs) {
//接收数组长度
int length = arrs.length;
//定义一个临时变量用于交换
int temp;
//判断是否进行了数据交换的标识,0为发生了数据交换,1表示没有发生交换(即数据当前顺序符合期望顺序)
int flag = 0;
//pos用于记录最后一次交换的位置
int pos = 0;
//寻找最小值的下标
int minindex = 0;
//innerlength用于记录内层比较的位置
int innerlength = length - 1;
//定义一个临时变量用于交换最小数
int tempmin = 0;
//用于记录最小数交换的位置
int minnext = 0;
//for循环遍历数组元素,外层为交换的轮数,内循环为每轮比较的次数
for (int i = 0; i < length - 1; i++) {
for (int j = 0; j < innerlength; j++) {
//判断当前数如果大于其后面的数时进行交换(因为我们的顺序是从小到大,只有当前数小于等于后一个数时不用交换)
if (arrs[j] > arrs[j + 1]) {
//通过临时变量来进行交换,如果不使用临时变量直接赋值会造成覆盖
temp = arrs[j];
arrs[j] = arrs[j + 1];
arrs[j + 1] = temp;
//上面表示发生了数据交换,则把flag置为1
flag = 1;
//记录当前轮最后交换的位置
// 因为这里我选择的顺序是从小到大,所以是后面的顺序是最先确定,所以下一轮判断到该位置即可
// 如果选择的顺序是从大到小,则前面的顺序是最先确定,下一轮要从该位置开始
pos = j;
}
}
//下一轮循环至该位置即可
innerlength = pos;
//当前轮完成之后,立即判断该轮是否进行过交换,如果flag为0则说明没有交换,数组已经有序直接跳出for循环,反之则继续进行冒泡排序
if (flag == 0) {
break;
}
//反向寻找最小值
for (int j = innerlength; j > minindex; j--) {
tempmin = arrs[j];
arrs[j] = arrs[j - 1];
arrs[j - 1] = tempmin;
flag = 1;
//记录作为下一次的起始位置
minnext = j;
}
minindex = minnext;
if (flag == 0){
break;
}
}
}
但是不管怎么优化,因为涉及到两次遍历数组,冒泡排序的最好时间复杂度为O(n),最坏时间复杂度依然为O(n^2)。