[Classic sorting algorithm] 6. Quick sort

Quick sort is an improved version of bubble sort.

Although the worst-case time complexity of quick sort is O(n²) (arranging sequential arrays, it will degenerate into bubble sorting), but its performance is better in data with poorer order, and can even be better than merge sort . This is because although the average time complexity of quicksort and merge sort is O(nlogn), the constant factor implicit in the O(nlogn) notation of quicksort is very small.

code show as below:

public class Main {

    public static void main(String[] args) {
        int[] arr = {3, 3, 5, 6, 2, 1};
        System.out.print("排序前:");
        arrPrint(arr);
        QuickSort(arr);
        System.out.print("排序后:");
        arrPrint(arr);
    }

    // 快速排序
    // 快速排序和归并排序一样采用了分治法的设计思想。
    // 把大问题分解成小问题,把大数组分解成小数组。
    //
    // 调用快速排序的递归函数,左索引记为left,初始化为0,
    // 右索引记为right,初始化为arr.length - 1。
    private static void QuickSort(int[] arr) {
        quickSort(arr, 0, arr.length - 1);
    }

    // 快速排序的递归函数
    // 递归终止条件为左索引>=右索引。不满足终止条件时:
    // 调用基准值分割函数partition,得到分割后的索引mid,
    // mid-1即为分割数组后的左子数组的终点,mid+1即为分割数组后右子数组的起点
    // 递归调用快速排序quickSort,对左子数组进行快速排序,
    // 递归调用快速排序quickSort,对右子数组进行快速排序。
    //
    // 想看中间输出的可以在partition函数后面使用arrPrint(arr)来打印
    private static void quickSort(int[] arr, int left, int right) {
        if (left < right) {
            int mid = partition(arr, left, right);
            quickSort(arr, left, mid - 1);
            quickSort(arr, mid + 1, right);
        }
    }

    // 基准值分割函数partition
    // 选取基准值pivot后,循环使用双指针寻找左边比pivot大的数arr[l],
    // 右边比pivot小的数arr[r],并交换arr[l] arr[r]的位置。
    // 使得在r和l相遇位置的左半边的数不大于pivot,而在右半边的数则不小于pivot,
    // 最后把pivot交换到r和l的相遇位置,即可补全空间意义上真正的基准值分割。
    // 此时pivot的位置(r和l的相遇位置)将数组分割为了两边。左半边总是不大于右半边的数字。
    // 之后再利用分治法递归地调用partition,继续基准值分割左右子数组即可完成整个快排。
    //
    // 选取基准值pivot(默认arr是随机排序,所以直接取arr[left]),
    // 将左指针初始化为left + 1,右指针初始化为right,
    // 满足l小于r时(双指针没有超过遍历边界时)执行第一层while循环:
    // 第2层第1个while: 如果左指针l没有超过边界,且遍历元素arr[l]不大于pivot,
    //                  则左指针l循环右移,直到找到从左往右第一个比pivot大的遍历数arr[l]。
    // 第2层第1个while:同理,如果右指针r没有超边界,且遍历元素arr[r]不小于pivot,
    //                  则右指针r循环左移,直到找到从右往左第一个比pivot大的遍历数arr[r]。
    // 如果此时l依然满足小于r(双指针没有过界),则将arr[l] arr[r]交换位置,
    // l和r相遇后,所有while结束,此时将pivot交换到r与l的相遇位置。
    // pivot记录了arr[left]的值,所以先用arr[r]把arr[left]覆盖掉,
    // 再把pivot放到arr[r]上,完成最后交换,补全空间意义上真正的基准值分割,
    // 此时pivot的位置(r和l的相遇位置)将数组分割为了两边,左半边总是不大于右半边的数字。
    private static int partition(int[] arr, int left, int right) {
        int pivot = arr[left];
        int l = left;
        int r = right;
        while (l < r) {
            while (l <= r && arr[l] <= pivot)
                l++;
            while (l <= r && arr[r] >= pivot)
                r--;
            if (l < r)
                swap(arr, l, r);
        }
        arr[left] = arr[r];
        arr[r] = pivot;
        return r;
    }

    // partition中的交换元素位置函数
    private static void swap(int[] arr, int i, int j) {
        int temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }

    // 辅助函数:将int[] 打印出来
    private static void arrPrint(int[] arr) {
        StringBuilder str = new StringBuilder();
        str.append("[");
        for (int v : arr) {
            str.append(v + ", ");
        }
        str.delete(str.length() - 2, str.length());
        str.append("]");
        System.out.println(str.toString());
    }
}

The quick sort animation demonstration of this example is as follows: (The partition function partition of the reference value is omitted)
Insert picture description here

The animation demonstration of the reference value division function is as follows (the animation is wrong, and l should start from left):

Insert picture description here

Guess you like

Origin blog.csdn.net/fisherish/article/details/113843498