一、排序算法的分类
分为比较排序(Comparison Sorting)如冒泡、选择、插入、归并、希尔、堆、快速排序与非比较排序。
二、冒泡排序的思想及优化思路(升序为例)
思想:从头开始比较一对相邻元素,若第一个元素大于第二个元素则交换位置,执行完一次遍历后,最末尾元素即最大值,忽略上次最大值,重复执行,直到全部数据有序。
优化思路一:当全部数据处于有序状态时,可通过一次遍历确定有序状态,可直接结束循环。
优化思路二:当最后部分数据有序且有序部分最小值大于无序部分最大值(有序部分无需进行任何交换),可以考虑记录最后交换位置,减少比较次数
三、代码实现
普通代码,arr为某无序数组
for (int end = arr.length - 1; end > 0; end--) {
for (int begin = 1; begin <= end; begin++) {
if (arr[begin - 1] > arr[begin]) {
int t = arr[begin];
arr[begin] = arr[begin - 1];
arr[begin - 1] = t;
}
}
}
优化思路一代码
for (int end = arr.length - 1; end > 0; end--) {
boolean sorted = true;
for (int begin = 1; begin <= end; begin++) {
if (arr[begin - 1] > arr[begin]) {
sorted = false;
int t = arr[begin];
arr[begin] = arr[begin - 1];
arr[begin - 1] = t;
}
}
if (sorted) break;
}
优化思路二代码,一次遍历后若未交换即有序,直接退出循环
for (int end = arr.length - 1; end > 0; end--) {
//完全有序时提前退出
int sortedIndex = 1;
for (int begin = 1; begin <= end; begin++) {
if (arr[begin - 1] > arr[begin]) {
int t = arr[begin];
arr[begin] = arr[begin - 1];
arr[begin - 1] = t;
sortedIndex = begin;
}
}
end = sortedIndex;
}
四、复杂度分析
最坏时间复杂度(所有数据无序):外层循环次数为length-1,内层为((length - 1) + (length - 2) + … + 1)/(length - 1)= length/2,故为O(n²)。
最好时间复杂度(即一次外循环就完成排序):O(n)。
空间复杂度为O(1)的算法被认为是原地算法。
原地算法(In-place Algorithm):不依赖额外的资源或者依赖极少部分的额外资源,仅依靠输出覆盖输入,冒泡排序为原地算法。非原地算法称位Not-in-place或者Out-of-place
五、排序算法的稳定性
相同数据在排序前后相对位置不变即该排序算法是稳定的。排序算法稳定性百度百科。
int []arr = new int[]{1, 6(A), 2, 6(B), 3, 7, 4, 5};
//经过排序算法处理,输出数组结果如下
1, 2, 3, 4, 5, 6(A), 6(B), 7
//(A)与(B)仅仅用来区分相同值
在上述的冒泡排序中将内层循环中的判断条件由>修改为>=,则原本稳定的算法变为不稳定。继而增加无意义的值交换,浪费性能。
if (arr[begin - 1] >= arr[begin])
//稳定,去掉=后变为不稳定