交换排序——冒泡排序与快速排序

交换排序

    • 冒泡排序
    • 快速排序
    • 小结

交换排序基本思想是:两两比较待排序列记录的关键码,发现记录逆置则进行交换,直到没有逆置对为止。冒泡排序和快速排序是典型的交换排序算法。

冒泡排序

冒泡排序与直接插入排序也许是生活中我们最常用的排序方法,冒泡排序由两层嵌套循环构成:外层循环每次确定一个位置,内层循环相对于外层,从相反方向与相邻元素两两比较,直到外层当前位置,继而得出对应的值。

动态演示图如下:
交换排序——冒泡排序与快速排序_第1张图片
代码实现如下(与动态演示图排序方向相反):

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个。

动态演示图如下:
交换排序——冒泡排序与快速排序_第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

你可能感兴趣的:(算法)