交换排序基本思想是:两两比较待排序列记录的关键码,发现记录逆置则进行交换,直到没有逆置对为止。冒泡排序和快速排序是典型的交换排序算法。
冒泡排序与直接插入排序也许是生活中我们最常用的排序方法,冒泡排序由两层嵌套循环构成:外层循环每次确定一个位置,内层循环相对于外层,从相反方向与相邻元素两两比较,直到外层当前位置,继而得出对应的值。
动态演示图如下:
代码实现如下(与动态演示图排序方向相反):
import java.util.Arrays;
public class BubbleSort {
public static void main(String[] args){
// 待排序列
int[] arrOriginal = new int[]{5, 9, 7, 4, 22, 2, 65, 1, 45};
System.out.println("before sort, the array is: ");
System.out.println(Arrays.toString(arrOriginal));
// 临时变量,内层循环中交换元素时应用
int temp = 0;
boolean noSwap;
// 外层:从某一端开始,确定要排序的某个位置
for (int i = 0; i < arrOriginal.length; i++) {
noSwap = true;
// 内层:从另一端开始,确定外层位置对应的值
for (int j = arrOriginal.length - 1 ; j > i; j--) {
if(arrOriginal[j] < arrOriginal[j-1]){
temp = arrOriginal[j];
arrOriginal[j] = arrOriginal[j-1];
arrOriginal[j-1] = temp;
noSwap = false;
}
}
if(noSwap) {
break;
}
}
System.out.println("\nend sort, the array is: ");
System.out.println(Arrays.toString(arrOriginal));
}
}
与直接插入排序一样,时间复杂度最优情况下为O(n),平均和最差情况为O(n2)。
快速排序,1962年发明,体现了分治的思想:首先,选定轴值;然后,将待排序列元素分别与轴值比较,划分到轴值两侧,轴值元素位置固定;最后,针对左右两侧递归调用快速排序算法,直到待排序列元素数小于2个。
import java.util.Arrays;
public class QuickSort {
public static void main(String[] args){
int[] arrOriginal = new int[]{5, 9, 7, 4, 22, 2, 65, 1, 45};
System.out.println("before sort, the arr is: ");
System.out.println(Arrays.toString(arrOriginal));
quickSortProcess(arrOriginal, 0, arrOriginal.length-1);
System.out.println("end sort, the arr is: ");
System.out.println(Arrays.toString(arrOriginal));
}
public static void quickSortProcess(int[] arr, int startIdx, int endIdx) {
if (startIdx >= endIdx){
return;
}
// 1. 选择轴值
int flagIndex = (startIdx + endIdx) / 2;
int flagValue = arr[flagIndex];
// 2. 将轴值与末尾元素交换,确定一个可交换的元素
switchVal(arr, flagIndex, endIdx);
// 设定正向与反向游标指针
int forwardIdx = startIdx;
int reverseIdx = endIdx;
// while 正idx < 反idx
while(forwardIdx < reverseIdx) {
// 正向从起始元素开始,遇到比轴值大的元素时,与反向指针元素交换位置,正向指针暂停,break;否则正向+1
while(forwardIdx < reverseIdx && arr[forwardIdx] < flagValue){
forwardIdx++;
}
switchVal(arr, forwardIdx, reverseIdx);
// 反向从结束元素开始,遇。。。。小。。。。,。正。。。。。。。。。,反向指针暂停,break;。。反向-1
while(forwardIdx < reverseIdx && arr[reverseIdx] > flagValue){
reverseIdx--;
}
switchVal(arr, forwardIdx, reverseIdx);
}
// 3. 二分,递归调用(注释代码为错误示范,出现堆栈溢出的异常)
// quickSortProcess(arr, startIdx, endIdx/2);
// quickSortProcess(arr, (endIdx/2 + 1), endIdx);
quickSortProcess(arr, startIdx, forwardIdx);
quickSortProcess(arr, forwardIdx+1, endIdx);
}
public static void switchVal(int[] arr, int i, int j){
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
快速排序时间复杂度为:最差情况为O(n2),最好与平均情况为O(nlogn)
冒泡与快速排序都基于交换排序的思路,提供了不同的实现方式,其中冒泡基于我们小数世界中的排序思路,每次挑出最大或最小,然后再从其余数据项中找次大或次小的,因此它是稳定的排序,但这种直观的思路其时间复杂度为平方级别,并不适用于大数据量的情况。
快速排序则使用分治的思想,通过自顶向下的拆分,将排序问题缩减规模,在完成小规模排序的同时,最终实现了整体的排序效果,且执行更加高效。
参考资料:
《数据结构与算法》
Bubble sort - wikiwand
Quicksort - wikiwand
系列示例代码下载:欢迎关注我的github