排序算法系列文章
排序(一):冒泡排序
排序(二):选择排序
排序(三):堆排序
排序(四):插入排序
排序(五):二分搜索
目录
前言----十大经典排序算法
- 以上表格是基于数组进行排序的一般结论
- 冒泡、选择、插入、归并、快速、希尔、堆排序,属于比较排序(Comparison Sorting)
冒泡排序(Bubble Sort)
冒泡排序(Bubble Sort)的基本思想是:通过对待排序数组从头到尾遍历(从索引较小的元素开始),依次比较相邻元素的值,若发现逆序则交换,使值较大的元素逐渐从数组头部移向尾部,就象水底下的气泡一样逐渐向上冒。
算法步骤
① 从头开始比较每一对相邻元素,如果第一个比第二个大,就交换它们的位置
② 执行完一轮后,最末尾那个元素就是最大元素
③ 忽略上一步中曾经找到的最大元素,重复执行步骤一,直到全部元素有序
代码实现
public static void main(String[] args) {
int[] array = {
10,9,19,28,37,34,5};
//end > 0为终止条件,只有end > 0,也即是说,end为1的时候,它还可以和它前面的进行比较
//end为0时,它前面就没有元素了
for (int end = array.length - 1; end > 0; end--){
//一共比较多少趟
for (int begin = 1; begin <= end; begin++){
//每一趟中的最大元素
if (array[begin] < array[begin - 1]){
int tmp = array[begin];
array[begin] = array[begin - 1];
array[begin - 1] = tmp;
}
}
}
//输出打印
for (int i = 0; i < array.length; i++){
System.out.println(array[i] + "_");
}
}
代码优化①
- 如果序列已经完全有序,可以提前终止冒泡排序
- 增加一个boolean值,用于判断每一个循环后是否有数据交换,如果没有,则退出排序。说明数组元素是有序的。
- 如果数据不是完全有序,此优化会因添加成员变量而导致计算时间更长
public class BubbleSort2 {
public static void main(String[] args) {
int[] array = {
1,9,19,28,31,34,35};
//end > 0为终止条件,只有end > 0,也即是说,end为1的时候,它还可以和它前面的进行比较
//end为0时,它前面就没有元素了
for (int end = array.length - 1; end > 0; end--){
//一共比较多少趟
// 这个变量写在这里,是为了判断每一趟排序操作是否将序列变为完全有序.
// 因为有可能在某一趟之后, 数组的元素就变为完全有序了
boolean sorted = false; // 是否排过序
for (int begin = 1; begin <= end; begin++){
//每一趟中的最大元素
if (array[begin] < array[begin - 1]){
int tmp = array[begin];
array[begin] = array[begin - 1];
array[begin - 1] = tmp;
sorted = true; // 排过序
}
}
if (!sorted) break;
}
for (int i = 0; i < array.length; i++){
System.out.println(array[i] + "_");
}
}
}
代码优化②
- 如果序列尾部已经局部有序,可以记录最后一次交换的位置,减少比较次数。
- 记录上一次循环最后一次交换的位置,将其作为下一次循环的截止位置。
public class BubbleSort3 {
public static void main(String[] args) {
int[] array = {
10,9,19,28,37,34,5};
//end > 0为终止条件,只有end > 0,也即是说,end为1的时候,它还可以和它前面的进行比较
//end为0时,它前面就没有元素了
for (int end = array.length - 1; end > 0; end--){
//一共比较多少趟
// sortedIndex=1,是针对完全有序的情况: 因为完全有序下,就不会走到下面的if判断. 所以初始值给1. 最后end=1,一轮扫描就结束了
int sortedIndex = 1; // 从哪个索引开始,后面的都已经排好序了
for (int begin = 1; begin <= end; begin++){
//每一趟中的最大元素
// 如果不进入下面的判断,表示本轮循环肯定是有序的
if (array[begin] < array[begin - 1]){
int tmp = array[begin];
array[begin] = array[begin - 1];
array[begin - 1] = tmp;
// 记录最后一次交换的位置
sortedIndex = begin;
}
}
// 将最后一次交换的位置,给end, 作为下次循环的终点
end = sortedIndex;
}
for (int i = 0; i < array.length; i++){
System.out.println(array[i] + "_");
}
}
}
复杂度
最坏时间复杂度O(n^2)
最好时间复杂度O(n)
平均时间复杂度O(n^2)
空间复杂度O(1)
排序算法的稳定性(Stability)
- 如果相等的2个元素,在排序前后的相对位置保持不变,那么这是稳定的排序算法。
- 排序前:5,1,3a,4,7,3b
- 稳定的排序:1,3a,3b,4,5,7
- 不稳定的排序:1,3b,3a,4,5,7
- 对自定义对象进行排序时,稳定性会影响最终的排序效果。
- 冒泡排序属于稳定的排序算法。
- 因为冒泡排序过程中, 两个相同的元素根本就不会比较, 只有左边的元素 > 右边的元素才会进行交换排序。所以冒泡排序是稳定的排序。
- 但是在比较大小时,如果加了等于“=”,那这样的冒泡排序代码就是不稳定的
原地算法(In-place Algorithm)
- 不依赖额外的资源或依赖少数的额外资源,仅依靠输出来覆盖输入。
- 空间复杂度为O(1)的都可以认为是原地算法。
- 非原地算法,称为Not-in-place或者Out-of-place。
- 冒泡排序属于In-place。